Restore the SSI countable-resource asset columns via the SCF-wealth stage (#368 Deliverable 2)#373
Merged
Merged
Conversation
…#368) - scf_wealth.py: person-level own-income predictors + head-carry output; add fetch_scf_2022_summary_extract provisioning helper. - __init__.py: export the scf_wealth surface. - l0_refit_export.py: bank/stock/bond join US_RELEASE_REQUIRED_PERSON_SOURCE_COLUMNS. - ecps_parity_known_gaps.json: drop the three now-produced asset layers. - test_us_l0_refit_export fixture: carry the three asset columns. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…CF fetch (#368) Deliverable 2: the scf_wealth stage was implemented and unit-tested but never invoked by the release builder — imported only. Wire with_us_scf_wealth_inputs + us_scf_wealth_signal_gate into base_frame construction (after the SNAP discretionary-exemption stage, before ACA source outputs), loading the SCF 2022 donor from --scf-summary-extract or the cached fetch. The three SSI countable-resource asset inputs (bank_account_assets / stock_assets / bond_assets) now reach the export, so the #368 input-coverage gate and the SSI reform-coverage smoke both turn green instead of failing by design. fetch_scf_2022_summary_extract: - Send a browser User-Agent — Federal Reserve econres returns HTTP 403 to the default urllib agent, so the fetch failed outright. - Pin and verify SHA-256 of both the zip and the extracted rscfp2022.dta (block-ladder _sha256 provenance convention); a cache hit is reused only when its digest matches, a drifted cache is re-fetched, and a mismatched download raises. Verified against the Federal Reserve download 2026-07-09. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…onkeypatch the stage in the main() pipeline test (#368) Two fixes surfaced by the Deliverable 2 acceptance probe: 1. Data integrity: the module docstring claimed head-carry "keeps the SSI baseline intact ... the SSI baseline is preserved (verified on the Build H dense frame)". The acceptance probe (seed 0, policyengine-us 1.764.6) disproves it — the SCF-only draw drops the restored SSI baseline to 4.74M recipients vs the dense-native 8.05M and runs the $10k/$20k delta to +$9.7B vs the +$1.6B reference. Replace the fabricated claim with the measured numbers and the two documented root causes (both #356 follow-ups, neither a bug in this stage): the probe overlays assets on weights calibrated without them (no SSI-target refit; post-hoc overlays are documented to land at +$9-26B), and the stage ships the SCF draw without the retired SIPP blend (FINANCIAL_ASSET_SOURCE_SCF_PROBABILITY=0.5, confirmed in the retired asset_imputation.py). Also stop the docstring overclaiming that it "reproduces" the retired construction — it ships the SCF half of it. 2. test_main_writes_diagnostics_before_post_calibration_gate_failure runs the full main() pipeline with every stage monkeypatched; add the four scf-stage patches (fetch/load/with_us_scf_wealth_inputs/us_scf_wealth_signal_gate) so the newly wired stage does not fetch SCF or fit a QRF on the fake frame. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…368) The salvage commit placed the scf_wealth import/export block out of alphabetical order (before fiscal_targets instead of after release_input_coverage), which fails `ruff check .` — the CI lint step. Move it to the sorted position; no behavior change. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This was referenced Jul 9, 2026
Open
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
populace #368 Deliverable 2 — restore the SSI countable-resource asset columns
Deliverable 1 (#369) shipped the hard input-coverage gate + SSI reform-coverage smoke, both RED by design on today's asset-less artifacts. This finishes Deliverable 2: it restores
bank_account_assets/stock_assets/bond_assetssossi_countable_resourcesderives again and the SSI resource-limit reform class scores nonzero — turning both #368 gates green.What was missing
The
scf_wealthstage (packages/populace-build/src/populace/build/us_runtime/scf_wealth.py) was implemented and unit-tested but never invoked by the release builder — the imports and the--scf-summary-extractCLI arg were wired, butwith_us_scf_wealth_inputswas not called anywhere. This wires it intobase_frameconstruction (after the SNAP discretionary-exemption stage, before the ACA source outputs), following the establishedwith_us_*_inputs→us_*_signal_gate→ raise-on-fail pattern, so the three asset inputs reachexport_frame→ the input-coverage gate (line 6353) and the reform-coverage smoke (line 6484) both pass.SCF fetch: fixed HTTP 403 + sha-pinned
fetch_scf_2022_summary_extractfailed outright — Federal Reserve econres returns HTTP 403 to urllib's default user-agent. It now sends a browser UA and pins/verifies the SHA-256 of both the zip and the extractedrscfp2022.dta(thebuild_us_block_ladder_artifact._sha256provenance convention): a cache hit is reused only when its digest matches, a drifted cache is re-fetched, a mismatched download raises. Digests verified against the Federal Reserve download 2026-07-09:scfp2022s.zip→3bb4d890ae2463ff6039ec7692e375f544dd98a55a37ca2cb2340354b9cc9d80rscfp2022.dta→6b8dd2d935a76ed225ddebc80fb2db22a467f0c80d9a1acaa67b4584aa4bafd1Acceptance probe — the SSI reform binds again
Imputed the three columns onto the Build H dense frame (
_buildh-runtime/.../dense/artifacts/populace_us_2024.h5, 337,704 households / 865,046 persons) via the real stage (with_us_scf_wealth_inputs, seed 0), wrote a release H5, and scored the reforms with policyengine-us 1.764.6 (the Build H engine; dense-native SSI is identical on 1.764.6 and the #356 reference's 1.752.2). Reference class = dense-native f0af251 verified from #356 (TY2026): baseline 8.05M / $63.6B, 42.5% nonzero on assets, $10k/$20k = +$1.60B / +499k, no-limit = +$16.05B / +3.23M.ssi_countable_resourcesnonzero shareBefore restore,
ssi_countable_resourcesis exactly $0 for every record and both reforms score +$0.00B — the #368 failure, probe-reproduced. After restore, countable resources carry signal and the reforms bind: +$9.66B at $10k/$20k clears the $1B reform-coverage floor ~9.7×, so the smoke gate passes. 2024 is consistent (+$9.23B / +$25.90B).Plausible but above the dense-native magnitude — diagnosed (two documented factors, neither a stage bug)
The reform binds with the right sign and order of magnitude (no-limit +$27.22B is within ~1.7× of the +$16.05B reference), but the $10k/$20k delta runs above +$1.60B and the restored baseline (4.742M) sits below 8.05M. Two compounding causes, both already flagged on #356:
policyengine_us_data/utils/asset_imputation.py): the retired pipeline draws these three leaves from a 50/50 SIPP+SCF blend (FINANCIAL_ASSET_SOURCE_SCF_PROBABILITY = 0.5); SIPP supplies the realistic low liquid-asset mass at the bottom. This stage ships the SCF draw only, assigning more liquid wealth to the SSI-marginal population.The nonzero incidence already matches the reference (40.3% vs 42.5%); the gap is amount + calibration, not the grain. The module docstring records this precisely, and the prior draft's fabricated "the SSI baseline is preserved (verified on the Build H dense frame)" claim is replaced with the measured result. Aligning the magnitude — SIPP blend + SSI-target refit in a full rebuild — is the #356 follow-up.
Tests
test_us_scf_wealth.py(20 tests): manifest, donor load, sentinel/race mapping, head-carry, determinism, frame integration, signal gate, sha-pinned fetch.test_us_fiscal_refresh_builder.py::test_main_writes_diagnostics_...: the full-pipelinemain()test now monkeypatches the scf stage consistently with the other stages.ruff check .clean (also fixed an unsorted scf export block the salvage left inus_runtime/__init__.py, which would have failed CI lint).No changelog fragment: populace carries no changelog mechanism (no
changelog.d/, no towncrier config), consistent with #369.Refs #368. Reference and root cause verified against #356 and the retired
asset_imputation.py.🤖 Generated with Claude Code