Skip to content

Carry ASEC reported SSI receipt as receives_ssi in the US build - #1312

Draft
MaxGhenis wants to merge 4 commits into
mainfrom
us-reported-ssi-receipt
Draft

MaxGhenis wants to merge 4 commits into
mainfrom
us-reported-ssi-receipt

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 18, 2026 •

Copy link
Copy Markdown
Contributor

Summary

PolicyEngine-US rules that key on actual SSI receipt read (ssi > 0) | receives_ssi. Examples are state supplements, the Medicaid SSI categories, SNAP categorical eligibility, Head Start, and county GA exclusions. Colorado's AND-CS joins them once PolicyEngine/policyengine-us#10145 merges. No US release carries receives_ssi, so every one of these rules depends only on modeled SSI. This PR makes the base build carry the survey fact, and makes the release coverage gate require it.

Draft until Max rules on the methodology question below (d1675). Merging changes what the next national default carries. Nothing here touches a published release.

What changes

  • Producer. cps_carried.reported_ssi_receipt marks a person when SSI_VAL > 0 and at least one reported reason is their own (or none is recorded).

    • Respondents whose only RESNSSI reasons are "on behalf of a disabled/blind child" (codes 3 and 4) are payees, not recipients, so they are not marked. That is 133 rows and 0.45M weighted people on the pinned default.
    • ASEC asks SSI only of people aged 15 and older, so no child under 15 is marked.
    • The producer refuses positive amounts when the reason columns are missing.
    • The annual report is broadcast to all months, like receives_snap, receives_tanf and receives_wic.
    • No amount input is filled. Sending the reported amount as the ssi input would replace the federal formula for every reporter, so SSI reforms would stop moving them. ssi_reported is deprecated and feeds nothing.
  • Release gate. receives_ssi is a required coverage input with no reviewed exclusion (US_ASEC_REPORTED_SSI_RECEIPT_REQUIRED_INPUTS). us_release_input_coverage_gate also checks two invariants:

    • every marked person reports a positive amount of their own: SSI_VAL on CPS rows, native ssi_reported on stacked ACS rows;
    • every marked CPS-measured person carries takes_up_ssi_if_eligible. The take-up stage anchors CPS reporters only, so ACS rows are counted, not judged.

    Release preflight treats these row-dependent failures as at risk on the L0 path and as certain on the full pool.

  • ACS rows (multispine pool and ACS local chain). map_acs_native_inputs maps receives_ssi natively from a positive SSIP (blank below age 15 is no receipt). The pool lists it as complete on both spines (_POOL_NATIVE_COMPLETE_OUTPUTS), so it is never a QRF transfer target, and the stacked surface stays at 134 targets.

    • Only the pool's engine-input provisioning (assembled native 5→6) and the checkpoint identity move. They were re-derived with tools/repin_us_pool_engine_contracts.py and tools/spec_engine_coverage.py.
    • The release coverage manifest was regenerated with tools/build_us_release_input_coverage_manifest.py.
    • ACS records no reason for receipt, so ACS rows cannot exclude child payees.
  • Docs. docs/us-reported-ssi-receipt.md, plus measured evidence in docs/evidence/us-reported-ssi-receipt/ (aggregates only).

Invariants (property-tested with Hypothesis)

  • A marked person always reports a positive SSI_VAL.
  • A person whose recorded reasons, in either position, are all on-behalf-of-child codes is never marked.
  • Every other positive reporter is marked.
  • Adding an own reason never unmarks a person.
  • The derivation is idempotent and keeps an existing column.
  • The consistency check fails exactly when a marked person lacks a positive observed SSI_VAL or the take-up anchor.
  • Round trip: a receipt derived from SSI_VAL, with the take-up stage's anchor, always passes the check.

Measured effect (pinned default populace_us_2024.h5, 2026; fixed-weight sensitivity)

Method and full tables are in docs/evidence/us-reported-ssi-receipt/README.md.

  • Approach: PolicyEngine-US main fab18b7d6b and #10145 f450d4ab00, run on every household with a reporter plus every Colorado household. receives_ssi is a person input with no cross-household interaction, so these deltas are the national deltas. The run checks this on Colorado's non-reporter households, whose deltas are exactly zero.
  • No full-population run. A full-population rerun is held back by the host reservation until 10/21.
Change on today's default (fixed weights, not recalibrated; no ACS rows) With receives_ssi
Federal SSI $0
Household net income +$339M
SNAP +$261M
Early Head Start +$1,156M
ACA PTC −$863M
Medicaid enrollees +1.41M people
State supplements (MI, WA, WY) +$14M
Colorado AND-CS under #10145 $0 (unchanged)

Nearly all of this comes from reporters whom the model pays $0. They are treated as SSI recipients for categorical purposes while their modeled SSI income stays $0. Of 5.15M own-reason reporters, the model pays 2.79M. The other 2.35M get nothing for these reasons:

  • 1.03M PUF clones without the disability criterion. PUF tax-detail clones of under-65 reporters do not inherit the ASEC meets_ssi_disability_criteria anchor, although they carry SSI_VAL and the take-up anchor. Anchoring them would add $6.2B of federal SSI before take-up recalibration.
  • 0.78M income-test failures. On PUF clones these have a median countable income of $5,265/month from PUF-imputed incomes; on ASEC rows, $1,750.
  • 0.45M resource-test failures. Zeroing every reporter household's liquid assets brings only 0.16M of them into payment (cf. Imputed savings put CPS SSI reporters over the resource limit, so reforms 'add' people who already report SSI #1184).

Main's $233M Colorado supplement goes to 11 records, none of which reports SSI. They are modeled-eligible non-claimants, so reported receipt restores none of it.

Methodology choice for Max (d1675)

More than one reading is defensible, so this is queued as a decision. There are two separate axes.

A. CPS rows (the national default):

  1. As implemented. Receipt goes on ASEC rows and their PUF clones, the same as the take-up anchor and the other receipt inputs.
  2. ASEC rows only. Clones carry no receipt, which matches the disability-criteria anchor. This is about 40% of the effect (+0.37M Medicaid enrollees, +$141M net income).
  3. Hold until the attribute fixes land. These are SSI anchors treat adults who receive SSI only on behalf of a child as disabled SSI claimants #1313 (payees), PUF clones of under-65 SSI reporters keep the take-up anchor but not the SSI disability-criteria anchor #1314 (PUF-clone disability anchor) and Imputed savings put CPS SSI reporters over the resource limit, so reforms 'add' people who already report SSI #1184; then (1).
  4. Don't carry receipt. Keep receipt modeled-only.

B. ACS rows (stacked and local-area releases only; the national default has none):

  1. As implemented. Receipt is mapped natively from SSIP. ACS cannot exclude child payees, and ACS reporters are not take-up-anchored until SSI take-up anchors CPS SSI reporters but not ACS SSIP reporters on stacked frames #1317, so some carry receipt with $0 modeled SSI. This effect is unmeasured.
  2. No ACS receipt until SSI take-up anchors CPS SSI reporters but not ACS SSIP reporters on stacked frames #1317 anchors ACS take-up.

Recommendation: A1 + B1. The receipt is a leaf survey fact that the raw-only doctrine carries. The disagreements come from imputed attributes, fixed where they arise (#1313–#1315, #1317).

Review

Round 1 (GPT-6.1 Sol, dd6e51791) asked for changes; all five findings are addressed in 1466d4195:

  1. The QRF-transferred ACS receipt could break the take-up invariant. The take-up arm is now scoped to CPS-measured rows.
  2. ACS has native SSIP. ACS receipt is now native and never transferred.
  3. Dry-run classification: receipt-consistency failures are at risk on L0, with their counts kept.
  4. The payee rule is now order-free, with an independent oracle.
  5. The evidence claim about the two engine trees is corrected.

Round 2 (Sol, 1466d4195) found all five round-1 findings resolved and asked for six smaller changes. They are addressed in 902c6d2d8:

Verification

  • ruff check . passes. python3 tools/ci_test_plan.py verify passes.
  • Focused suites pass: test_us_reported_ssi_receipt (new), test_us_stacked_spine, test_us_spine_blindness, test_us_puf_support, test_us_acs_transfer, test_us_plan, test_release_input_coverage, test_spec_engine_typed_closure.
  • Full engine-free and engine-us categories run in CI (a host reservation holds full local suites).
  • tools/generate_us_bundle_from_constants.py --check and --engine-lock-only --check pass. tools/repin_us_pool_engine_contracts.py reports current.

Follow-ups filed

axiom: n/a: Microcosm data input, no rules change.

🤖 Generated with Claude Code

PolicyEngine-US rules that key on actual SSI receipt read
(ssi > 0) | receives_ssi, but no US release carries receives_ssi, so every
such rule depends on modeled take-up and imputed attributes alone.

- cps_carried.reported_ssi_receipt: positive SSI_VAL, excluding a
  respondent whose only RESNSSI reasons are on behalf of a disabled or
  blind child (the child's payee). Refuses positive amounts without the
  reason columns. Broadcast to all months like the other receipt inputs.
- Release input coverage: receives_ssi is a hard requirement with no
  reviewed exclusion; the gate also checks every marked person reports a
  positive SSI_VAL and carries takes_up_ssi_if_eligible.
- Stacked ACS chain: the column joins the source_operator_cps_carried
  transfer family (surface 134 -> 135, early targets 48 -> 49); spec
  bundle, engine ABI lock and pool contracts regenerated with the repo's
  tools.
- Tests: examples, Hypothesis properties for the producer and the
  consistency check, and coverage-contract tests.
- Evidence: measured effect on the pinned default with PolicyEngine-US
  main and #10145 (docs/evidence/us-reported-ssi-receipt).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 3 commits October 18, 2026 20:10
The added source_operator_cps_carried person boolean moves the reviewed
inventories that pin the US spec surface: field-usage pointer pins and
counts (+23 authored fields), the F0 inventory hashes (stacked authority,
early families, gap-fill schedule, checkpoint identity), the coverage
report, the legacy pool publication manifest golden, and the counts in the
multispine, battery, head-to-head, compiler-IR and lineage tests. Every
moved value was recomputed by the code that asserts it; the late, tail and
calibration authority components are unchanged.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Review (Sol r1) found the stacked ACS chain QRF-transferred receives_ssi
although ACS measures SSI receipt itself, and that a transferred ACS
receipt need not carry the CPS take-up anchor.

- ACS rows: map_acs_native_inputs maps receives_ssi from positive SSIP,
  and the pool lists it as complete on both spines, so it is never a
  transfer target. This reverts the stacked-surface re-pins (134 targets
  again); only the pool's engine-input provisioning and checkpoint
  identity move.
- Consistency gate: the amount arm uses SSI_VAL on CPS rows and native
  ssi_reported on ACS rows; the take-up arm judges CPS-measured rows,
  which the take-up stage anchors, and counts ACS rows.
- Preflight: receipt-consistency failures are at risk on the L0 path,
  certain on the full pool, and keep their counts.
- Payee rule is order-free across RESNSSI1/RESNSSI2, with an independent
  test oracle.
- Evidence README: correct the claim that the two engine trees differ
  only in Colorado.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- ACS SSIP: refuse a blank at age 15 or older (blank only below the
  income universe is no receipt) and record the universe count in the
  native receipt; the operator boundary accepts that declared extra key
  for receives_ssi only.
- Consistency check: count ACS receipt rows without take-up separately,
  and fail receipt when the table carries neither SSI_VAL nor
  ssi_reported.
- Tests: receives_ssi is never a pool transfer target and is native on
  the legacy ACS multispine chain.
- Text: manifest note, local-release coverage note, waiver flag help, and
  the take-up anchor wording (physical ASEC source identities).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant