Repository navigation
Handle the capital-gains tail clone in release stages on a PUF-support base - #992
Merged
Merged
Conversation
A PUF-support base built without a selection source carries the capital-gains own-tail copy (clone index 2) as a second PUF-role row of its source unit. Stages that identified copies by (source ID, role) refused it as a duplicate. Clone provenance now ranks copies uniformly on assembled and historical frames; only channel-only fixtures keep the two-role ranking. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Adds a fixture that builds clone-index-2 own-tail copies with the production tail clone operator, then shows on non-assembled frames that Head Start and voluntary filing accept the copy, predict once per source unit from the lowest surviving copy rank and fan the decision to every copy; that the prior-year availability and SSI disability clone checks compare the tail copy with its source; and that a genuinely repeated (source, clone index) pair still fails closed. Also corrects the voluntary-filing receiver comment: PUF-role copies carry PUF-imputed wages, so a surviving PUF copy does not predict identically to the native copy; it supplies the unit's predictors. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
September 23, 2026 12:35
MaxGhenis
added a commit
that referenced
this pull request
Sep 23, 2026
Record the per-target chain-prefix rule, the maintained allocation and its three stricter arm-zero refusals, the explicit numerical policy, the host option and declarations, and the native clone-index audit against the (source ID, role) keying fixed on main by #992. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
voluntary_filing, sipp_head_start and ssi_disability_criteria are seed sites, so the seed protocol's implementation digest and the compiled seed map digest move with their source, and CI's engine-shared job refused #992 merged with main (test_us_inventory_is_structure_exact_and_complete, test_semantic_hash_has_golden_vector_and_surface_separation and the coverage tool's committed-report checks). Observed at this head and written verbatim, never hand-derived, as #959 did: - inventory_coverage EXPECTED_HASHES: seed_protocol 56f832aa…, seed_map 18594e59… - the minimal-loader golden spec_sha256 db3fdab1… - docs/evidence/spec-engine/us-f0-coverage.json regenerated by tools/spec_engine_coverage.py (42,180/42,180 fields, 41/41 inventory checks; --check green) Spec-engine and touched-stage suites pass on this merge of origin/main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Sep 23, 2026
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The constants adapter test pinned the bundle spec_sha256 from before the clone2 seed-site edits. tools/generate_us_bundle_from_constants.py --check now reports 3667e30e..., which is the value the adapter produces live. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
added a commit
that referenced
this pull request
Sep 23, 2026
MaxGhenis
added a commit
that referenced
this pull request
Sep 24, 2026
REVIEW-1: the with_tail fixture copied whole clone-one rows, so a tail copy's candidate cells equalled its twin's and the tail == incumbent assertions could not catch a carry keyed on source ID and role instead of the clone index (the #992 failure mode). Tail copies now carry producer-like capital-gains values, distinct floats and flipped booleans; the existing assertions stay. New tests exercise the TAIL_CARRIED refusal (a carried tail cell tampered after the carry) and the ARM_ZERO_AXIS guard. REVIEW-1's twin-fill mutant now fails 3 tail tests (it passed all 10 before); 177 finalization, placement and host tests pass. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gate peer finding (P2): support_copy_rank_series cast clone indices with a raw NumPy cast, so an assembled frame with indices [0, inf] passed Head Start canonical selection (inf became INT64_MAX) where the base implementation raised. The same weakness sat in four sibling readers: validate_assembly_provenance and spine_provenance_counts accepted +inf and values past int64, support_role_series accepted +inf, 1e20 and 2**63, and puf_tax_detail_clone_mask silently marked +inf rows as not PUF-detail. Every clone-index conversion in support_provenance now goes through _validated_clone_indices, which refuses non-finite, negative, fractional or out-of-range values before any cast, and compares integer dtypes exactly so values above 2**53 are not rounded. Valid encodings (int64, float64, nullable Int64, uint64, numeric strings) give identical roles, ranks, masks and receipt counts. 95 new cases (inf, -inf, NaN, 1.5, negative int and float, 2**63, string inf, <NA>) across the assembled and historical paths fail on the old code; spec-engine pins and the bundle sha are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Gate peer finding (P2): support_copy_rank_series fell back to role ranks whenever the clone-index column was missing, so an assembled table (raw spine source IDs present) that had lost its clone indices passed Head Start canonical selection and voluntary filing, where the base raised. Assembled channels name physical sources, not copies, so without clone indices a surviving row cannot be told native from donor copy. The role-rank fallback now applies only to channel-only historical frames; assembled frames raise. Regression tests cover both consumers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…entry A differential harness (350 cases x 10 entry points, base d9a248d vs head) found 141 regressions where #992's head accepted assembled tables (spine id present) missing their channel or clone-index column that the base refused: Head Start's early no-metadata return, voluntary-filing and prior-year occurrence pairing, and the SSI divergence diagnostic. One validator in support_provenance now refuses such tables, and a raw spine id counts as support metadata even when both role columns are missing, so every role reader validates before any fallback. Head Start and voluntary filing validate at entry; prior-year and SSI route through the shared detector without changing their attested bytes (bundle sha 3667e30e unchanged). Harness after: 0 regressions; the 35 intended divergences are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Historical support tables (no raw spine ID) may carry only the clone indices the producers write: 0, 1 (PUF tax detail) and 2 (capital-gains own tail, now PUF_CAPITAL_GAINS_TAIL_CLONE_INDEX). An index of 3, 7 or 2**62 on a repeated source was refused as a duplicated same-role row before #992's ranking and was accepted as a separate copy after it. Assembled tables keep the full nonnegative int64 domain. - An assembled table must carry a complete, non-null <entity>_source_id, which spine assembly writes for every entity. The prior-year gate, which reaches the shared validator through support_role_series, now refuses a table whose missing source IDs would drop copies out of its clone comparison. - Voluntary filing: with no support metadata, a repeated or missing tax_unit_source_id flags clone_metadata_missing, matching the receiver. A channel-only table that repeats a (source, role) pair is refused, and every copy of a source is compared rather than paired by occurrence. - The SPM universe reader validates assembled provenance before its native-copy fallback and reads clone indices through the shared int64 validator. The attested prior_year_income.py and ssi_disability_criteria.py bytes and the US bundle digest 3667e30e are unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Real-data check of round 8 (8c59fe2) on the Route A base H5 from run 8f63bf0 (
|
Round 8 requires every assembled table (raw spine ID present) to carry a complete <entity>_source_id, which spine assembly writes for every entity and stacked_spine already requires. Three hand-built stacked fixtures carried the spine ID, channel and clone index but no person_source_id, so their consumers refused them before the assertions under test. They now carry the assembly-unique source ID, shared by a source's clones. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Sep 25, 2026
MaxGhenis
added a commit
that referenced
this pull request
Sep 26, 2026
Brings in the 19 main merges since 6442030: the Route A remediation stack (#1016, #1018, #1017, #1025, #1024, #1028), #1029, #1005, #1008, #1004, #992, #1031, #1015, #1033, #994, #954, #966, #1006 and #1010. Eight conflicted paths, each recorded in docs/native-integration-20260923.md: - test.yml keeps the native sharded matrix (main's --durations=25 is already in every native pytest call); - the us_runtime facade stays lazy and gains main's three fiscal-target exclusion exports (the parent union, 920 names; the facade-union test is re-pinned to that union's digest); - reform_validation.py keeps main's _released_engine_state and the native explicit-constructor default_simulate_factory; - build_us_fiscal_refresh_release.py carries the native explicit consumer seams (formula metadata, dataset and microsimulation constructors, SPM selection) onto main's household-batched post-export scorer and batched base materialization; omitted seams keep main's exact calls, and _main supplies none. Three native test files that addressed the removed frame-based factory now address the scorer with the same assertions; - identity pins observed on the merged tree: five EXPECTED_HASHES entries, the regenerated F0 coverage report (42,239/42,239 fields, 41/41 checks), the US spec identity 2dfa51b8... and the loader golden f2047cb9... tools/generate_us_bundle_from_constants.py --check, tools/ci_test_groups.py --verify, the CI matrix contract and ruff pass. The fiscal consumer, formula-metadata, shared target/solve and calibration-attachment files pass; the rest of the fiscal battery and the known pre-existing failures are follow-up work on this branch. No actual-data native run, release certification or publication. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
hua7450
pushed a commit
to hua7450/microcosm
that referenced
this pull request
Sep 28, 2026
PR-4 (post-export scoring) and PR-5 (batched target materialization) each defined the same four-variable population-aggregate list and a walker over an engine's known periods. They now share US_POPULATION_AGGREGATE_VARIABLES and _engine_known_periods. The shared walker is the superset: live branches, the branches target materialization records before they are detached, and a reform= engine's baseline. policyengine-core sets baseline only when reform= is passed (simulation.py:247-253), and the post-export scorer passes the reform's tax_benefit_system alone, so post-export scoring behaves exactly as before. Both list-pinning scans are kept; they use different markers. Tests: - The known-period walker returns exactly what an independent breadth-first walk over random engine graphs (cycles allowed) finds. Dropping any of its three links fails the test. - Both refusal paths see a value held only on baseline. The docs now say an assembled table needs a complete, non-null <entity>_source_id (PolicyEngine#992 round 8) and name the shared list. 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.
Part of #956 (route A:
tools/build_us_puf_support_base.py→tools/build_us_fiscal_refresh_release.py --base-h5, no selection source). Stacked on #959 (us-spm-role-stage). Route A will build from a commit that merges this branch and #991 (us-asec-720-coverage-recodes); this PR does not touch #991's files.The failure
Route A's full-scale base (
base_populace_us_2024_puf_support.h5, sha256cb1bd1e6…, built at #959 head47976be6c) keeps every support copy, because no selection source prunes it. Read directly from the H5's person table:asecpuf_tax_detailpuf_tax_detailClone index 2 is the capital-gains own-tail copy (
us_runtime/puf_capital_gains_tail.py,_clone_and_transfer). It copies a primary PUF-detail household, shifts its IDs, setsclone_index = 2, and keepsperson_source_idand thepuf_tax_detailchannel. Each of the 42,336 tail persons has a clone-0 and a clone-1 twin.(person_source_id, clone_index)is unique;(person_source_id, role)repeats 42,336 times. The base carries noperson_spine_source_id, sohas_assembled_support_metadatais False, and release stages took their non-assembled branch. That branch identified a copy by (source ID, role). A diagnostic release (only the #720 gate waived) failed 199 s in:Voluntary filing has the same refusal and would have failed next.
Design decision
What the tail copy is. The tail transfer's receipt names its method
selective_post_qrf_household_weight_split. Clone 2 takes part of a clone-1 household's weight. The transfer writes onto it only the capital-gains vector, new IDs and tail-provenance columns. The base builder then runs its post-clone stages (weeks_unemployed_post_clone,childcare_post_clone, and others) aftertransfer_puf_capital_gains_tailinSTAGE_BOUNDARIES. They re-impute every PUF-role row, the tail copy included, row by row. The semantics follow:support_role_seriesalready maps any nonzero clone index to the PUF role. Row-level stochastic imputations give it its own draw, exactly as the base's own post-transfer stages and assembled pools do. They are not forced equal to the twin.Scope: the stages, not the base. The alternative was to make the base look assembled by giving it
person_spine_source_id. That is wrong. On an assembled frame,support_gate_source_channel_seriesreads the channel column as the physical source, sopuf_tax_detailclone-1 rows would be treated as a non-ASEC spine. That would change the ASEC-source masks in Medicare, retirement, weeks-unemployed and alimony. It would also require an assembly manifest this base does not have, and it would change base bytes and force a rebuild. The stage rule is smaller and uniform. A new provenance-owner helper,support_copy_rank_series, returns the validated clone index whenever the clone-index column is present, assembled or not. Only channel-only fixtures, which cannot express a tail copy, keep the historical two-role ranking (ASEC 0, PUF 1). Each affected stage now keys and orders copies by (source ID, copy rank). The helper sits besidesupport_role_seriesand inherits its validation (clone index must be a nonnegative integer that agrees with the historical channel), so malformed metadata still fails closed.Per-stage audit
Every stage
_mainruns on a--base-h5PUF-support base before calibration was traced from the release tool to its module. Each module was searched forsupport_role_series,has_assembled_support_metadata, clone-index and source-ID use,duplicated(,drop_duplicates(andcumcount. Modules with hits were read.with_us_sipp_head_start_input)test_historical_tail_copy_fans_the_source_decision_to_every_copy,test_historical_puf_only_survivor_predicts_from_the_primary_detail_copy,test_historical_duplicate_clone_index_still_fails_closed; channel-only refusaltest_imputer_rejects_identical_duplicate_same_role_source_rows(message updated)with_us_voluntary_filing_input)test_historical_tail_copy_fans_the_source_decision_to_every_copy,test_historical_puf_only_survivor_predicts_from_the_primary_detail_copy,test_historical_duplicate_clone_index_still_fails_closed; channel-only refusaltest_duplicate_same_role_source_rows_fail_closed(message updated)us_prior_year_income_signal_gate; the base runs it too)test_clone_availability_checks_the_historical_tail_copy. Basecb1bd1e6: 0 availability mismatches under both groupings, computed from the H5test_historical_tail_copy_predicts_in_the_puf_role_stream,test_historical_tail_copy_divergence_joins_its_source_groupwith_us_ssi_take_up, reporter lineage)person_source_idacross all copiessupport_role_series(clone 2 has the PUF role); no copy-uniqueness assumptionThe post-calibration modules the release uses (
l0_refit_export,exact_k_ladder,release_input_coverage,release_target_parity,reform_coverage_smoke,register_consistency,input_mass,nonzero_shares) contain no support-provenance references. The release-gate battery re-runs the summaries fixed above.Real-data evidence
Throwaway local branch (never pushed): this PR plus the #720 gate waiver
4494fa249cherry-picked. Run undersupervise.pywith the diagnostic config's limits, on basecb1bd1e6, with the exact diagnostic argv (own--out/--checkpoint-root, release IDpopulace-us-2024-DIAGNOSTIC-clone2-…).1fbc4d962; artifacts under_recovered/scratch-backup/893/overnight-20260923/clone2-fix/iter1/) got past SIPP Head Start, where the old run died at 199 s. It then passed SSI take-up, SCF auto loans, SIPP vehicles, voluntary filing, SIPP tips, ORG wages, ACA, Medicaid and SNAP eligibility and other health insurance; no earlier gate failed. It failed at 1,742 s wall, peak RSS 64.8 GB (60.4 GiB), at the pre-solve degenerate-input gate. The message was "strike_benefits: every observed value equals the engine default (0)". This is not a clone issue. In the base, rawOI_OFFnever equals 12 in any source year (codes seen: 0 to 10, 13, 19, 20), andalimony.pyderivesstrike_benefitsasOI_VALwhereOI_OFF == 12. The main session has since settled it: the ASEC dictionary definesOI_OFF12 as strike benefits, so the mapping is correct and the column is genuinely zero. The reviewed exclusion lives onus-strike-benefits-reviewed-exclusion, not in this PR.700bf0423. That head merges Read the ASEC at-interview coverage recodes the 2022/2023 inputs lack from the pinned Census archives (#720) #991, this PR at888c53cefand the strike_benefits exclusion, and runs without the Pooled ASEC 2022/2023 vintages lack the NOW_* coverage recodes: reported Medicaid at interview thins to 24.6M vs ~58M survey under 65 on the certified artifact #720 waiver. No second diagnostic iteration was run from this branch.Provenance hardening (review rounds 6 to 8)
Review found that copy ranking could accept assembled or malformed support tables that the base refused. A base-vs-head differential harness now runs every entry point this PR touches over 416 synthetic tables: each missing-column combination, clone indices 0/1/2, 3, 7, 262 and 263, fractional, negative and NaN indices, and missing or NaN source IDs. Round 8's final run:
Frameobjects with synthetic records (14 historical and 36 assembled cases, 10 entry points each) matches round 7 exactly and refuses nothing.What rounds 6 to 8 added:
<entity>_source_id, or with a null source IDrequire_assembled_support_provenanceat the entry of every consumer this PR touches, and of the SPM universe reader, before any channel-only or one-row-per-source fallback. Spine assembly writes all four columns for every entity.puf_support, 1 bypuf_support, 2 by the capital-gains tail (PUF_CAPITAL_GAINS_TAIL_CLONE_INDEX). Assembled tables keep the full nonnegative int64 domain, as before._validated_clone_indices).tax_unit_source_idclone_metadata_missing, matching the receiver's refusal.The attested
prior_year_income.pyandssi_disability_criteria.pybytes, and the US bundle digest3667e30e…, are unchanged. Their paths close through the shared readers they already call: the prior-year gate refuses a missing source ID throughsupport_role_series.Deferred. These need new seed receipts, because the spec engine's source inventory hashes these modules and editing them moves the bundle digest:
puf_capital_gains_tail(the same value, 2).acs_transfer._pregnancy_source_groups(pre-existing and outside this PR's consumers).derive_us_prior_year_income_from_manifest. Its stage path is already closed, because the next operation reads roles through the validator.Not covered
strike_benefitsdegenerate-input stop is handled outside this PR (see above). The throwaway run waived it locally only.ruff format --checkstill flags pre-existing formatting in several touched files (unchanged lines). CI runsruff checkonly, which passes.Verification
uv run pyteston the touched and neighbouring suites: Head Start, voluntary filing, prior-year income, SSI disability, multispine PUF clone, spine blindness, capital-gains tail, SSI take-up, WIC, weeks unemployed, fiscal-refresh builder, fiscal-refresh checkpoint inspector, PUF-support base builder, stacked spine, multispine pool, pool input consumers, take-up contract, eligibility, Early Head Start exclusion, release input coverage, spine assembly and agreement, late-producer DAG. All pass.517d9a407. The SSI disability imputation test also passes there: it pins behaviour that was already right. The copy-rank helper test imports a function the base commit does not have.uv run ruff check .;uv run python tools/ci_test_groups.py --verify;uv run python tools/graph_acceptance_burndown.py --verify: all ok.tests/us_tail_clone_fixtures.pybuilds the clone-2 copy with the production_clone_and_transfer, not a hand-written imitation.Producer-hash note.
support_provenance.pyis a hashed producer file for the ACS donor receipt qualification. The committed 2026-09-22 receipt pins its origin/main bytes, and this PR changes the file. Valid inputs produce identical outputs, but that receipt lineage must be re-qualified after merge (tools/build_us_acs_donor_receipt_qualification.py) or certified from a pre-#992 checkout.🤖 Generated with Claude Code