Compile USDA SNAP state and national caseload targets from FNS average-monthly facts#371
Conversation
…e-monthly facts The ledger usda-snap-fy69-to-current package already ships average_monthly_households and average_monthly_persons record sets at national and per-state grain alongside the total_benefits facts populace compiles today, but only the dollar measures were mapped. That leaves the SNAP surface dollars-only: a build can hit a state's benefit total with an unrepresentative recipient set, and nothing pins caseload or average benefit per household. Map both caseload measures as indicator_sum targets over the simulated taker set: SPM units with positive annual snap (households) and their members via indicator_map_to person (persons). The take-up assignment (#294) seeds takers to reproduce the FNS participation rate, so the taker set is the model counterpart of the average monthly caseload. FNS caseload facts carry aggregation 'mean' (a fiscal-year mean of monthly stock counts), which the ledger compile guard rejected wholesale. Add an explicit per-mapping fact_aggregation=time_mean contract: a time-mean of a stock count is still a linear level target, while per-unit ratios (e.g. average_monthly_benefit_per_person) remain rejected by default. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…cipants ~50% Previewing both indicator targets against the buildi-sparse release showed the household mapping lands at -1.4% of the FY2024 FNS national average (21.89M vs 22.20M) with no calibration pressure, while the person mapping overcounts by +48% (61.8M vs 41.7M): the real SNAP assistance unit is often a subset of the SPM unit (FNS persons per household 1.88 vs 2.82 members per simulated taker unit) and PolicyEngine-US does not model sub-unit participation. Calibrating to the person counts would fight the household target, so the persons measure stays unmapped, with a test pinning that it does not compile. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Preview of the compiled household-caseload targets evaluated against National: 21.89M weighted taker units vs 22.20M FNS (−1.4%). Feasibility ceiling under the 5× weight cap: 3.75× the target. States (largest household-count errors, current weights):
Two observations:
The persons measure was dropped from the mapping after the same preview showed a structural +48% overcount (see second commit); the per-state persons errors ranged +11% to +398% with the bias always positive, confirming it is a unit-definition mismatch rather than noise. |
…nt unit Rebasing onto main (SNAP caseload targets, #371) surfaced two flaws in the finding-4 unit gate: - expected_unit was checked before the aggregation/value checks, so a fact that cannot compile at all (a mean fact with no time_mean contract) failed on the missing declaration rather than on its real cause. The gate now runs after the structural checks. - the committed expected-unit map assigned usd to every indicator measure, but USDA SNAP average-monthly caseload is a count. Count-based indicators now map to their real denomination (SNAP -> count, CMS -> people), so the household caseload fact (unit 'count') passes and a genuine drift still fails the gate. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The snap_take_up stage (#294) anchors take-up on reported ASEC receipt and fills non-reporters to the national FNS participation rate. CPS SNAP receipt underreporting is strongly state-dependent, so the national fill starves the worst-underreporting states of taker households no reweighting can recover: the buildi-sparse release undershoots ten state benefit targets by 7-43% while a feasibility audit shows every state but California reachable under the weight cap, and state taker-household counts err from -38% to +325% against FNS caseloads (#372). Add a snap_state_take_up stage following the medicaid_take_up pattern (#331) at SPM-unit grain: reported recipients always take up; the fill draws at an in-build state rate (FNS average-monthly household count over weighted modeled-eligible units, an assignment prior, never cited as provenance) and is greedily calibrated to the FNS FY2024 state counts among eligible non-anchored units. The targets are the same snap_households rows the weight-calibration targets compile from, so the seed and the calibration objective agree. Saturation is recorded, not failed; a release gate enforces anchor preservation, per-state count fidelity within unit-weight granularity, full state target coverage, and the universal-take-up landmine. Persons caseloads stay untargeted: the SNAP assistance unit is often a subset of the SPM unit, so member counts overcount FNS participants by roughly half. Implements #372; stacked on the #371 caseload-target compilation. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The snap_take_up stage (#294) anchors take-up on reported ASEC receipt and fills non-reporters to the national FNS participation rate. CPS SNAP receipt underreporting is strongly state-dependent, so the national fill starves the worst-underreporting states of taker households no reweighting can recover: the buildi-sparse release undershoots ten state benefit targets by 7-43% while a feasibility audit shows every state but California reachable under the weight cap, and state taker-household counts err from -38% to +325% against FNS caseloads (#372). Add a snap_state_take_up stage following the medicaid_take_up pattern (#331) at SPM-unit grain: reported recipients always take up; the fill draws at an in-build state rate (FNS average-monthly household count over weighted modeled-eligible units, an assignment prior, never cited as provenance) and is greedily calibrated to the FNS FY2024 state counts among eligible non-anchored units. The targets are the same snap_households rows the weight-calibration targets compile from, so the seed and the calibration objective agree. Saturation is recorded, not failed; a release gate enforces anchor preservation, per-state count fidelity within unit-weight granularity, full state target coverage, and the universal-take-up landmine. Persons caseloads stay untargeted: the SNAP assistance unit is often a subset of the SPM unit, so member counts overcount FNS participants by roughly half. Implements #372; stacked on the #371 caseload-target compilation. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Integrates the SNAP state surface (#256 #371 #376), the PUMA ladder (#394), the loader-test decouple (#392), and --no-latest (#397) with the campaign's 93 restored input families. Six builder conflict hunks + the us_runtime export merges resolved keeping BOTH lineages: main's snap_state_take_up stage/diagnostics/telemetry sequenced alongside the campaign's other_health_insurance and ssi_take_up stages. Full populace-build suite passes on the merged tree (exit 0, 1 cred skip). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Implements #370.
What
Maps the FNS household-caseload measure the ledger
usda-snap-fy69-to-currentpackage already ships (national + per-state, alongside thetotal_benefitsfacts populace compiles today) into calibration targets:("usda_snap", "average_monthly_households")→indicator_sumover SPM units with positive annualsnap(target_rolesnap_households)It flows through the existing
INDICATOR_LEDGER_TARGETS→_direct_reference_from_factmachinery, so state facts compile per state withstate_fipsmetadata automatically, exactly like the benefit-dollar targets.average_monthly_personsis deliberately not mapped (second commit): previewed against the buildi-sparse release, a person indicator over taker-unit members overcounts FNS participants by +48% (61.8M vs 41.7M) because the real SNAP assistance unit is often a subset of the SPM unit (FNS persons per household 1.88 vs 2.82 members per simulated taker unit) and PolicyEngine-US does not model sub-unit participation. A test pins that the persons measure does not compile.Aggregation contract change
FNS caseload facts carry
aggregation: mean(a fiscal-year mean of 12 monthly stock counts), andtarget_spec_from_ledger_referencerejected every non-sum fact. This PR adds a scoped opt-in: a mapping may declarefact_aggregation: time_mean, asserting the mean is taken over time periods on a stock count — still a linear level target. Per-unit ratios (e.g.average_monthly_benefit_per_person) remain rejected by default, and a new test pins the rejection of mean facts without the contract.Concept mapping and validation
The compiled counts are of the simulated taker set. The take-up assignment (#294) seeds takers to reproduce the FNS participation rate, so the taker set is the model counterpart of the average monthly caseload rather than an annual-ever count. Previewed on
populace-us-2024-buildi-sparse-rmloss100with shipped weights: 21.89M weighted taker units vs the 22.20M FY2024 FNS national average (−1.4%) with no calibration pressure on counts. Per-state preview in the PR comment below.Why
The SNAP surface is currently dollars-only. In the default release, 10 states undershoot their benefit targets by 7–43% while a feasibility audit shows all but CA are reachable within the 5× weight cap — the dollar targets lose loss-weight trades (the
usda_snapfamily holds ~0.9% of total loss weight under sqrt-value weighting). Caseload counts land in the count basis, which holds its own half of the 50/50 loss budget, raising SNAP's effective share within the standard scheme; and dollars + caseload jointly pin average benefit per household, anchoring recipient composition for downstream reform work. Full audit numbers in #370.Dependency
Targets compile only for facts present in the consumer feed. If the current
consumer_facts_buildh_v8.jsonlfilters out the caseload measures, the consumer bundle needs regeneration before these targets appear in a build.Tests
test_snap_household_caseload_fact_maps_to_snap_indicator(national, household grain)test_state_snap_household_caseload_fact_compiles_with_state_fips(state grain,state_fips)test_snap_person_caseload_fact_is_not_compiled(persons stays unmapped)test__given_mean_fact_without_time_mean_contract__then_compilation_fails(doctrine guard)test_us_fiscal_targets.pyandtest_ledger_targets.pypass.🤖 Generated with Claude Code