The Congressional Budget Office’s 10-Year Budget Projection is the baseline everything in federal finance gets measured against — deficits, debt, the arguments. So I checked whether its numbers age well: cbo-validator lines up three published vintages (2024-06, 2025-01, 2026-02) against the actuals CBO later reported, for government-wide outlays, receipts, deficit, and debt held by the public, FY2024–FY2025. All data is public — five CSVs plus both schema files from CBO’s data repo, frozen into the project on 2026-09-11.
The headline
The cleanest comparison is the 2024-06 vintage’s FY2025 row — published about two years before the actual, a true ex-ante forecast:
| Variable | Baseline → Actual | Deviation |
|---|---|---|
| Receipts (FY2025) | — | +$196.988bn above baseline |
| Outlays (FY2025) | — | +$34.504bn above baseline |
| Deficit (FY2025) | — | $162.484bn smaller than projected (+8.38%) |
Context for the scale: FY2025 actuals were outlays of $7,009.986bn, receipts of $5,234.616bn, a deficit of −$1,775.370bn, and debt held by the public of $30,172.402bn — 99.4% of GDP. (Sign convention: deficits are negative, so a positive deviation means a smaller deficit than projected.)
The trap: not every row is a forecast
This is the part that matters, and the reason a naive comparison misleads:
- The first fiscal year of each vintage is an anchor, not a forecast. The 2025-01 vintage’s FY2024 column was published with the FY2024 actual already known — CBO was reporting, not predicting. That row measures CBO’s restatement between publications, and the tool labels it “restatement check, not forecast” and mutes it in the chart so forecast error and revision stay visually separable.
- CBO restates history between publications. Measured on the frozen files: FY2023 outlays ran 6,134.507 (2024-02 vintage) → 6,134.672 (2025-01) → 6,134.673 (2026-02); FY2024 outlays 6,750.485 → 6,750.856. Small in dollar terms, large in principle — a baseline comparison that isn’t vintage-aware quietly mixes CBO’s revisions into CBO’s forecast error.
- The anchor year verifies the freeze. Each vintage’s first FY equals the matching historical CSV exactly — 12 of 12 values — and that identity is used as a pin, never scored.
The build step enforces the other identity on every row: outlays − receipts + deficit = 0, and the page re-checks it from the shipped JSON on every load.
The caveats, stated
Baselines assume the legislation in place at publication (CBO’s assumed-legislation caveat), the scope is completed fiscal years only — FY2026 is in progress at the freeze and is excluded from every comparison — and the FY2025 gross-debt context figure is sourced from the 2026-02 ten-year anchor, labeled as such, because the historical dataset carries no gross-debt series. It’s an independent analysis of public data, not CBO’s work.
The method generalizes: when you compare any forecast to an actual, first ask what the forecaster knew at publication time — the rows where the answer is “the actual” are audit rows, not scorecard rows.
Next: a copilot that says no.
— Joanna