Skip to content

Add dated notes, starting with 2026Q2 CPI#94

Open
vahid-ahmadi wants to merge 5 commits into
mainfrom
notes-cadence
Open

Add dated notes, starting with 2026Q2 CPI#94
vahid-ahmadi wants to merge 5 commits into
mainfrom
notes-cadence

Conversation

@vahid-ahmadi

Copy link
Copy Markdown
Contributor

Stacked on #90, #92 and #93merge those first.

Why

The site had models, validation, and now a track record, but no recurring output: nothing that reads a release and says what it implies. That is the gap between a model catalogue and research.

What

notes/ — short dated notes. Each one names the data vintage it was written against, ends in an implication, and states what would change the view. Because the vintage store is append-only, a note stays checkable against the data as it stood when written rather than against data revised afterwards. The index page states plainly that a note is not advice, not a house view, and not a claim of skill.

First note: CPI fell to 2.8% y/y in 2026Q2 — 0.12pp above our published median, inside the 68% band — but the same forecast round has inflation back at 3.12% by 2026Q4, against a Bank Rate held at 3.75% since 18 December 2025. The implication is stated as directional, on the central case, with the model's own 2.09–4.14% band for that quarter given as the caveat rather than omitted.

Every figure is sourced

CPI from ONS D7G7 · GDP from ABMI · unemployment from MGSX · Bank Rate from BoE IUDBEDR (verified: 150bp of cuts through 2024–25, unchanged since 18 Dec 2025). Forecast figures from the archived 2026-07-21 round.

Deferred: the market transmission layer

The planned markets/ page is not in this PR. BoE IADB serves the par-yield series (IUDSNPY, IUDMNPY, IUDLNPY) but I could not verify from the API which maturity each code denotes, and publishing "2-year gilt" against the wrong series is exactly the class of error that would discredit the page. It needs a maturity mapping confirmed against BoE documentation first. Bank Rate is unambiguous and is used here.

🤖 Generated with Claude Code

vahid-ahmadi and others added 5 commits July 25, 2026 02:12
The site could show a current forecast but not a record of past ones:
current_forecast.json is overwritten on every data-edge refresh, so a
superseded forecast survived only in git history. That is right for the hero
chart and wrong for evidence — a forecast only counts if it provably existed
before the outturn did.

forecasts/ archives each round immutably and scores it once outturns land:

- archive.py snapshots the moving artifact into rounds/<date>/<model>.json,
  refusing to overwrite an existing round.
- score.py scores rounds against versioned outturns on four measures — MAE,
  signed bias, 68/90% band coverage, and naive baselines — and generates both
  scorecard.json and the marked blocks of the page, with --check for staleness.
- forecast-archive.yml fails any PR that modifies, renames or deletes an
  archived round.

The page states the empty record plainly (1 round, 0 scored, first scorable
period 2026Q2) rather than deriving a headline accuracy figure from nothing.
It also distinguishes this from the existing pseudo-out-of-sample evaluation,
which tests the method but was run knowing the answer.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Everything on the site was pinned to published forecast vintages with no live
official data behind it, so there was no way to score a forecast, and no way to
tell whether an analysis had used data that existed at the time.

data/fetch.py pulls three ONS series (real GDP ABMI, CPI annual rate D7G7,
unemployment rate MGSX) and writes a dated snapshot per fetch. Nothing is ever
edited in place: an upstream revision arrives as a new file beside the old one,
so revisions are visible and any past number stays reproducible.

- A fetch finding no upstream change writes nothing, so real revisions are not
  buried in a stream of identical files. Network failures are reported as
  failures, never as "no revision".
- latest/ is a flattened copy for the page builders. The site is static under a
  CSP with connect-src 'self', so the browser cannot call the ONS; anything
  shown has to be baked in at build time.
- JSON rather than Parquet: these series are small, and a git-diffable format
  makes a revision a reviewable diff instead of an opaque blob.
- fetch-vintages.yml runs weekday mornings after the release window and opens a
  PR rather than pushing.

Verified against the live API: 285 quarters of GDP to 2026Q1, 150 of CPI to
2026Q2 (2.8%), 221 of unemployment to 2026Q1.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Stacked on the forecast archive and the data vintage store: this connects them.

forecasts/ingest_outturns.py maps the forecast variables onto the ONS series
(CPI direct; GDP converted from the chained-volume level to year-on-year), and
appends outturns tagged with the data vintage they were read from. Only periods
some archived round actually forecast are ingested — this is evidence for the
track record, not a general data mirror.

First result: 2026Q2 CPI, forecast 2.68% against an outturn of 2.80%. Error
-0.12pp, inside the 68% credible band. 2026Q2 GDP is still pending; the "next
result due" logic now checks per variable rather than per period, since CPI
lands about six weeks ahead of the quarterly GDP estimate.

The page records, rather than glosses, the weakness of this first data point:
the 2026-07-21 round was archived retroactively out of current_forecast.json,
so its provenance rests on that file's git history (committed 2026-07-21 14:54
BST) rather than on the archive, and the ONS published 2026Q2 CPI the following
morning. A sixteen-hour lead is a real lead but thin evidence, and saying so is
cheaper than having a reader find it.

CI now also fails when an outturn is available upstream but not recorded, so the
record cannot silently stop being scored.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The site had models, validation and now a track record, but no recurring
output — nothing that reads a release and says what it implies. That is the
gap between a model catalogue and research.

notes/ is the cadence. Each note names the data vintage it was written
against, ends in an implication, and states what would change the view. The
append-only vintage store means a note stays checkable against the data as it
stood when written rather than against data revised afterwards.

First note: CPI fell to 2.8% y/y in 2026Q2, 0.12pp above our published median
and inside the 68% band, but the same round has inflation back to 3.12% by
2026Q4 against a Bank Rate held at 3.75% since 18 December 2025. Stated as a
directional read on the central case, with the model's own 2.09-4.14% band for
that quarter given as the caveat rather than omitted.

Every figure is from the vintage store or the archived round: CPI from ONS
D7G7, GDP from ABMI, unemployment from MGSX, Bank Rate from BoE IUDBEDR.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 25, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
macromod Building Building Preview Jul 25, 2026 1:29am

Request Review

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant