Seed pregnancy at the national rate; document the never-carried SNAP exemption inputs#352
Conversation
MaxGhenis
left a comment
There was a problem hiding this comment.
Review — seeded rate verified against the cited source; the absent-inputs register is the right mitigation for the #340 blind spot
Reviewed the seeded value against its citation, checked that nothing computed is hardcoded, and traced both the pregnancy channel and the three documented-absent inputs into the pinned PolicyEngine-US ABAWD formula. It holds up well.
The 4.1% rate is exactly corroborated by the cited CDC source
The seed lives in source_stages.json as data (pregnancy_rate.value = 0.041) with its citation, reached as a manifest parameter — not hardcoded in Python. Good. And the value is right: the point-in-time formula is annual births × 39/52 ÷ female-15-44 population, which equals the general fertility rate × 39/52. CDC final 2023 natality (NVSR 74-1) reports a general fertility rate of 54.5 births per 1,000 women aged 15–44, so the point-in-time pregnant share is 0.0545 × (39/52) = 0.0409 ≈ 4.1%. Equivalently, 3,596,017 births × 39/52 ÷ ~66.0M women 15–44 = 4.09%. The 39/52 gestation factor (≈0.75, a 9-month pregnancy as a share of the year) is the standard construction. Value verified.
The one divergence from the retired pipeline — national uniform rate instead of the retired live-fetched state VSRR/ACS rates — is documented honestly in both the module and the manifest notes, with the hermetic-build rationale and the #351 follow-up. That is the right way to handle a scope reduction (and a good contrast with #350, where two mapping refinements are still framed as parity).
Plumbing confirmed — is_pregnant and the three absent inputs all feed the ABAWD formula
is_pregnantis read at exemption (E) Pregnant — 7 U.S.C. 2015(o)(3)(E) inmeets_snap_abawd_work_requirements, matching the citation in the stage notes exactly. Seeding women 15–44 (others get False) is correct since only that population can carry the flag.- The three
documented_absent_inputs—is_homeless,was_in_foster_care,is_incapable_of_self_care— are genuinely consumed downstream:is_homelessandwas_in_foster_care(asformer_foster_youth) are both read in the pre-HR1 exemption branch of the same formula. So documenting them as absent-and-defaulting is not bookkeeping — those exemption channels silently do not fire, exactly the #340 failure mode, and the register makes that visible in release diagnostics rather than invisible.
Suggestion (not blocking): connect the register to the #340 column families
documented_absent_inputs is the first release-diagnostics home for "an input the engine reads but the build cannot populate." That is precisely the surface #340 asked for. The two are complementary but distinct: this register covers inputs with no survey source (structurally unfixable from ASEC), whereas #340's families (tip_income, overtime, auto-loan, education-credit, retirement-contribution, casualty, misc-itemized, childcare) have a source and were merely never persisted (build/persistence-scope — and #350 fixes five of them). Consider whether these should converge into one "inputs not reaching the engine" diagnostics block with two clearly-labeled sub-registers (no-source vs not-persisted), or at least cross-reference #340 here, so a release has a single place to answer "what isn't modeled and why." Worth a look, not a merge condition.
CI is green on test/wheels for 3.13 and 3.14; 18 tests cover the seeding bounds, determinism, clone consistency, guards, and gate behavior. Not merging — review only.
|
Agreed the no-source vs not-persisted distinction deserves one diagnostics home. Pushed d57abe9 adding the scope note to the register: this block is the no-survey-source class (structurally unfixable from ASEC), the #340 families are persistence-scope (and #350 fixes five of them), and if a not-persisted register lands later the two should surface as labeled sub-blocks of one 'inputs not reaching the engine' diagnostic. Left the structural convergence itself to #340 rather than widening this PR. |
|
Expanding on the earlier note with the specifics of how the review suggestion was taken (d57abe9, CI green): What changed. The What deliberately didn't change. The structural convergence itself stays with #340 — building the combined block here would mean this PR defining #340's register shape before #340's own work lands, and the suggestion was explicitly non-blocking. The seeded rate (0.041, whose CDC corroboration in your review — GFR 54.5 × 39/52 — is a nice independent check), the manifest citation, the gate band, and all 18 tests are untouched. If you'd rather the combined block land now with a placeholder not-persisted sub-register, happy to do that here instead — say the word. |
|
#350 is merged (thanks for the docstring fix). #352/#353 now conflict with main after the #350 squash — 24 hunks, mostly tools/build_us_fiscal_refresh_release.py — so leaving the rebases to you as offered rather than racing your active branches; happy to take them over if you'd rather. #358 merged independently. Once #352/#353 land, the next build cut picks up the full work-requirement surface. |
…exemption inputs Add a pregnancy source stage porting the retired pipeline's seeding (women 15-44, CDC natality-derived rate, stable blake2b draws keyed on source identity) at the national 4.1% rate — state-level variation is documented follow-up under #351 since populace builds cannot make live CDC/ACS fetches. Remove the is_pregnant parity-register entry. Add US_DOCUMENTED_ABSENT_INPUTS, emitted into every release manifest as documented_absent_inputs, for the three inputs with no ASEC source (is_homeless, was_in_foster_care, is_incapable_of_self_care) — they are not persisted columns, so the degenerate-input gate cannot document them, and this register makes the always-False assumption visible per the #248 acceptance criteria. Closes #351; advances #248. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Post-rebase wiring: pregnancy slots after eligibility_inputs in the stage manifest, runtime registry, gate plumbing, and release manifest. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
d57abe9 to
c5802bc
Compare
Add a snap_abawd_discretionary_exemption source stage seeding is_snap_abawd_discretionary_exempt with stable blake2b draws across potentially covered adults 18-64 at the 7 U.S.C. 2015(o)(6) cap rate (8% from FY2024); the engine intersects with modeled coverage so the exempt share among covered individuals lands at the statutory rate in expectation. Full-cap seeding is documented as an explicit upper bound (USDA reports usage typically runs well under the allotment; cap exceeded in only five state-year instances across FY2016-19), with usage-report refinement tracked under #323. Rebuilt on main after #350/#352 merged; stage order runs eligibility_inputs -> pregnancy -> snap_abawd_discretionary_exemption. Addresses the SNAP half of #323; advances #248. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add the three work-program inputs (is_snap_work_program_participant, weekly_snap_work_program_hours, is_snap_workfare_participant) to the documented-absent-inputs register: no ASEC item measures SNAP E&T, qualifying work-program, or workfare participation, so the always-False defaults are now visible in every release manifest with the compliance channels they suppress. USDA reports E&T reaches a small minority of participants, so the default understates compliance only modestly. Add contract tests pinning the register's coverage, issue citations, disjointness from the degenerate exclusions, and non-overlap with seeded inputs. Closes #249; advances #248. Stacked on #352. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Add a snap_abawd_discretionary_exemption source stage seeding is_snap_abawd_discretionary_exempt with stable blake2b draws across potentially covered adults 18-64 at the 7 U.S.C. 2015(o)(6) cap rate (8% from FY2024); the engine intersects with modeled coverage so the exempt share among covered individuals lands at the statutory rate in expectation. Full-cap seeding is documented as an explicit upper bound (USDA reports usage typically runs well under the allotment; cap exceeded in only five state-year instances across FY2016-19), with usage-report refinement tracked under #323. Rebuilt on main after #350/#352 merged; stage order runs eligibility_inputs -> pregnancy -> snap_abawd_discretionary_exemption. Addresses the SNAP half of #323; advances #248. Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Closes #351; advances #248.
Summary
Two deliverables for the unsourced SNAP exemption inputs, split by whether the retired pipeline had an answer:
1. Seed
is_pregnant(ported from the retired pipeline). The CPS ASEC does not measure pregnancy, so the retired enhanced-CPS pipeline seeded it: women 15–44 drawn against CDC natality-derived pregnancy rates (point-in-time rate = annual births × 39/52 over the female 15–44 population). This PR adds apregnancysource stage doing the same with stable blake2b draws keyed on source identity (clone-consistent, bit-reproducible — the #294 pattern). One honest divergence, documented in the module and manifest notes: the retired pipeline varied rates by state via live CDC VSRR + Census ACS fetches with a 4.1% national fallback; populace builds are hermetic, so this stage seeds at the cited national rate and state-level variation stays follow-up work under #351. The rate is data, not code — it lives insource_stages.jsonwith its citation and reaches the handler as a manifest parameter. Apregnancy_signalrelease gate enforces non-constant output and a weighted pregnant-share band ([0.002, 0.02], bracketing rate × female-15–44 share ≈ 0.8%). Theis_pregnantentry leaves the parity known-gaps register since the stage now produces it.2. Document the three never-carried inputs.
is_homeless,was_in_foster_care, andis_incapable_of_self_carehave no ASEC source and are not persisted columns, so the degenerate-input gate cannot see them (and its anti-rot rule means register entries for unstored columns would break it — checked before writing). Instead, aUS_DOCUMENTED_ABSENT_INPUTSregister is emitted into every release manifest asdocumented_absent_inputs, naming each input, why it cannot fire (the CPS samples the housed population; no foster-history or incapacity items), and the tracking issue. That satisfies #248's acceptance criterion that unsupported inputs be explicitly documented in release diagnostics rather than defaulting invisibly.Why now
The pre-OBBBA SNAP counterfactual (PolicyEngine/policyengine-us#8944) restores the pregnancy, homeless, and former-foster-youth exemption channels. With this PR, pregnancy contributes realistically to population estimates, and the other two are visibly documented as absent instead of silently zero — so OBBBA work-requirement research can cite exactly which channels are modeled.
Testing
test_us_pregnancy.py: 18 tests over manifest declaration (rate present with citation), eligibility bounds (sex and 15–44 age edges), per-seed determinism, source-identity clone consistency, guard paths (missing raw columns, missing rate, unexpected parameters), frame integration (idempotence, constant-default reseeding), and gate pass/fail behavior.is_pregnantjoinsUS_RELEASE_REQUIRED_PERSON_SOURCE_COLUMNS).Note: this branch and #350 (
eligibility_inputs) both extend the same wiring anchors; whichever merges second gets the usual rebase.🤖 Generated with Claude Code