Verification checks (P4)
- Clean re-run: deleted out/results.json, forecasts.csv, exclusions.csv; ran analysis.py twice; results.json byte-identical (cmp) and forecasts.csv identical md5: PASS
- 32 forecast rows: PASS
- 8 origins x 2 h x 2 models unique: PASS
- vintage_date==origin_date: PASS
- train_last_date < origin_date < target first-release date: PASS
- target = train_last + 3h months: PASS
- naive forecast == train_last_value: PASS
- train_last matches vintage file 2024-01-31: PASS
- train_last matches vintage file 2024-04-30: PASS
- train_last matches vintage file 2024-07-31: PASS
- train_last matches vintage file 2024-10-31: PASS
- train_last matches vintage file 2025-01-31: PASS
- train_last matches vintage file 2025-04-30: PASS
- train_last matches vintage file 2025-07-31: PASS
- train_last matches vintage file 2025-12-31: PASS
- actuals match first_release: PASS
- rmse/mae naive h1 recomputed: PASS
- mae<=rmse naive h1: PASS
- rmse/mae model h1 recomputed: PASS
- mae<=rmse model h1: PASS
- rel_impr h1 identity: PASS
- rmse/mae naive h2 recomputed: PASS
- mae<=rmse naive h2: PASS
- rmse/mae model h2 recomputed: PASS
- mae<=rmse model h2: PASS
- rel_impr h2 identity: PASS
- exclusion ledger: 314 - removed == model n_train (159): PASS
- ledger rows n_before-n_after==n_removed: PASS
- n_input_rows match wc: PASS
- n_analysed == rows of last vintage (314): PASS
- revision stat in plausible range [0,2] pp: PASS
- Magnitude vs baseline: model RMSE (1.48/1.40) below naive (2.01/2.27); AR(1) phi≈0.34, intercept≈1.75 stable across origins (implied mean ≈2.6%) — plausible for US GDP: PASS