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.
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.
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.
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.
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.
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.
Budget-readiness release ahead of the autumn 2026 fiscal event.
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.
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().
march-<year> slugs before october-<year> /
november-<year> within each year, so once an autumn EFO published,
the package would have kept silently returning the spring EFO for the
rest of the year. Candidates are now probed newest-first. The same fix
is applied to the Historical Forecasts Database and Policy Measures
Database resolvers.obr_pin() and vintage = arguments now accept a well-formed vintage
label that is not yet in the package's EFO calendar (e.g.
"November 2026" on Budget day, before a package update ships). The
download URL is constructed from the OBR's slug convention, with a
warning.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.
get_pension_projections() is deprecated and now returns NULL with a
warning. In July 2026 the OBR restructured the Fiscal Risks and
Sustainability Report workbook set: the executive-summary sheet this
function read (C1.2, state pension spending split into demographic and
triple-lock scenarios) no longer exists, so a live call errored with
"Sheet 'C1.2' not found". The equivalent series is now published in the
FSR Chapter 3 "Long-term spending projections" workbook as Chart 3.11
("State pension spending under alternative uprating assumptions"), with a
different scenario structure. The stub is retained so existing scripts do
not fail with "object not found"; it will be removed in a future release.
This resolves the donttest check ERROR reported on CRAN for 0.2.5.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).
obr_efo_catalogue() returns a data frame describing every detailed-
forecast table the package can fetch: table_id, file, section,
title, layout, default metric_type, default unit. Use this to
discover what's available.get_efo_table(table_id, vintage, refresh) is the generic dispatcher.
Pass any catalogue id (e.g. "6.13", "1.19", "6.1") and get the
parsed contents back in the standard v0.4.0 schema (period,
period_type, series, metric_type, value, unit).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.
The dispatcher routes each table to one of:
quarterly_wide periods in col 2 (Q1/Q2 strings), series acrossquarterly_single periods in col 2, single value column to the rightannual_year_wide calendar years as column headers, series in col 2annual_period_wide calendar years in col 2 (rows), series acrossfiscal_year_wide fiscal years as column headers, series in col 2subsector_matrix single fiscal year, series in col 2, sub-sector in column headers (used for Table 6.4); adds a sub_sector extra columnquarterly_indented "Q1 2016" format periods in col 3, value in col 4, with Outturn/Forecast section markers in col 2 (used for Table 6.10)cross_reference sheet redirects to a previous EFO; the dispatcher follows the redirect, fetches the named vintage, and returns the linked table with provenance pointing at the previous vintageTightened classify_metric_type() so the v0.5.0 expanded coverage tags
each row correctly:
Index-linked gilts (and similar) is no longer classified as
metric_type = "index". The "index" pattern now requires Index at
the end of the series name or an explicit (2015=100) / (2010=100)
base-year tag. Net-debt composition (Table 6.13) now correctly tags
every row as pct.change no longer triggers yoy_pct because OBR uses "change in
X" for level differences too (e.g. Adjustment for the change in pension entitlements, in £bn). Explicit YoY signals (growth, inflation,
y/y, yoy, year on year, % change, annual %) still trigger.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:
period - the time period as a character stringperiod_type - one of "fiscal_year", "quarter", "calendar_year"series - the variable namemetric_type - one of "level", "yoy_pct", "index", "pct", "pct_pts"value - the numeric valueunit - one of "gbp_bn", "pct", "pct_pts", "index", "count_k", etc.get_forecasts() adds forecast_date as a leading column.
get_pension_projections() adds scenario_type as a trailing column.
| 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.
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."
classify_metric_type() (internal): heuristic classifier for series
names. Returns "index", "yoy_pct", "pct", "pct_pts", or
"level". Used by every parser to populate metric_type from raw
source labels.default_unit_for_metric() (internal): maps a metric_type to its
default unit. For "level" returns NA (caller must supply since a
level can be gbp_bn, count_mn, etc.).obr_long() (internal): canonical constructor for the v0.4.0 schema.
All parsers use this to build their tidy long output.OBR_PERIOD_TYPES, OBR_METRIC_TYPES, OBR_UNITS: controlled
vocabularies for the schema metadata columns. Internal but documented
in the package source.obr_compare_vintages(vintage_a, vintage_b, what) joins the same EFO
table from two vintages on the standard schema and returns a tidy diff
with value_a, value_b, and revision = value_b - value_a. Supports
what = "fiscal" (default), "inflation", "labour", or
"output_gap".obr_actual_vs_forecast(series) joins the long-format Historical
Forecasts Database against PFD outturn for the same series, returning
one row per (forecast vintage, fiscal year) with the realised forecast
error. Supports series = "PSNB", "PSND", or "expenditure".Both helpers feed naturally into the kind of forecast-evaluation tables the OBR's own Forecast Evaluation Report uses.
vignette("efo-forecasts") covers the v0.4.0 schema: how to read the
Index vs YoY split, how to combine EFO with PFD outturn via the shared
schema, and how to compare two vintages. The existing vignette("vintages")
is updated for the renamed period column.obr_tbl object: a data.frame
with attached provenance recording the source URL, OBR publication code
("PFD", "HFD", "EFO", "WTR", "FSR"), publication vintage (e.g. "March 2026"),
retrieval timestamp, file MD5 fingerprint, and obr package version. This
lets users audit which OBR publication produced any number, and reproduce
analyses across new package or OBR releases.summary() returns the full provenance card.obr_provenance() extracts the metadata as a list.[ preserves the class and attributes; as.data.frame()
strips them.obr_efo_vintages() returns a structured table of every EFO published
since the OBR was created in June 2010, with publication dates and URL
slugs.obr_as_of(date) returns the EFO that was current on a given calendar
date. Useful for reproducing analyses as they would have looked at a
given point in time.obr_pin(vintage) sets a session-wide EFO vintage that flows into
get_efo_fiscal() and get_efo_economy(). obr_unpin() clears it and
obr_pinned() returns the current pin.get_efo_fiscal() and get_efo_economy() now accept a vintage =
argument that overrides any pin and downloads the file for that specific
EFO. Cached files are vintage-tagged so different vintages do not
overwrite each other.get_policy_measures() provides programmatic access to the OBR's Policy
Measures Database, with one row per fiscal-event-scored measure since
1970 (tax) or 2010 (spending). Columns include the fiscal event, plain-
English description, Treasury head, fiscal year, and Exchequer effect
in GBP million.type ("tax", "spending", or both), search (regex
on description / head, case-insensitive), since (fiscal-year cut-off).policy_measures_summary() aggregates the long format to net Exchequer
effect by event and fiscal year.obr_fiscal_rules() returns a structured table of the three numerical
rules in the current Charter for Budget Responsibility (Autumn 2024,
updated Autumn 2025): the stability rule (current budget surplus), the
investment rule (PSNFL/GDP falling), and the welfare cap. The metric,
pass direction, target-year mechanics, and source Charter / Act are
shipped as data. Numerical headroom is not shipped as a constant
because it changes at every fiscal event; users should derive it from
current EFO output via [get_efo_fiscal()] or read the EFO press release.get_forecast_revisions() provides programmatic access to the OBR's
Forecast Revisions Database, which decomposes each EFO-to-EFO change
in the headline PSNB forecast into policy, classifications and one-offs,
and underlying (economic determinants) components. Available in both
GBP billion and per cent of GDP.obr_forecast_panel() pivots the long-format Historical Forecasts
Database to a wide real-time panel: rows are forecast vintages, columns
are fiscal years, cells are forecast values. Mirrors how the OBR's own
Forecast Evaluation Report lays out forecast performance and lets users
read the h-step-ahead forecast for any vintage off the diagonal.obr_tbl class and provenance attributes.cli::cli_warn() instead of failing silently. The returned data frame
records source = "fallback" so callers can detect this state.obr_resolve_url() now uses HEAD requests for faster candidate probing
and captures the redirect target so the Public Finances Databank vintage
can be recovered from the stable slug.parse_efo_output_gap() (sheet 1.14) now scans for the value column with
the most aligned numeric entries instead of hardcoding column 3.parse_pension_projections() (FSR sheet C1.2) now detects sections via
fuzzy matching on "demographic" and "triple lock" rather than exact strings.match.arg() is now used for series and measure arguments.inst/CITATION reads the version from DESCRIPTION so it stays in sync.tempdir() instead of the user's home directory,
fixing CRAN policy compliance for \donttest examples.options(obr.cache_dir = ...).get_efo_fiscal() for five-year fiscal projections (net borrowing components) from the latest Budgetget_efo_economy(measure) for quarterly economic projections: "inflation" (CPI, CPIH, RPI, RPIX), "labour" (employment, unemployment, participation), or "output_gap"list_efo_economy_measures() lists available economy measuresget_welfare_spending() returns working-age welfare spending split by incapacity and non-incapacity (% of GDP, from 1978-79)get_incapacity_spending() returns incapacity benefits spending by benefit type (ESA, IB, Invalidity Benefit, Sickness Benefit, SDA) as % of GDPget_incapacity_caseloads() returns combined incapacity benefit caseloads and prevalence since 2008-09get_pension_projections() returns 50-year state pension spending projections (% of GDP) under alternative demographic and triple-lock scenariosget_psnb(), get_psnd(), get_expenditure(), get_receipts() for Public Finances Databank aggregatesget_public_finances() for all Databank series in tidy long formatget_forecasts() and list_forecast_series() for Historical Official Forecasts Databaseclear_cache() to remove locally cached files