OBR published economic effects of policy: AS2023–AB2025 packages, supply-side scorings, March 2026 decisions (external side) - #75
Conversation
…l side) — AS2023-AB2025 packages, supply-side scorings, March 2026 decisions Harvest-first step of #55: 266 claims across four metrics from six Wayback-original OBR artifacts (sha256-gated in the adapter): - gdp_level_effect: package impacts on real GDP for AS2023/SB2024 (by channel), AB2024 (by component AND by measure), AB2025 (by component) - cpi_inflation_effect: AB2025 budget policies by measure - supply_side_impact: Briefing Paper No.10 Table 2.1 — all 19 measures OBR has scored for potential output (% GDP, labour/capital/TFP channel), SB2023 -> SS2025 - decisions_effect_on_borrowing: March 2026 Table B.1 nested total/direct/indirect split, GBP, published sign convention carried verbatim, aggregate_level/parent double-count guard (obr-welfare convention) Tests pin block counts, closed row shape, spot values, the TB.1 hierarchy identities, and byte-stable rebuild from the committed raw bytes. No PE counterparts, no engine registration — external side only. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Gate round 1 (reviewed as a pair with #81) — strong vendoring; four findings, three blockingClean and verified: all six SHA-256 pins match the vendored artifacts, parsing reads stored XLSX values at full precision (the office:value discipline), a no-write regeneration was byte-identical at 30+20+36+30+35+36+19+60 = 266, and every harvested TB.1 cell is a forecast — no Ledger-boundary violations.
🤖 Generated with Claude Code |
1. Units are no longer conflated. GDP-level deviations, CPI effects and supply-side impacts were all emitting bare `percent`; they are three different quantities and now carry three unit concepts — `percent_of_real_gdp`, `percentage_points`, `percent_of_potential_gdp` — with `gbp_nominal` unchanged on Table B.1. Same rule that split GBP_PER_WEEK from bare GBP, and the adapter test now pins the mapping per metric instead of entrenching the conflation. 2. 266 is source-reconciled. Table B.1 prints 66 numeric cells across 11 lines; the parser selected 10 and the memo current-budget line fell through a silent `continue`. Every value-bearing line is now classified — emitted (TB1_ROWS) or a declared drop (TB1_DROPS, with a reason) — and an unclassified value line raises. The build reports and asserts `272 source cells = 266 claims + 6 deliberate drops`; the memo line is dropped because it scores the current budget, not PSNB. 3. Baseline provenance is per row, not one blanket sentence. Each row carries the pre-measures world of its own round (`obr_pre_measures_<event>`), the counterfactual KIND — Briefing paper No.10 scores tax/welfare measures against a legislated-parameter counterfactual and DEL/regulatory measures against the pre-existing activity baseline — and a locator. March 2026 Table B.1 keys `obr_november_2025_budget_forecast`, its own stated counterfactual, and the March 2025 WCA reversal's locator records that it scores against the WCA-adjusted world AS2023 created. 4. Identity vocabulary is closed, and `basis` stops squatting. Chart series labels, BP10 measures, types and channels are explicit registries; an unregistered label raises instead of `_slug()` minting one. `basis` is now `forecast` (its standard meaning everywhere in this repo) and the scoring method moved to its own `scoring_method` axis. Also: publication provenance is per artifact, so each round's claims carry their own release date and URL rather than one generic obr.uk/publications/ stamp — the input to #81's date fix. 266 claims unchanged; suite 261 passed, ruff format clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016HuXJFVme8HRbnke2Ey3Me
Reviewed as a pair with #75, which this branch now carries; the harvest side supplies the per-row provenance the fixes here consume. 1. Baselines are the rounds' own pre-measures worlds, not current_law. Every claim now carries a ReformRef.baseline descriptor — {policy: obr_pre_measures_<round>, counterfactual: <kind>} — mirrored into conditions["baseline_policy"], with the counterfactual kind split as Briefing paper No.10 chapter 2 splits it (legislated parameters for tax/welfare, the pre-existing activity baseline for DEL, the regulatory baseline for planning). March 2026 Table B.1 keys the November 2025 Budget forecast, its own stated counterfactual. All eleven (round, counterfactual) worlds are registered in baselines.py, a row whose baseline disagrees with its own round raises, and a baseline without a locator raises. 2. BP10 horizons are per scoring round. The paper re-states scorings from five earlier events, so "the fifth year of our forecast" is each measure's own round's fifth year: 2027-28 (Mar 2023) through 2029-30 (Mar 2025). All 19 were keyed to 2030-31, the November 2025 round's horizon. Period is claim identity, so those were 19 claims about years OBR never scored them for. _BP10_HORIZON resolves per event and an unregistered round raises rather than borrowing another's year. 3. Unit concepts are deliberate, and validated before mapping. Three new UnitConcept members — PERCENT_OF_REAL_GDP, PERCENTAGE_POINTS, PERCENT_OF_POTENTIAL_GDP — replace the collapse into bare PERCENT, and the staged label is now compared against the metric's expected unit and raises on drift instead of being canon-checked then discarded. Bare "percent" is no longer a registered label for this source, so the three cannot collapse back. 4. Publication provenance is per artifact. Each round's claims carry their own release date and dated URL rather than one generic obr.uk/publications/ stamp dated 2025-11-26, and the 60 March-2026 claims carry the publication date (2026-03-03) instead of the Wayback capture (2026-03-16). An unregistered artifact raises. 5. `basis` stops squatting: it is `forecast` on every row (its repo-wide meaning) and the scoring method moved to its own `scoring_method` condition, which also keys the relationship lookup. 266 claims and the exact accounting unchanged; the DB builds deterministically (two builds agree on content_hash), suite 299 passed, ruff format clean. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_016HuXJFVme8HRbnke2Ey3Me
|
All four addressed in
Also: publication provenance is per artifact, which is the input #81's date fix consumes. 266 claims unchanged; suite 261 passed, ruff format clean. |
DTrim99
left a comment
There was a problem hiding this comment.
Reviewed at head ea37f62 — ran the suite (13 pass) and recomputed the SHA pins (match). This is a clean, disciplined harvest; same rigor as the other lanes.
Verified:
- Provenance — all 6 OBR workbooks are SHA-256 pinned in
adapter.py,source.json, and the README, with a recompute-and-compare gate that runs first inbuild(); editions are pinned by dated snapshot URLs + per-artifactdate(with the Wayback-capture-≠-publication-date distinction asserted), not alatestURL. - Adapter parses by sheet name + title anchor + a closed series registry (not fragile absolute cell refs), and values pass through verbatim — the only transform is the TB.1 £bn→nominal
*1e9(I checked the raw→nominal value matches the pin). Four distinct unit concepts stop %-vs-£bn / pp-vs-% misfiling. - Fail-loud on any unmapped series/measure/type/channel, title-prefix mismatch, non-FY period, or unclassified TB.1 line — all tested.
- Accounting reconciles (272 cells = 266 claims + 6 tallied memo drops) with a TB.1 tie-back and hierarchy identities (direct+indirect=total, £1 tol), and determinism holds (sorted iteration, no timestamps, byte-stable rebuild test).
Only cosmetic nits: a hardcoded header_row=25 and a deliberately-truncated TB.1 title prefix are mildly fragile, but the title-anchor + closed-registry + FY guard would catch any real re-shuffle. Approving — merge this before the stacked ingest (#81).
Harvest-first step of #55 (per the 8/16 steer and the claim comment's execution order): the external side of the population the Macro members will answer — no PE counterparts, no engine registration, no emulator work in this PR.
What was retrieved (all Wayback original bytes; obr.uk is Turnstile-guarded, so the
uk_obr/NOTES.mdno-bypass recipe applies; URLs + snapshots + SHA-256 insources/obr-policy-effects/raw/README.md, and the adapter sha256-gates every raw file before parsing)Rows: 266, four metrics
gdp_level_effect151 ·cpi_inflation_effect36 ·supply_side_impact19 ·decisions_effect_on_borrowing60. Every row carriesfiscal_event,basis(post_behavioural / supply_side),scope, verbatimsource_column, and — for the nested TB.1 — theaggregate_level/parentdouble-count guard in the obr-welfare adapter's convention, with the published borrowing sign convention carried verbatim rather than normalised.A free cross-validation surfaced by the spot pins: Briefing Paper 10's AS2023 employee-NICs supply-side scoring (0.16741543…) is bit-identical to the Nov 2023 Chart 2.A terminal-year NICs value — the inventory restates the original scorings at full precision.
Tests
tests/test_obr_policy_effects_adapter.py: block counts, closed row shape, unit-per-metric, five spot values traced to published cells, TB.1 hierarchy identities (direct+indirect=total, subtotals=Σcomponents, £1 tolerance), byte-stable rebuild from the committed raw bytes. Full suite 248 passed / 6 skipped; ruff clean.Deferred, honestly
Annex A workbooks for five vintages were fetched, inspected, and NOT vendored (forecast levels only, no policy content — documented in raw/README.md); likewise the Mar 2025 / Nov 2025 / Mar 2026 Chapter 2 and Nov 2025 Ch.4/5 workbooks. Spring 2025/2026 package-effect chart data was not located in this pass; Mar 2026 decisions are covered in aggregate by TB.1. DB ingest waits for the post-#57 ingest shape; counterparts (PE-UK LSR, emulator) are #55 steps 2–3.
Builds #55 (step 1: harvest).
🤖 Generated with Claude Code