Skip to content

Require state-level SNAP benefit hard targets#256

Open
daphnehanse11 wants to merge 1 commit into
mainfrom
snap-state-hard-targets
Open

Require state-level SNAP benefit hard targets#256
daphnehanse11 wants to merge 1 commit into
mainfrom
snap-state-hard-targets

Conversation

@daphnehanse11

Copy link
Copy Markdown
Collaborator

Summary

Closes #255.

SNAP currently calibrates to a single national hard target. This PR makes the 51 state-level SNAP benefit totals (50 states + DC) a required part of the US target profile, so a build can no longer silently ship national-only SNAP calibration.

Why the change is this small

Investigation for the 2026-07-01 SNAP research readout found the pipeline is already almost fully wired:

  • The Ledger package usda-snap-fy69-to-current already ships per-state total_benefits facts (usda_snap.fy2024.state_benefits.<region> record sets, geography_level: state, all 53 state agencies).
  • _direct_reference_from_fact already accepts state-geography facts for mapped families and stamps state_fips metadata — the same path per-state TANF targets use today.
  • The release builder already materializes state_fips-scoped targets by masking households to the state.
  • Territories (GU/VI) resolve no PolicyEngine state FIPS and are skipped automatically.

So with a current consumer_facts.jsonl, state SNAP hard targets activate with zero mapping changes. The only gap was enforcement.

Changes

  • gates.py: add required_metadata_keys to TargetCoverageRequirement — presence-of-key matching, so a requirement can demand state-dimensioned rows (state_fips present) without enumerating 50 FIPS values. Validated non-empty in __post_init__.
  • fiscal_targets.py: add the snap_state_benefits coverage requirement (family usda_snap, role snap_total, state_fips key required, min_matches=51).
  • Tests: gate-level tests for required_metadata_keys (present/absent/empty-key rejection); compile test proving a state-level usda_snap fact becomes a state_fips-scoped spec while a Guam row is skipped; profile tests proving 50 state rows or a national-only surface fail the gate; state SNAP rows added to the complete-profile fixture.

Draft status / rollout note

This gate makes builds fail if the consumer-facts artifact lacks the state SNAP record sets. Before marking ready:

  • Confirm the production consumer_facts.jsonl includes the state_benefits record sets (the source package parses them from the FNS FY24 workbook, but the deployed artifact vintage needs checking).
  • Run a fiscal refresh build against a current artifact and review the per-state SNAP calibration diagnostics (zero-support states would need reviewed exclusions, TN-style).

Follow-up (not in this PR)

State participation counts (average_monthly_households/average_monthly_persons) as indicator targets — needs an explicit average-monthly vs annual-ever bridge before becoming a hard constraint.

Test plan

  • uv run pytest packages/populace-build/tests — full suite passes (269 tests).
  • ruff check / ruff format --check clean on changed files.

🤖 Generated with Claude Code

The Ledger's usda-snap-fy69-to-current package ships per-state
total_benefits facts, and the direct-reference path already compiles
state-geography usda_snap rows into state_fips-scoped hard targets.
Nothing required them, though: a build consuming a facts artifact that
predates the state record sets silently ships national-only SNAP
calibration.

Add a snap_state_benefits coverage requirement (50 states + DC) so the
target-profile gate fails loudly when the compiled registry lacks the
state SNAP surface, and extend TargetCoverageRequirement with
required_metadata_keys so a requirement can demand state-dimensioned
rows by metadata-key presence instead of enumerating FIPS values.
Territory rows (GU, VI) resolve no PolicyEngine state FIPS and are
skipped at reference compilation, so they never count toward the gate.

Closes #255

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@daphnehanse11 daphnehanse11 force-pushed the snap-state-hard-targets branch from 42d651f to 19f77c6 Compare July 2, 2026 14:17
@daphnehanse11 daphnehanse11 marked this pull request as ready for review July 2, 2026 16:53
@daphnehanse11

Copy link
Copy Markdown
Collaborator Author

@PavelMakarchuk one open item for merge timing (not code review): this gate makes the 51 state SNAP rows mandatory, so it should land only once the production consumer_facts.jsonl is confirmed to include the usda_snap.fy2024.state_benefits.* record sets (they're in the Ledger source package, but I can't check the deployed artifact vintage from my side). One-line check against the artifact:

grep -c 'state_benefits' consumer_facts.jsonl   # expect >= 51

If the artifact predates the state record sets, merging this will (correctly) fail the next build until the artifact is refreshed — that's the gate doing its job, but better to sequence it deliberately.

@MaxGhenis

Copy link
Copy Markdown
Contributor

Independent review verdict: the mechanism is sound (presence-of-key matching validated non-empty; state SNAP already maps to an active hard target on main, so this enforces an existing surface; orthogonal to #245's CD posture) and local gate/target tests pass. Two things block merge, matching the PR's own unchecked boxes: (1) run one fiscal-refresh build on a current artifact to confirm the deployed consumer_facts carries the state_benefits record sets, and (2) record any zero-support states as reviewed exclusions (TN-style). With per-state diagnostics from that run attached, this is merge-ready.

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.

Add state-level SNAP benefit hard targets from USDA FNS administrative data

2 participants