P4 checks (2026-10-30)
- Clean re-run (deleted results.json/forecasts.csv/exclusions.csv, re-ran): byte-identical: PASS
- forecasts.csv has 32 rows: PASS
- each origin has 4 rows (2h x 2 models): PASS
- origin < first_release date of targets, target > train_last_date: PASS
- target = train_last + h quarters: PASS
- naive forecast == train_last_value: PASS
- train_last_value matches own vintage last row: PASS
- actual == first_release value: PASS
- RMSE >= MAE > 0 for all: PASS
- rel_improvement consistent: PASS
- exclusion ledger sums: 314-151-3=160=n_analysed: PASS
- first_origin < last_origin: PASS
- n_input_rows match audit (307..314, 318, 13): PASS
- mean abs revision equals mean of per-target revisions: PASS
- analysis.py does not read current_vintage for estimation (only cur.loc in revision line): PASS
- Magnitude: model RMSE (1.49/1.40) below naive (2.01/2.27); plausible given naive inherits full quarter-to-quarter noise (e.g. the 2025Q1 first release of -0.3). PASS