# 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