Add SNAP release acceptance validation#413
Conversation
daphnehanse11
left a comment
There was a problem hiding this comment.
Reviewed against today's Build K run (populace-us-2024-buildk-snapval-1fe5fc4-20260713T171826Z, first certified build with the #371/#376 caseload surface — context in #419). This PR independently converges on several of the same findings, and the acceptance harness is exactly the right idea. One blocking issue, one split request.
What this addresses (keep all of it)
- The acceptance report + CLI is the regression check #419 asks for (its "proposed fixes" item 3), with better structure than the ad-hoc scorecard used for Build J/K: per-state gates, missing/duplicate-state checks, AK borough coverage, focused tests.
- The "release blocker" finding (H_TENURE stripped; construction base/checkpoint unpublished) independently corroborates #412 and adds a concrete new instance — worth cross-linking there.
- FY2022 participation rates as advisory-only is doctrine-clean and the 93.7%-vs-88% national gap points the same direction as #419's eligibility-surface finding.
Blocking: the fit gates certify calibration, not the release
us_snap_state_target_fit_gate reads final_estimate vs target from calibration_diagnostics.json — the calibrated column. Build K demonstrates why that's insufficient for a gate named "release acceptance": its calibrated caseload fit is 52/52 within 10%, while snap > 0 simulated from its exported h5 (what every downstream PE-US user computes) is 23/52 outside 10% (AK +563%). Mechanism in #419: the take-up stage derives fill rates on pre-mass-repair weights, and the export drops eligibility-narrowing inputs the build frame carried, so the pinned column doesn't reproduce from the artifact. Either add an artifact-simulated caseload gate (the participation validation already opens the h5, so the plumbing exists) or rename/document these gates as calibration-fit checks. As written, a release could pass acceptance while shipping 5× caseload error in some states — the exact failure the harness exists to catch. Also worth pinning the measure semantics (FNS average-monthly households vs annual snap>0) either way — the #371 persons-mapping lesson.
Split request: the tips promotion
Promoting irs_soi.form_w2_social_security_tips to compiled is directionally right but for the wrong stated reason, and it belongs in its own PR:
- The body says "now that the current Ledger feed supplies it" — the feed always carried the amount; the fence was about model support (structural-zero
tip_income). The correct promotion basis is that thesipp_tipssource stage +us_sipp_tips_signal_gateare now wired on main. - The diff deletes the fence wholesale rather than recording the promotion evidence (#405 fences every entry for exactly this reason).
- It's unverified at release scale (the rebuild aborted at H_TENURE). The signal + zero-support gates make failure loud rather than silent, but a promotion that's never survived a build shouldn't ride along in an acceptance-harness PR.
Coordination note: any fresh feed (this PR's 57f1198 bundle, or the Build K bundle) compiles ~16 separate-CHIP state targets that v8's zeros never compiled, and they have zero support on the sparse frame — a build with this feed hits the zero-support gate unless it carries the 40-cell exclusion list from Build K (see #411 and the Build K notes in #419).
🤖 Generated with Claude Code
…caseload gate (#419) Fixes CI: snap_fy2022_participation_rates.json was shipped undeclared in country_package.json, failing the package-manifest gates. Adds us_snap_artifact_caseload_gate, wired into the assembled acceptance verdict as a required check: simulate engine snap > 0 per state from the shipped H5 (MicroSeries weights) and bound every miss against the same FNS snap_households targets the build calibrated to. The calibrated-column fit gate alone certifies the solver, not the artifact — populace#419 measured a release with 52/52 calibrated caseload fit whose shipped dataset misses 23/52 states (AK +563%). Semantics recorded in the gate payload (average-monthly stock target vs annual engine participation). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s env) The wheels CI installs bare wheels without the us extra; tables is optional there by design. Matches the test_demographics.py convention. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Correction to my review's blocking rationale: the "23/52 artifact divergence" I cited was a measurement artifact in my own audit script (state via 🤖 Generated with Claude Code |
Summary
irs_soi.form_w2_social_security_tipsback into the compiled fiscal target set now that the current Ledger feed supplies itFresh-feed recertification
I composed a current FY2024 US Ledger feed plus the required FY2022/FY2023 support facts:
57f11984f85c03bb4b5ef13912789b20762240b9f7a97f6465e96c5fb3e734d32736774snap_householdsrows and 51 statesnap_totalrowsThe currently published Build J release is not SNAP-certifiable against that feed:
Build J predates the state take-up stage, so its caseload audit uses post-calibration release weights and the current external FY2024 targets. It is an audit of the published artifact, not a reconstruction of its stage-time assignment gate.
Release blocker
I also attempted a no-bypass rebuild from current
main. A fresh release candidate cannot be reproduced honestly from the published artifacts: the final H5 strips construction-only inputs including rawH_TENURE, while Build J's construction base/target-frame checkpoint and exact ACS donor were not published. I stopped rather than invent household tenure. Nothing was published andlatest.jsonwas not changed.The highest-impact next artifact is therefore the exact construction base/target-frame checkpoint (plus its exact ACS donor), followed by fixing the modeled SNAP eligibility surface exposed by California and the other saturated states.
Tests
uv run ruff check ...uv run pytest packages/populace-build/tests/test_us_snap_release_acceptance.py packages/populace-build/tests/test_release_target_parity.py packages/populace-build/tests/test_us_fiscal_targets.py -q