Skip to content

Assign SNAP take-up from state participation rates#375

Closed
daphnehanse11 wants to merge 1 commit into
mainfrom
codex/snap-state-conditional-takeup
Closed

Assign SNAP take-up from state participation rates#375
daphnehanse11 wants to merge 1 commit into
mainfrom
codex/snap-state-conditional-takeup

Conversation

@daphnehanse11

Copy link
Copy Markdown
Collaborator

Summary

  • replace the national SNAP take-up top-up with FY2022 USDA FNS state participation rates for all 50 states and DC
  • calibrate the SPM-unit flag against weighted eligible people (design weight × modeled snap_unit_size) while preserving reported SPM_SNAPSUB > 0 recipients as an anchor
  • apply each state's calibrated stable-draw cutoff to currently ineligible units too, preserving a take-up response for eligibility-expanding reforms
  • keep support-channel clones together, emit state/national participation diagnostics, and hard-gate missing rates, lost anchors, collapsed eligibility, state rate misses, and unexplained universal take-up
  • move the stage after the SNAP eligibility inputs and SCF assets are materialized so PolicyEngine-US can supply is_snap_eligible and snap_unit_size

Source and semantics

The manifest now carries USDA FNS's Reaching Those in Need: Estimates of State SNAP Participation Rates in 2022, Table 1:

The estimates measure the share of eligible people participating in an average month. FNS publishes whole-percentage estimates and reports estimates at or above 100% as 100%; the manifest and diagnostics document that limitation.

Verification

  • uv run pytest packages/populace-build/tests -q
  • uv run ruff check ...
  • uv run ruff format --check ...
  • git diff --check

Closes #372

@MaxGhenis MaxGhenis left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review (draft) — not merging; the code is healthy on main, but the calibration basis is inconsistent with the merged #371, and this overlaps #376.

Reviewing per request while this is a draft. The mechanism is clean and it rebases and tests green on current main (details below), so the blocker is a design/basis question, not code.

Core issue: the seed basis differs from what #371 now calibrates to

This stage calibrates the take-up flag to FY2022 FNS eligible-person participation rates. #371 (merged today) added snap_households weight-calibration targets that pin per-state FY2024 average-monthly household caseloads. Those are different measures (person-rate vs household-count) and different vintages (FY2022 vs FY2024), so on current main the seed and the weight-calibration objective are not pinning the same quantity — the double-pressure the two stages are supposed to avoid. #376 implements the same #372 goal by seeding to the FNS average_monthly_households facts #371's targets compile from, so its seed and #371's calibration agree by construction. As written, this PR would need to move to the household-caseload basis (i.e., converge with #376) — or the team picks one of the two #372 approaches — before it's consistent with the merged surface.

The four scrutiny points

  1. FY2022-on-2024 vintage — transparently recorded, assumption not stated. The manifest carries "vintage": "fiscal_year_2022", the notes cite Reaching Those in Need: 2022 Table 1, and the 100%-cap rounding limitation is documented. What's missing is a one-line acknowledgment that FY2022 is the latest published rate applied to the 2024 frame under a rate-stability assumption — worth stating explicitly (as #371 did for its own concept gap).
  2. Take-up contract / register-consistency — verified clean. takes_up_snap_if_eligible stays out_of_scope, so it's outside register-consistency's seed / count_calibrated signal registers and outside the rate-schema validation (which only fires for seed). Removing rate.value in favor of the state/period/measure block is therefore fine for an out_of_scope entry. Rebased onto main it passes assert_take_up_contract_current and the register-consistency test.
  3. Eligible-person weighting vs SPM-unit flag — documented and coherent, with a marginal caveat. The anchor (reported SPM_SNAPSUB > 0) and the person-weighted top-up (design weight × modeled snap_unit_size) are clearly described. The residual is conceptual: matching a person participation rate by flipping whole SPM units is an approximation at the margin (a unit is all-in or all-out at its person weight). Fine as a design choice, but note it — and it is the same reason the caseload-count basis (#376/#371) is a cleaner fit for a unit-grain flag.
  4. Conflict with #371 — the core issue above. No textual conflict (this PR touches snap_take_up.py / take_up_contract.json / source_stages.json / the builder; #371 touched ledger_targets.py / fiscal_targets.py, zero overlap), but a semantic one: person-rate seed vs household-count calibration.

Mechanics I verified

Rebases cleanly onto current main (single commit, no conflicts); test_us_snap_take_up.py + test_us_take_up_contract.py (22 tests) and the register-consistency / contract-preflight / snap slices of the builder test all pass on the rebased state. So it is technically healthy — the open question is which #372 basis to standardize on. Since #376 already implements the household-caseload basis consistently with the merged #371, the cleanest path is likely to converge there; flagging now, before more work, rather than after.

@MaxGhenis

Copy link
Copy Markdown
Contributor

Closing in favor of #376, which implements #372 on the household-caseload basis: it seeds the taker set to the same FNS average_monthly_households facts that the merged #371 (027355b) now calibrates weights to, so the seed and the weight-calibration objective pin the same administrative measure by construction. This draft's FY2022 eligible-person participation-rate basis is a different measure and vintage from what the merged surface targets, so carrying both forward would put the two SNAP stages in tension (full reasoning in the review comment above).

Worth salvaging from here: the FY2022 FNS Reaching Those in Need state participation-rate manifest (Table 1, with the 100%-cap and rounding caveats documented) is a good validator — an independent, sourced check that per-state modeled participation among eligibles lands near FNS's estimates — even though it isn't the seeding basis. If someone picks that up, this can be reopened or mined for the manifest, diagnostics, and the eligible-person weighting machinery.

The state-grain gate ideas here (missing-rate hard-fail, anchor preservation, collapsed-eligibility and universal-take-up landmine checks) all have counterparts in #376's gate, so nothing release-blocking is lost by closing.

@MaxGhenis MaxGhenis closed this Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Assign SNAP take-up state-conditionally: reported receipt + national rate bakes in CPS state underreporting

2 participants