Repository navigation
Place each ACS rent draw on its own household's head - #1444
Merged
Merged
Conversation
impute_us_pre_subsidy_rent drew rent in household-table order and wrote it through a head mask in person-table order. The two orders differ on every US base, so 91% of base households carry another household's rent, and rent lands almost independently of tenure. Match draws to heads by household id instead, and pin the behaviour with a test that reverses the person table. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 1, 2026
Draft
housing_inputs.py is an attested US kernel, so the rent fix moves its seed map, seed protocol and spec digests. This commit applies the documented repin sequence and regenerates the coverage receipt. The new property test gives each household a distinct draw, adds multi-person households and shuffles the person table. It fails on the old assignment. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
November 1, 2026 23:54
About 91% of households hold another household's draw, but many of those draws are zero both ways. About 47% hold the wrong rent. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Merging on the standing gates at head 233f459.
Run the rebuild from a rent-free base, because |
This was referenced Nov 2, 2026
MaxGhenis
pushed a commit
that referenced
this pull request
Nov 3, 2026
#1444 changes the attested kernel us_runtime/housing_inputs.py. Running the documented sequence locally (repin --legacy-only, the bundle generator, the repinner, the coverage tool) moved: - seed_map and seed_protocol in inventory_coverage.py EXPECTED_HASHES, to main's values (07fe9086..., 4ff99232...), which the merge had already carried into the coverage evidence's map/protocol/implementation digests; - the bundle spec_sha256, be81ebbc... -> 7d2aaa4d..., in test_us_multispine_pool_tool.py and both spec_sha256 fields of docs/evidence/spec-engine/us-f0-coverage.json. Nothing else moved: source_stages.json, the bundle YAML, the field-usage counts (42,489 configuration fields, 32,704 authored, 9,785 resolved), the source_manifest, late_resource_semantics, tail, full_checkpoint and take_up digests, and the resolved-reference and tail-control counts (337, 942) are unchanged. The coverage tool reports 42489/42489 configuration fields and 41/41 inventory checks. The repinner's first pass builds its inventory report against the stale seed pins and wrote covered_item_count == 40 into test_spec_engine_coverage_tool.py; a second pass restored 41, so that file is unchanged here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6Jmpqc2fSZAM219btF
MaxGhenis
pushed a commit
that referenced
this pull request
Nov 6, 2026
* Impute federal student loan balances from the 2025 SCF (#1073) Add the scf_education_debt runtime module. It carries federal education debt from the 2025 SCF through two weighted QRFs and allocates it to the SCF-style reference person and spouse as four person inputs: federal_student_loan_balance and federal_parent_plus_loan_balance (policyengine-us#9721), is_income_based_repayment_new_borrower and federal_student_loan_graduate_share (policyengine-us#9724). - Only loans flagged federal count; X7179 is spread over loans 1-6 in proportion to their balances; whose-education code 3 is Parent PLUS on the reference person, codes 6, 7 and -7 the reference person's own. - scf_wealth declares the two pinned 2025 files as artifacts 3 and 4 with a new scf_2025 vintage. Net worth, liquid assets and auto loans stay on 2022. - The fiscal-refresh tool runs the stage only with --scf-education-debt. The four columns join the stored-input register with the pending- engine reason the parent ids carry; the coverage manifest is left until the engine pin defines them. - Regenerate sources, vintages and calibration YAML and re-pin the frozen source_stages.json and calibration projection digests. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Add SCF 2025 education-debt fit receipts (#1073) Weighted fit of the stage against SCF 2025: source checks against the Fed's Alternate Table 13 (19.92%, $25,500, matched exactly), a five-fold family hold-out with a predictor ablation, and the stage imputed onto the published default populace-us-2024-spm-20260915 at its weights. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * WIP: SCF-matching recipient definitions and #1073 host-fit diagnostics Codes the CPS recipient side the way the SCF defines it: married includes cohabiting partners, children count the partner's children, and the reference person follows the householder-couple rule. These move Parent PLUS from 21.5% to about 16% of imputed federal balances (SCF 13.8%) but leave the borrower-household share at about 14.2% (SCF 18.2%). Adds diagnosis and experiment scripts and their first receipts. The predictor-shift and remedy runs still need to run (on Modal; local memory is too low); the fit receipt and PR body are not updated yet. Part of #1073. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Condition the education-debt QRFs on SCF-comparable predictors (#1073) The first host fit drew federal education debt for 14.4% of CPS households against 18.2% in SCF 2025, and gave Parent PLUS 21.5% of balances against 13.8%, while the SCF hold-out reproduced the SCF. The cause is covariate shift: the CPS host carries tax-record imputations of interest and dividends and of Social Security and pensions, which put 42% and 18% of 25-34 households above zero against 16% and 6% of SCF families. Mapping each to its SCF distribution at equal rank moves the host's borrower share most of any predictor; all together reach the hold-out level (receipts/1073-predictor-shift-diagnosis.json). - SCF_EDUCATION_DEBT_CANDIDATE_PREDICTORS keeps every predictor both sides build; SCF_EDUCATION_DEBT_PREDICTORS, used by both QRFs, leaves out the two shifted income components, each with its reason in SCF_EDUCATION_DEBT_EXCLUDED_PREDICTORS. - The recipient definition sums income over the reference person, spouse or partner and their children, the CPS counterpart of the SCF primary economic unit, on top of the SCF-matching codings. - The stage spec drift check now covers both QRFs; the stage notes, module docstring and changelog say why. - The receipt builder reports the build, the first receipt and the all-candidates setting side by side and records commit and run id. - experiments/modal_1073.py runs the heavy steps on Modal, adds generate and spec steps, and collects every tracked file a run changed. Re-pins the frozen source_stages.json digest; the generated mirrors are regenerated on Modal in a follow-up commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Regenerate the US stage spec for the SCF-comparable education-debt predictors (#1073) Output of tools/generate_us_bundle_from_constants.py (run on Modal at b9f3d67, run 20261020T154855Z-b9f3d6719): both education-debt QRFs drop interest_dividend_income and social_security_pension_income, the stage notes follow the module, and the stage-asset digest moves. The Modal experiment runner now strips the Modal runtime's PYTHONPATH from step environments; inheriting it put Modal's own packages beside the venv's and the worker-identity check refused the duplicate idna. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Make the spouse-routing test independent of draw chance (#1073) test_a_spouse_draw_without_a_spouse_moves_to_the_reference_person checks where a spouse's draw lands. With the synthetic donor's random incidence and a 30-household frame, the SCF-comparable predictor set draws no debt for any couple on 3 of 20 seeds (seed 11 among them), while the routing assertion holds on all 20 for both predictor sets (Modal probe run 20261020T161732Z-5d047f687). Every donor family now borrows in this test. The Modal runner gains --script for an uncommitted local probe, recorded by sha256 in the run log. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Collect the e35c4b0 receipts, add a Modal launch entrypoint, bind the loan register entries (#1073) - experiments/1073-scf-education-debt-receipts.md and its JSON are the receipt of Modal run 20261021T133110Z-e35c4b0f9 at e35c4b0 (build, first-receipt and all-candidates settings on the published default). - experiments/modal_1073.py gains `launch`, which spawns the run and exits, so a stopped local client cannot cancel it, and `--script` for an uncommitted probe. - test_support/microcosm_data/stored_inputs.py lists the four federal student loan inputs among the producer-bound register entries, next to the parent ids: no file examined for #1026 stores them, and test_us_stored_input_register.py binds them to their live producer. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec-engine identity values the education-debt stage moves (#1073) The scf_wealth education-debt declaration adds 145 configuration fields to the US bundle (141 authored, 4 resolved). Modal probe run 20261021T145336Z-b1a461f19 printed every value below from the code; none is derived by hand. It also diffed the field universe against origin/main: all 159 added and 14 removed pointers belong to exactly one claim, and no field is unclaimed or claimed twice. - field_usage: 42,329 configuration / 32,545 authored / 9,784 resolved (was 42,184 / 32,404 / 9,780); claim pins source_stages 1,845 (the two 2025 artifacts and six operations of scf_wealth), vintage_records 120 and resolved_vintage_authorities 67 (scf_2025), calibration 316 (the four person outputs in the QRF tail-concentration list), each with its observed digest; ledger mode and generation-0 effect counts in the field-usage and coverage-tool tests. - inventory_coverage: source_manifest, late_resource_semantics, tail and full_checkpoint digests; tail control fields 938 and resolved references 336. - The pool tool's bundle spec_sha256 (a6f254a8...). - experiments/modal_1073.py: the spec step first runs the bundle generator and the coverage attestation in --check mode. The coverage tool's pointer-inventory digest and the regenerated docs/evidence/spec-engine/us-f0-coverage.json follow in the next commit. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Pin the coverage pointer inventory and regenerate the F0 attestation (#1073) Modal probe run 20261021T150509Z-fcbd704c9, with the previous commit's field-usage and inventory pins in place, printed the coverage tool's pointer-inventory digest (37ce78d6...) and regenerated docs/evidence/spec-engine/us-f0-coverage.json with tools/spec_engine_coverage.py; --check then reported 42329/42329 configuration fields and 41/41 inventory checks. The bundle spec_sha256 stays a6f254a8.... Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-pin the inventory test's resolved-reference and tail-control counts (#1073) test_support/microcosm_build/spec_engine_inventory_coverage.py keeps its own copy of the reviewed inventory counts. Modal run 20261021T151444Z-05856e6ca reported the two the stage moves, matching inventory_coverage.py and probe run 20261021T145336Z-b1a461f19: resolved references 336 (the scf_2025 vintage) and tail control fields 938 (the four outputs in the QRF tail-concentration list). Everything else in that run passed. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the merged tree's bundle digest and F0 attestation (#1073) Modal run 20261021T183247Z-97bb3af92 regenerated the bundle mirrors on the merge commit (no change) and re-derived every identity value there: the field-usage counts and claim pins, the ledger counts, the inventory digests and counts, and the pointer-inventory digest all match the committed values. The only moved value is the bundle spec_sha256, now 90fa3260... (main's kernel changes plus this branch's stage), which the pool tool pins. tools/spec_engine_coverage.py regenerated the attestation and --check reported 42329/42329 configuration fields and 41/41 inventory checks. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Pin the #1073 fit receipt to the merged commit Reran build_1073_scf_education_debt_receipt.py on Modal at 2a6e65a (run 20261022T153020Z-2a6e65a73), after the merge with main brought in #1332's QRF change. Every fit figure is unchanged; only the receipt's commit, run and date move. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Describe #1073 as the student debt pilot in the changelog and module docstring Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Route loans for a child's education to co-resident adult children and swap X8000 codes on loans 4 and 6 (#1073) Parent PLUS is drawn for itself before the own shares, the IBR new-borrower share is labelled an upper bound, and the receipt benchmarks Parent PLUS and borrowers against Federal Student Aid. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Keep the education-debt stage operations within the sources schema (#1073) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values for the child-loan routing (#1073) Regenerated on Modal (run 20261023T143414Z-b93cc3ec0): sources.yaml, the field-usage counts and source_stages claim pin, three inventory digests, the coverage pointer-inventory digest, the bundle spec_sha256 and the F0 attestation. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rerun the #1073 receipts at the merged head and benchmark Parent PLUS against FSA Modal run 20261023T152015Z-60255dd98 at 60255dd (tests, spec and receipt steps all passed). Parent PLUS falls from 13.8% to 9.1% of SCF federal balances and from 16.8% to 10.7% on the host; FSA's Direct Parent PLUS share is 6.7%, a floor. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values on the merge with main (#1073) Regenerated on Modal (run 20261102T162227Z-3c4cfe441): sources.yaml, the field-usage counts (42,489 configuration fields, 32,704 authored and 9,785 resolved), the source_stages and calibration claim pins, four inventory digests (source_manifest, late_resource_semantics, tail, full_checkpoint), the resolved-reference and tail-control counts (337, 942), the coverage pointer-inventory digest, the calibration projection digest and the bundle spec_sha256. The source_stages.json byte digest is the merged file's. The F0 attestation is from run 20261102T163748Z-c012b92c9 (write, then --check and the generator's --check: 42,489/42,489 configuration fields, 41/41 inventory checks). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rerun the #1073 receipts on the merge with main Modal run 20261102T172355Z-3297aefd8 at 3297aef. No reported number moved: the fit JSON differs from the 60255dd receipt only in its provenance and in the last digit or two of 14 floats (floating-point summation order). Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values on the merge with main (#1073) #1444 changes the attested kernel us_runtime/housing_inputs.py. Running the documented sequence locally (repin --legacy-only, the bundle generator, the repinner, the coverage tool) moved: - seed_map and seed_protocol in inventory_coverage.py EXPECTED_HASHES, to main's values (07fe9086..., 4ff99232...), which the merge had already carried into the coverage evidence's map/protocol/implementation digests; - the bundle spec_sha256, be81ebbc... -> 7d2aaa4d..., in test_us_multispine_pool_tool.py and both spec_sha256 fields of docs/evidence/spec-engine/us-f0-coverage.json. Nothing else moved: source_stages.json, the bundle YAML, the field-usage counts (42,489 configuration fields, 32,704 authored, 9,785 resolved), the source_manifest, late_resource_semantics, tail, full_checkpoint and take_up digests, and the resolved-reference and tail-control counts (337, 942) are unchanged. The coverage tool reports 42489/42489 configuration fields and 41/41 inventory checks. The repinner's first pass builds its inventory report against the stale seed pins and wrote covered_item_count == 40 into test_spec_engine_coverage_tool.py; a second pass restored 41, so that file is unchanged here. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6Jmpqc2fSZAM219btF * Count #1073's module in the transport battery's binding identity (#1073) engine-free failed on the merge ref with test_the_batterys_binding_identity_ adds_only_the_leaf_and_its_imports: assert 221 == (197 + 23). The battery's binding closure reaches the us_runtime package, which imports us_runtime.scf_education_debt (us_runtime/__init__.py:786), so the merged tree holds one module more than main's base of 197. Diffing the closure on main (220) and on the merge (221) shows that module as the only addition. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LwQq6Jmpqc2fSZAM219btF * Hold graduate child loans and the dependent limit to the loan rules (#1073) A loan for a child's education that no listed child fits is Parent PLUS only when it financed undergraduate study (34 CFR 685.200(c)(2)(i)); a graduate one is now the head's own loan, as codes 6, 7 and -7 are. On the host, a child share drawn for a household without an adult child splits the same way: its graduate part joins the head's own balance, the rest is Parent PLUS. The dependent undergraduate aggregate limit follows the loan year: $17,250 before July 1, 1993, $23,000 from then (Pub. L. 102-325) and $31,000 from July 1, 2008 (Pub. L. 110-227), with 1993 and 2008 loans counted as on or after July 1. The docstring, stage notes and changelog say the over-the-limit rule leans toward Parent PLUS, and the receipt sizes a $57,500 reading. The loader checks local SCF files against the pinned member SHA-256s. The receipt counts the loans each rule moves, the X8000 swap by loan position, the host's moved child shares and the host's children-in-law. Citations: 20 U.S.C. 1087vv(d)(1), 34 CFR 685.209(b)(13)(ii), program 16. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rerun the #1073 receipts for the graduate-loan and dependent-limit rules Modal run 20261103T172225Z-78928589e at 7892858. The receipt's loans-moved table header is relabelled ("Rule") in the builder and in the committed note, which otherwise is the run's output. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Update the #1073 changelog figures to the new receipt Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values for the new stage notes (#1073) The scf_wealth stage notes changed in 7892858, so the documented repin sequence ran on Modal at that commit (run 20261103T172238Z-78928589e): repin --legacy-only, the bundle generator, the repinner (twice), the coverage tool, then each tool's --check, all passing. Moved: the frozen source_stages.json digest (de488b42...), sources.yaml, the source_manifest, late_resource_semantics and full_checkpoint inventory digests, the bundle spec_sha256 (4fd75d58...) and the F0 attestation. The field-usage counts, claim pins and inventory counts did not move. ruff format turns the repinner's single-quoted literals, and 0caf490's two seed pins, into double quotes; the values are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values on the merge with main (#1073) Ran the documented sequence locally on the merge with main 31c8939: repin --legacy-only, the bundle generator, the repinner, the coverage tool, then the repinner once more (the second pass changed nothing). Moved: - the field-usage counts: 42,394 configuration fields (was 42,489), 32,609 authored (was 32,704); compiler_semantic 27,673, front_end_validation 349, no_generation0_effect 3,614. Resolved bindings (9,785), legacy_behavior (14,269 / 38,780) and identity_only (103) did not move. The counts are the merge base's (42,305) plus this branch's 184 minus the 95 authored fields main dropped; - the coverage pointer-inventory digest (f6832e8d...); - the bundle spec_sha256 (ce8b1865...) in test_us_multispine_pool_tool.py and both spec_sha256 fields of us-f0-coverage.json; - the F0 attestation's documentation_sha256 and claim_count (48: main's #1484 retired the catalog unit waiver and its claim). Nothing else moved: source_stages.json and its frozen digest, sources.yaml, and the inventory_coverage.py digests are unchanged. The merge commit kept main's seed_map and seed_protocol, which the coverage tool accepts. The coverage tool reports 42394/42394 configuration fields and 41/41 inventory checks. ruff format turns the repinner's single-quoted literals into double quotes; the values are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Count the two modules #1342 adds to the transport bindings' identity (#1073) The merged head's binding identity holds 223 modules: 200 without the leaf's 23. This branch added us_runtime.scf_education_debt (198, b1218b0), and main's #1342 adds microcosm.calibrate.hierarchy_labels and microcosm.diagnostics.catalogs, which the bindings reach through microcosm.calibrate's package __init__. Main fails the same assertion at 31c8939 (222 == 197 + 23); #1495, still open, counts them there. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> * Sum an only child's loans against the dependent limit, and pin the boundaries (#1073) Review of 2026-11-03 (second round): - The dependent undergraduate limit caps a student's total. When exactly one listed child fits the family's federal undergraduate loans for a child, those loans are read year by year, oldest first: a year's loans within the limit on their own are the parent's once the child's total through that year would pass that year's limit, and the parent's loans do not count toward it. With two or more fitting children each loan is still compared with the limit alone, which leans toward the child; the docstring, stage notes and changelog say so, and the receipt counts those loans. _DEPENDENT_LIMIT_SUMS_AN_ONLY_CHILDS_LOANS switches the build reading off for the receipt's per-loan reading, and _scf_education_loan_holders returns what the rule read (_LoanHolders). - Undergraduate means programs 10-12 and 16 (_UNDERGRADUATE_PROGRAM_CODES, checked against the 2025 codebook, which lists 10-16 and 0), so a loan with any other code is never parents-only. - The moved-dollar columns compute part / sums * total (_part_of_drawn_total), so a subnormal share sum gives no NaN. - The $46,000 pre-July-2008 limit for a dependent student whose parent cannot get PLUS joins the docstring, notes and changelog. Tests: routing cases on the age-16 boundary (code 25, 2014, $9,000) and the age-24 boundary (code 30, 2021, $40,000), and an unlisted program code; test_the_dependent_limit_sums_an_only_fitting_childs_loans (two $20,000 2016 loans against one $40,000 loan, oldest-first years, two fitting children, the per-loan reading); the split test now draws shares that sum to 0.6 and a non-new child loan for a new head, and asserts the head's flag comes out False; a direct test of the subnormal guard. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Count the summed-limit and pre-2008 loans in the #1073 receipt - child_education_loan_moves counts and sizes the code-3 undergraduate loans, each within its year's limit, that the child's total through their year cannot take, by holder under the build reading and under a new per-loan reading (per_loan_limit, also in the readings table), and the code-3 undergraduate loans borrowed before 2008 that fit a listed child who could still have been dependent, by where they fall against their year's limit and $31,000. - The reading for a parent who cannot get PLUS uses $46,000 before July 2008 and $57,500 from then (limit_when_a_parent_cannot_get_plus). - host_children_in_law also counts a PECOHAB partner of an adult child. - The host draw check compares the four balance columns, placed on the build's people, as well as the weighted total. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Rerun the #1073 receipts for the summed dependent limit Modal run 20261104T190152Z-63776f2ce at 63776f2. Summing an only fitting child's loans moves 27 pooled code-3 loans (5 in implicate 1; $3.79B, 0.38% of SCF federal balances) to Parent PLUS: the SCF's Parent PLUS share goes from 8.99% to 9.36%. Of the 49 loans each within its year's limit but over the child's remaining room, 22 stay the child's in families with two or more fitting children. No code-3 undergraduate loan before 2008 fits a dependent-age child. The refit second QRF puts the host's Parent PLUS share at 10.7% (was 9.7%) and the hold-out's at 6.8% (was 6.5%); household totals are unchanged. The repeated host draw matches the build in its total and in all four balance columns. With PECOHAB partners, 0.97% of host households have an adult child's spouse or partner present. The changelog's figures follow the receipt. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values for the summed-limit stage notes (#1073) The scf_wealth stage notes changed in b4f9260, so the documented repin sequence ran on Modal at 63776f2 (run 20261104T190206Z-63776f2ce): repin --legacy-only, the bundle generator, the repinner (twice), the coverage tool, ruff format on the changed files, then each tool's --check, all passing (42,394/42,394 configuration fields, 41/41 inventory checks). Moved: the frozen source_stages.json digest (098c90d5...), sources.yaml, the source_manifest, late_resource_semantics and full_checkpoint inventory digests, the bundle spec_sha256 (cd762197...) and the F0 attestation. The field-usage counts, claim pins and inventory counts did not move. ruff format turns the seed_map and seed_protocol pins' single quotes into double quotes; the values are unchanged. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Pin the summing rule's parts and note the Perkins lean (#1073) Third review of #1246. test_the_summing_rule_for_an_only_fitting_child pins nine parts of the summed dependent limit (oldest first, the parent's years out of the total, every kept year in it, a loan over the limit out of its year's group, a loan at the limit counted, private loans out, pooled loans deciding how many children fit, and the 1993-2007 and pre-1993 limits), and test_over_the_total_skips_a_year_the_child_could_not_be_dependent pins the dependency check in the running comparison. Both are the reviewer's. The module docstring's within-limit line gains the exception for an only fitting child whose total through that year exceeds that year's limit, and its lean bullet, the stage notes and the changelog note that the SCF lists Perkins loans among federal student loans while 34 CFR 685.203(e) caps only Direct, Stafford and SLS loans. The receipt builder's docstring says federal for the pre-2008 count and describes over_the_total as the family's running total. The stage notes' spec identity values are re-derived after the merge with main that follows. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> * Re-derive the spec identity values after the merge with main (#1073) The documented sequence in tools/repin_us_spec_engine_contracts.py, run on Modal (20261105T212737Z-525506fef): repin --legacy-only, the bundle generator, the repinner twice, the coverage tool, ruff format, then every tool's --check. What moved: - the frozen source_stages.json SHA-256 (the stage notes' Perkins line): sources.yaml, the generator, the test-support bundle and the core contract test; - the inventory digests full_checkpoint, late_resource_semantics and source_manifest; seed_map and seed_protocol keep main's values, requoted; - the bundle spec_sha256 in the multispine pool test; - the F0 coverage attestation (42,394/42,394 fields, 41/41 checks). The field counts and claim pins did not move. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Nov 6, 2026
Populace US means to differ from Census where it fixes CPS errors, so the note now measures differences from Table 18 and no longer treats them as misses: - The title and opening say so, and name the question: separating intended corrections from noise and bugs. "Systematic error" becomes a systematic shift, and "state-specific error" becomes state-specific variation. - "What would fix it" becomes "What we would change", and says a smaller gap alone does not make a better file. - The rent sections now record what landed. #1444 fixed the assignment, #1529 made the gate fail on rent outside renter households (the test the note named no longer exists), and no published file carries the fix yet. PolicyEngine/child-poverty-impact-dashboard#127 has left draft. No figure changes: a token comparison of the old and new text finds only punctuation differences. ACS-local figures stay on hold for the fill-0 rerun. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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.
Places each ACS rent draw on its own household's head. This changes no published data: rent changes only when a base is rebuilt, and that rebuild is a release decision (d2300).
The bug
impute_us_pre_subsidy_rentdraws rent in household-table order (_recipient_head_featuresreturnsall_features.reindex(household_ids)) and writes it withperson_rent[head_mask] = predicted, which takes person order. The two orders agree only when heads sit in ascending household id.Evidence
On the dense file
populace-us-2024-b5db384-8b0c1950b65f-20261024T083103Z, 168,852 base households:Only 9.15% of households sit at the same position in both orders. The default file and ACS-local donors carry the same draws. ACS rows get rent through the donor transfer, so they learned from misplaced rent and need a rebuild, not just these draws.
Impact
PolicyEngine US 2.90.2, 51 states, published weights held fixed. A rebuild can move these further, because calibration fit those weights to misplaced rent.
The fix
Match each draw to its head by household id.
housing_inputs.pyis an attested kernel, so the PR also repinsseed_map,seed_protocolandspec_sha256with the sequence intools/repin_us_spec_engine_contracts.py, and regeneratesdocs/evidence/spec-engine/us-f0-coverage.json.tools/generate_us_bundle_from_constants.pyneeds policyengine-us 2.2.1, which this checkout lacks, so CI's engine jobs check that step.Invariants
For every person order and household size:
test_each_head_holds_its_households_draw_under_any_person_orderstates these with Hypothesis, andtest_rent_reaches_its_own_household_when_person_order_differschecks placement by tenure throughwith_us_housing_inputs. Both fail on the old assignment.Not in this PR
Frames that already carry rent keep it, because
with_us_housing_inputsskips re-imputation when the signal gate passes, so the rebuild must start from rent-free frames.us_housing_inputs_signal_gatestill ignorespositive_rent_nonrenter; tightening it would block fiscal refresh on older bases, so it lands with the rebuild. Steps 1 and 2 of #1446 cover both.Diagnosis: #1445.
axiom: n/a: data build imputation, no policy logic.
🤖 Generated with Claude Code