NEWS


obr 0.6.3 (2026-11-01)

Bug-fix release. Three functions failed for any user without a cached copy of the source file, and one returned out-of-date data without saying so. Local test runs did not catch either because they read from the cache, which itself never expired.

Cached files no longer outlive their edition

Every edition of a publication was cached under one fixed file name that never expired, so once a file was downloaded, later editions were never fetched: the provenance named the new edition while the numbers came from the old file. A machine that first ran get_efo_fiscal() in April 2026 was still returning the March 2026 EFO, labelled October 2026. Each edition is now cached under its own name, and cached files are downloaded again after 7 days (options(obr.cache_ttl = ), in hours). If that download fails, the expired copy is used with a warning.

Historical forecasts and policy measures download again

The OBR now serves the Historical Official Forecasts Database and the Policy Measures Database from undated addresses (/download/historical-official-forecasts-database/ and /download/policy-measures-database/) that redirect to the current file. Every dated address the package probed, including the March 2025 fallback, now returns 404, so get_forecasts(), obr_actual_vs_forecast(), obr_forecast_panel() and get_policy_measures() errored on a cold cache. The undated addresses are now tried first and are the fallback.

The vintage recorded in provenance is now read from the file the address redirects to, so these tables report "Spring 2026" and "March 2026" rather than NA. obr_url_vintage() accepts the underscore and Spring/Autumn forms the OBR uses in filenames.

Public finances databank was a year old

get_psnb(), get_psnd(), get_receipts(), get_expenditure() and get_public_finances() read the databank from an undated address that, after the October 2026 EFO, still served the November 2025 edition. Each edition now has a dated address (public-finances-databank-october-2026-efo); these are probed newest first, with the undated one as the fallback, and the vintage in provenance is read from the dated address ("October 2026").

Duplicate forecast vintage in the historical database

The Spring 2026 database labels the July 1997 Budget "July 1996", giving two forecasts with the same date. obr_forecast_panel() kept only the first, with a warning from stats::reshape(). The second label is now corrected to "July 1997" when it follows "November 1996", and every series was checked for other duplicate keys (none).

Welfare Trends Report functions pinned to the incapacity edition

Each Welfare Trends Report has its own theme. The June 2026 report is on fraud and error, and its chart numbers point at fraud and error rates. Resolving "the latest report", which the package tried to do, therefore either failed (get_welfare_spending()) or would have returned fraud and error rates labelled as incapacity spending (get_incapacity_spending(), get_incapacity_caseloads()). These three functions now read the October 2024 report, the most recent on incapacity benefits, and check each sheet's chart title before parsing, so a renumbered workbook is an error rather than the wrong series.

Tables 6.11 and 6.15 returned a different table

In every EFO workbook since at least October 2024, sheets 6.11 (net debt year-on-year changes) and 6.15 (reconciliation of PSNCR and CGNCR) hold one line, such as "See Table 6.3 of our October 2026 Economic and fiscal outlook". That points at a table in the EFO report. Since 0.5.0, get_efo_table() fetched the sheet with that number from the workbook instead and returned it under the original title: 6.11 gave general government receipts labelled as net debt changes, and 6.15 gave Bank of England cash transfers or APF runoff. Both now return NULL with a warning that quotes the OBR's pointer. A sheet with no pointer (some older editions) is parsed as an ordinary table.

Footnote markers after a bracket

strip_footnote() removed a trailing footnote digit only after a letter, full stop or space, so a series named "Onshore corporation tax (includes Bank Surcharge and EGL)3" kept its marker. A closing bracket now counts too.

October 2026 EFO

obr 0.6.2 (2026-08-24)

Recovery release. obr was archived from CRAN on 2026-08-22 because the test ERROR reported for 0.6.0 was not corrected before the 2026-08-21 deadline. This release carries the 0.6.1 fix, which was prepared on 2026-08-15 but never submitted, and hardens the examples against the same underlying cause.

The archived failure, and the fix carried over from 0.6.1

get_policy_measures() validated its search and since arguments only after downloading the Policy Measures Database. The argument-validation tests therefore needed the network, and failed on the macOS builders when obr.uk returned HTTP 403. Both arguments are now checked before any download happens, so malformed input fails fast and offline, and no workbook is fetched for a call that cannot succeed.

Retry time is now bounded

req_retry() treats a 403 from obr.uk as transient, which is right for a real user hitting the CDN's rate limiting, but wrong for a machine the CDN refuses outright: every request sat through the full 2 + 4 + 8 second backoff before failing. Wrapping the examples in try() stopped that being an ERROR but left it slow, and the package being slow on CRAN's builders is what started this.

Both retry blocks now set max_seconds = 5. Measured against an always-403 endpoint, a refused request costs 15.2s uncapped and 6.6s at this setting. Caps of 8 seconds or above measured no better, because the uncapped backoff already completes inside them. A genuine transient 403 still recovers, and a normal download is unaffected: get_psnb() and get_efo_fiscal() both return in around 2 seconds.

DESCRIPTION no longer advertises the FSR pension projections

The package Description still listed "the Fiscal Risks and Sustainability Report (50-year state pension projections)" among the sources covered. get_pension_projections() has been deprecated since 0.5.1 and returns NULL, so that clause has been wrong since then and shipped to CRAN in 0.6.0. Removed. The README limitation covering the same ground has been rewritten: it described the v0.4.x state, in which only 4 EFO detailed-forecast tables were exposed, when v0.5.0 took that to all 39.

Examples hardened against an unreachable obr.uk

Every unguarded \donttest{} example that makes a network call is now wrapped in try(), so an unreachable or rate-limited obr.uk produces a printed condition rather than an example ERROR. That covers 20 of the 24 \donttest{} blocks. The other four are already safe: three (obr_headroom(), get_monthly_profiles(), and one of the get_policy_measures() examples) already guard their calls with tryCatch(), and clear_cache() touches only the local cache.

This is the same class of failure that produced the donttest ERROR reported against 0.2.5. obr.uk returns HTTP 403 to the CRAN build machines as a rate-limiting measure rather than as a genuine "not found", and the package retries those, so an example that cannot reach the site spends its retry budget and then fails. Wrapping in try() means the retry budget is still spent but the check no longer errors.

The options(op) cache-directory restore stays outside the try(), so it runs whether or not the download succeeded.

obr 0.6.1

Patch release fixing the test ERROR reported on the CRAN macOS check flavours for 0.6.0.

get_policy_measures() validated its search and since arguments only after downloading the Policy Measures Database. The argument-validation tests therefore needed the network, and failed on the macOS builders when obr.uk returned HTTP 403. Both arguments are now checked before any download happens, so malformed input fails fast and offline, and no workbook is fetched for a call that cannot succeed.

obr 0.6.0 (2026-08-02)

Budget-readiness release ahead of the autumn 2026 fiscal event.

New: get_monthly_profiles()

Wraps the monthly profiles workbook the OBR publishes alongside each EFO: the full-year forecast for receipts, spending, and the CGNCR apportioned across the twelve months of the fiscal year. These profiles are the reference point for judging each month's ONS/HMT public sector finances outturn ("borrowing so far this year vs the OBR profile") in the run-up to a fiscal event. Monthly rows use the new period_type = "month" (period in "YYYY-MM" format); each series also carries a fiscal_year row with the full-year forecast the profile sums to.

New: obr_headroom()

Returns the current budget surplus path from EFO Table 6.5: the margin against the Charter's stability rule ("headroom") in every forecast year. Pass target_year to flag the year the rule bites on. The target year is deliberately not guessed by the package because the Charter's convention changes over time; see obr_fiscal_rules().

Autumn-EFO readiness fixes

obr_compare_vintages() covers all 39 tables

what now accepts any table id from obr_efo_catalogue() (e.g. "6.16" to diff the debt interest forecast across two vintages), in addition to the four named shortcuts.

obr 0.5.1 (2026-07-27)

Deprecated

obr 0.5.0

Full EFO detailed-forecast-table coverage (39 of 39)

This release closes the v0.3.x feedback that "the range of forecast lines was fairly limited" by exposing every detailed-forecast table in the EFO Aggregates and Economy workbooks. v0.4.x exposed 4. v0.5.0 exposes all 39: 35 via standard layout parsers, 2 via bespoke layout parsers (subsector_matrix for Table 6.4, quarterly_indented for Table 6.10), and 2 via cross-reference auto-follow to the previous EFO vintage (Tables 6.11 and 6.15, which OBR redirects to Tables 6.2 and 6.5 of the November 2025 EFO).

New: get_efo_table() and obr_efo_catalogue()

get_efo_fiscal() and get_efo_economy() are now thin wrappers over the dispatcher and continue to work unchanged. Series names for the single-series Economy tables (output gap, nominal GDP, electricity price) are preserved via internal overrides so v0.4.x scripts still match.

New layouts handled

The dispatcher routes each table to one of:

Classifier hardening

Tightened classify_metric_type() so the v0.5.0 expanded coverage tags each row correctly:

obr 0.4.0

Breaking: standard tidy long schema for the EFO / PFD / HFD / WTR / FSR functions

This release standardises the columns returned by the data-fetching functions backing the Public Finances Databank (PFD), Economic and Fiscal Outlook (EFO), Historical Forecasts Database (HFD), Welfare Trends Report (WTR), and Fiscal Risks and Sustainability Report (FSR) so they can be rbind()'d, joined, plotted, and reasoned about the same way regardless of which OBR publication produced them. Driven by feedback from Ben Northcott (Office for Budget Responsibility) on the v0.3.x release.

The Forecast Revisions Database (get_forecast_revisions()) and Policy Measures Database (get_policy_measures()) keep their existing multi-dimensional schemas in v0.4.0; migrating them to the standard schema is queued for a later release.

All long-format outputs from the EFO / PFD / HFD / WTR / FSR functions now share the columns:

get_forecasts() adds forecast_date as a leading column. get_pension_projections() adds scenario_type as a trailing column.

Migration

| Old column | New column | |------------|-----------| | year, fiscal_year | period (with period_type to disambiguate) | | value_bn, psnb_bn, psnd_bn, tme_bn | value (with unit = "gbp_bn") | | pct_gdp (FSR pension projections) | value (with unit = "pct") | | scenario (FSR) | series |

get_psnb(), get_psnd(), get_expenditure() now return the standard long schema and tag rows with series = "PSNB", "PSND", "TME" respectively rather than collapsing the value into a series-named column.

CPI Index vs YoY split (the headline fix)

In v0.3.x, calling get_efo_economy("inflation") returned CPI Index values (~135) and CPI YoY growth values (~2.1) in the same value column with no machine-readable distinction between them. v0.4.0 tags every row with a metric_type ("index" for index levels, "yoy_pct" for growth rates, "pct" for shares, etc.) and a matching unit, so callers can filter or facet on metric type directly.

This is the v0.4.0 fix for the bug Ben Northcott raised on 2026-04-29: "If I pull e.g. the CPI forecast the index value and the YoY growth rate appear in the same 'value' column. Probably an additional column in long format indicating YoY or Index would be useful."

New helpers

Constants

New: workflow helpers

Both helpers feed naturally into the kind of forecast-evaluation tables the OBR's own Forecast Evaluation Report uses.

New: vignette

obr 0.3.0

New: provenance metadata on every returned object

New: vintage layer for reproducible analysis

New: Policy Measures Database

New: fiscal rules reference

New: Forecast Revisions Database and forecast panel

Breaking-ish changes

URL resolution and reliability

Parser robustness

Documentation

obr 0.2.5 (2026-04-14)

obr 0.2.4 (2026-03-17)

obr 0.2.3

obr 0.2.2 (2026-03-12)

obr 0.2.1

obr 0.2.0

New datasets

Economic and Fiscal Outlook (EFO)

Welfare Trends Report (WTR)

Fiscal Risks and Sustainability Report (FSR)

obr 0.1.0