Repository navigation
Conversation
The scf_wealth stage mapped the SSI countable-resource leaf bond_assets to
SCF summary-extract `bond` alone. The Fed's extract macro defines BOND as
"total bonds, not including bond funds or savings bonds" (bulletin.macro.txt
line 1497); savings bonds are the separate SAVBND = X3902. The archived
pipeline at 42ed5d45 kept savbnd as scf_savings_bonds, a construction-only
net-worth component that microcosm does not build, so an SCF-drawn
household's savings bonds reached no SSI leaf.
PolicyEngine-US 2.2.1 documents bond_assets as "Value of bonds and
government securities. Imputed from SIPP TVAL_BOND." and cites POMS
SI 01140.240 (U.S. Savings Bonds), as does its SSI countable-resource list.
SIPP 2023 TVAL_BOND, the other half of the 50/50 blend, sums government
securities and municipal/corporate bonds, and the 2023 SIPP data dictionary
defines government securities as "such as savings bonds, T-Bills, T-Bonds,
T-Notes" (EOWN_GOVS). The SCF side now reads bond + savbnd.
- SCF_FINANCIAL_ASSET_TARGET_COMPONENTS["bond_assets"] = ("bond", "savbnd").
- The loader's required columns now derive from the components map, so a
missing savbnd raises the documented ValueError rather than a KeyError.
- Target construction moves into _scf_financial_asset_targets so its
invariants can be property-tested in memory.
- Docstrings no longer claim exact parity with 42ed5d45 and say why.
- Tests: the raw fixture gains savbnd (drawn last, so other columns keep
their values); the loader test checks bond + savbnd row for row; a
parametrized test requires every component column; a static test checks
that the leaves partition disjoint FIN constituents; Hypothesis tests
check the sum rule, that savings bonds add exactly to bond_assets and
nothing else, and that sentinels count as zero.
- Re-pin the seed-kernel attestation that any byte edit to scf_wealth.py
moves: EXPECTED_HASHES seed_protocol and seed_map, the loader golden, the
US spec_sha256, and docs/evidence/spec-engine/us-f0-coverage.json.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The in-session review of 35a7429 found no code defect but several overclaims and gaps in prose, plus one untested branch: - Test the zero floor: a Hypothesis test with signed non-sentinel components checks each leaf equals max(sum, 0) and is nonnegative (the floor-removal mutant now fails it). The savings-bond additivity test says why it draws only nonnegative values. - Rename the static test to what it checks: the leaves draw on distinct FIN constituents (five of the ten). It no longer claims a partition, and the "never exceed FIN" note now states its premise. - Docstring: face value applies to savbnd and to every component of bond, while SIPP asks balance or market value and POMS uses market value (SI 01140.250) or redemption value (SI 01140.240); liq includes prepaid cards and is at least $1 for account holders; the eCPS reference shares behind the gate bands are unweighted record shares. - Changelog: the new fragment names the non-SSI consumers of bond_assets in PolicyEngine-US 2.2.1 (SNAP asset test, spm_unit_cash_assets, MO TANF). The pending #1129 fragment no longer says this stage does not read savbnd. - Re-pin the seed-kernel attestation for the new scf_wealth.py bytes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
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.
What changes
The
scf_wealthstage mapped the SSI countable-resource leafbond_assetsto SCF summary-extractbondalone. It now readsbond+savbnd.bulletin.macro.txtline 1497). U.S. savings bonds are the separateSAVBND = X3902(line 1825).42ed5d45mappedbond_assetstobondand keptsavbndasscf_savings_bonds, a construction-only component of its net-worth reconciliation. Microcosm builds none of those components (net_worthis the directnetworthanchor). So onmainan SCF-drawn household's savings bonds reach no SSI leaf.Why this is a fix toward the cited source, not a methodology choice
uv.lockpins; read from the PyPI wheel):bond_assetshas documentation "Value of bonds and government securities. Imputed from SIPP TVAL_BOND." Itsreferenceis POMS SI 01140.240 and SI 01140.250.gov.ssa.ssi.eligibility.resources.countablelistsbond_assets, and its reference list includes "SSA POMS SI 01140.240 - U.S. Savings Bonds".pu2023_schema.jsonvarnum 4196 definesTVAL_BONDas "Person-level sum of value of other interest-earning assets (TJSGOVSVAL TJOGOVSVAL TOGOVSVAL TJSMCBDVAL TJOMCBDVAL TOMCBDVAL)".EOWN_GOVSas "Owned any government securities during the reference period [such as savings bonds, T-Bills, T-Bonds, T-Notes, and government sponsored enterprise (GSE) credit instruments such as Fannie Mae]".No double counting. In PE-US 2.2.1, no formula adds
bond_assetsto another input that holds savings bonds:net_worth, which this stage fills from SCFnetworthand which includessavbnd, is read by no formula.spm_unit_assetsunset.Who reads
bond_assets. In PE-US 2.2.1 the change reaches more than SSI.bond_assetsalso feeds:gov.usda.snap.asset_test.sources, 7 CFR 273.8(c)(1));spm_unit_cash_assets, which many state TANF and local general-assistance resource tests read;Those consumers were not probed separately.
Measurement limits. These affect amounts, not which leaf savings bonds belong to, and they also apply on
main:X3902) and for every component ofbond.Valuing
savbndat an estimated redemption value would be a separate methodology change. It is tracked as a follow-up and not done here.Size of the effect
SCF 2022 donor (pinned
rscfp2022.dta, survey-weighted, 131.3M households;wgtalready sums to all households across the five implicates):bond(before)savbndbond+savbnd(after)savbndamong holders is $2,000, which is the SSI individual resource limit.bondand 1.08% holdsavbnd.probes/ssi_threshold_flips.py):savbndmoves 0.24% from at or below $2,000 to above it.TVAL_BOND> 0, and 8.3% of households haveTHVAL_BOND> 0. Among the rows the stage trains on (its allocation-flag mask), the reference-person share is 4.1%.Stage probe. This used the
origin/mainscf_wealthandsipp_financial_assetscode, the real SCF extract, and the realpu2023.csv(streamed from the Census zip and SHA-256 checked against the pin). Recipients were CPS ASEC 2023 persons (146,133), with seeds 0, 1 and 2, comparingmainwith this PR's first commit:bond_assetsweighted person nonzero share (the gate's metric; band 0.001–0.12)bond_assetson this samplenp.array_equal) between arms in every seed. Onlybond_assetschanges.Invariants
CI tests in
tests/engine_free/us/test_us_scf_wealth.py:FIN=LIQ+CDS+NMMF+STOCKS+BOND+RETQLIQ+SAVBND+CASHLI+OTHMA+OTHFIN, macro line 2177). The leaves use five of the ten. This test is static.max(sum, 0)and is nonnegative.bond_assetsequals the bond-only target plus cleanedsavbndexactly, never falls below it, and leavesbank_account_assetsandstock_assetsunchanged. With a negative non-sentinelbond, the floor could absorb savings bonds; the SCF extract has none.ValueErrorfor every component, includingsavbnd.Checked once on the pinned extract (a probe, not a CI test, because CI has no SCF data):
fin(0 rows).Mutation check. Each of four mutants was killed by the targeted tests:
savbndsentinels: 3 failures.nmmfinto bond: 3 failures.Pins
Any byte edit to
scf_wealth.pymoves the seed-kernel attestation; this is the same cascade as #1129, perdocs/us-spm-role-stage.md§5. These values are atfd4381fbf:mainEXPECTED_HASHES["seed_protocol"]b4afa376…62b2155f…EXPECTED_HASHES["seed_map"]0fa116d9…767ac1c4…spec_sha256(test_us_multispine_pool_tool.py)d1df6b31…d70f33e4…test_spec_engine_loader.py)6273f82f…8d578ce2…docs/evidence/spec-engine/us-f0-coverage.jsonsource_stages.json,sources.yaml) declares no SCF component map, so it does not move.scf_*outputs, includingscf_savings_bonds. The module docstring now says thatscf_savings_bondsmust leave that partition if those components are ever built.mainhas since gained Export Microcosm schema metadata for Orrery #888 (graph and Orrery only). Recomputed against it with this PR's files overlaid, the four pins are unchanged andspec_engine_coverage.py --checkpasses.How this was verified
engine-us, which checks the USspec_sha256pin.origin/maincode served from git objects, with this PR's files overlaid, because free disk was under the 40 GB floor when the work started. The interpreter was another worktree's venv, used read-only; its third-party versions matchuv.lock.spec_engine_coverage.py --checkpasses against the committed JSON.fd4381fbf,tests/engine_free/us/test_us_scf_wealth.pyandtests/engine_free/shared/test_spec_engine_loader.pygive 52 passed.spec_engine_coverage.py --checkpasses (42184/42184 fields, 41/41 inventory checks).ruff checkandruff format --checkare clean.Review
Round 1 was an in-session multi-lens review (code and tests, source fidelity, sizing), with an adversarial verifier on each finding. It found no code defect. Its prose and test findings are fixed in
fd4381fbf:Round 2 goes to an independent review.
Release-path notes (no action in this PR)
tools/route_a/route_a.env.example(lines 44-45), which the Route A README tells operators to copy, passes the d490 tail register (SHAfa576099…). That register waivesbond_assets(share 0.766 to 0.773, under d450), and the waiver expires at the next certified build. If this change lowersbond_assets' top-100 share to 0.75 or below on the export, as the probe suggests it may, the release gate fails on a stale exclusion (gates.pystale-exclusion branch;build_us_fiscal_refresh_release.pyhelp: "Stale entries fail the gate"). The operator then re-measures and drops the entry, which moves the pinned SHA. The register's own text cites Max's d490 pre-authorization for a re-measured register with "same or fewer columns".route_a.shSSI_SHA). The shift among SSI recipients is small (+0.06 to +0.07 pp above $2,000 in the probe), but the next release should note it.savbndmoves households across both the $2,000/$3,000 and $10,000/$20,000 limits, and the net effect on the $10k/$20k reform was not measured.Not in this PR
bank_account_assetsis parallel but separate. SIPPTVAL_BANKincludes CDs, while SCFliqexcludes them (cds, $841B). However, PE-USbank_account_assetsdocuments only checking, savings and money-market accounts and cites only POMS SI 01140.200, so the cited source does not settle it. It is tracked as a follow-up.savbndat redemption value rather than face value is tracked as a follow-up.axiom: n/a: microsim input imputation (SCF donor mapping); no policy rule changes
Evidence (probe scripts and outputs, sources, mutants, review findings):
~/reviews/microcosm-scf-savings-bonds/🤖 Generated with Claude Code