From 4eb31d10126e5508bda41e210050fb060cf5ae60 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:18:29 -0400 Subject: [PATCH 01/21] Apply d637 amendment 2 to the U2 source registries Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written. This is its registry commit, the first of the d637 commits section 16a's "Registry effect of the rulings" requires. - income.json: the ten 2015-2023 family-file spouse age and sex records (income..wife_age, wife_sex) are RESOLVED as slot metadata only (usage slot_metadata_only), with a no-slot-inference rule in use_restriction quoting each record's own inapplicability wording. Each keeps its adjudication record (A), gains a resolution and a ruling (d637, 2026-09-30, 16b, amendment 2), and keeps the answered question as superseded_question. A d637 rulings_applied record lists the amendments applied here. - u2_source_registry: RATIFIED_AMENDMENTS pins each applied amendment's registry, entries, ruling, required gate fields and removed fields; the validator holds a d637 ruling to that scope and requires every scoped entry to record it. - sources: the income byte pin moves; two docstrings say that the family file's spouse age only gives a slot flag that income_rows holds equal to the roster's code-20 or code-22 occupant. - invented: a new named variant, appended last, codes the spouse age 0 ("no Spouse/Partner in FU") beside a roster occupant; the income rows refuse, under both role contexts. - The loader's source preflight now refuses on 69 blockers (74 before): code 88 12 (d745), 5b 11, 5a 44, amendment 6 2. - Master record: 1778 RESOLVED, 0 REFUSED, 14 TO VERIFY; the spouse age/sex blocker row, a d637 section and the ten rows. Specification section 17 item 2 and a section 19 block record it; the spec is re-pinned. Exposure is appended to u2_m1b_exposure.md. Tests move deliberately: the milestone-1b and d1090 projection tests now read the d1090 registries from Git (commit 07916bb) instead of the working tree, and a new cumulative differential pins every gate field the d637 commits move against them. New tests check the ruled entries, the scope (twelve mutations, each refused), the amendment-2 rule text and the builder's refusal. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 159 ++++++++++++---- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 8 +- docs/design/u2_m1_source_adjudication.md | 38 ++-- docs/design/u2_m1b_exposure.md | 27 +++ .../data/u2_source_registry.py | 88 +++++++++ .../uniform_cut_track_u2/invented.py | 27 +++ .../uniform_cut_track_u2/loader.py | 16 +- .../uniform_cut_track_u2/sources.py | 27 ++- tests/README-tiers.md | 8 +- tests/tier_counts.json | 6 +- tests/track_u2/test_adjudication_applied.py | 127 ++++++++++++- tests/track_u2/test_source_registries.py | 115 ++++++++++++ tests/track_u2/test_u2_mappings.py | 169 ++++++++++++++---- tests/track_u2/test_u2_roles.py | 34 ++++ tests/track_u2/test_u2_scripts.py | 4 + 16 files changed, 749 insertions(+), 106 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index b34cc8ae6..e8407b703 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -4568,7 +4568,6 @@ "wave": 2015, "route": "wife_age", "variable": "ER60019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2015/FAM2015ER.sps", @@ -4600,13 +4599,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2015, how do the family-file spouse age/sex universe and spouse income slot treat individual code 90 and a male code 20? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 68).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 68, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2015, how do the family-file spouse age/sex universe and spouse income slot treat individual code 90 and a male code 20? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 68)." }, { "id": "income.2015.n_children", @@ -7490,7 +7499,6 @@ "wave": 2015, "route": "wife_sex", "variable": "ER60020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2015/FAM2015ER.sps", @@ -7522,13 +7530,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2015, how do the family-file spouse age/sex universe and spouse income slot treat individual code 90 and a male code 20? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 69).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 69, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2015, how do the family-file spouse age/sex universe and spouse income slot treat individual code 90 and a male code 20? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 69)." }, { "id": "income.2015.head_farm_acc", @@ -9138,7 +9156,6 @@ "wave": 2017, "route": "wife_age", "variable": "ER66019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2017/FAM2017ER.sps", @@ -9170,13 +9187,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2017, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 71).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 71, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2017, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 71)." }, { "id": "income.2017.n_children", @@ -12076,7 +12103,6 @@ "wave": 2017, "route": "wife_sex", "variable": "ER66020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2017/FAM2017ER.sps", @@ -12108,13 +12134,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2017, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 72).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 72, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2017, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 72)." }, { "id": "income.2017.head_farm_acc", @@ -13716,7 +13752,6 @@ "wave": 2019, "route": "wife_age", "variable": "ER72019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2019/FAM2019ER.sps", @@ -13748,13 +13783,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2019, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 74).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 74, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2019, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 74)." }, { "id": "income.2019.n_children", @@ -16646,7 +16691,6 @@ "wave": 2019, "route": "wife_sex", "variable": "ER72020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2019/FAM2019ER.sps", @@ -16678,13 +16722,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2019, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 75).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 75, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2019, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 75)." }, { "id": "income.2019.head_farm_acc", @@ -18296,7 +18350,6 @@ "wave": 2021, "route": "wife_age", "variable": "ER78019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2021/FAM2021ER.sps", @@ -18328,13 +18381,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2021, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 77).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 77, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2021, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 77)." }, { "id": "income.2021.n_children", @@ -21296,7 +21359,6 @@ "wave": 2021, "route": "wife_sex", "variable": "ER78020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2021/FAM2021ER.sps", @@ -21333,13 +21395,23 @@ "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", "format_text": "1 'Male'\r\n 2 'Female'\r\n 0 'Inap.: Reference Person is single male or female; no Spouse/Partner in FU (ER79524=5)'", - "question": "In 2021, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 78).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 78, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2021, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 78)." }, { "id": "income.2021.head_farm_acc", @@ -23091,7 +23163,6 @@ "wave": 2023, "route": "wife_age", "variable": "ER82020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2023/FAM2023ER.sps", @@ -23123,13 +23194,23 @@ "negative_domain_documented": false, "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", - "question": "In 2023, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 80).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 80, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2023, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 80)." }, { "id": "income.2023.n_children", @@ -26091,7 +26172,6 @@ "wave": 2023, "route": "wife_sex", "variable": "ER82021", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2023/FAM2023ER.sps", @@ -26128,13 +26208,23 @@ "all_missing_assigned_statement_present": false, "verification_scope": "Exact setup label, position/width, printed codebook definition and domain; not empirical validation or individual relationship routing", "format_text": "1 'Male'\r\n 2 'Female'\r\n 0 'Inap.: Reference Person is single male or female; no Spouse/Partner in FU (ER83493=5)'", - "question": "In 2023, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 81).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 81, "disposition": "A", "amendment": 2 - } + }, + "status": "RESOLVED", + "usage": "slot_metadata_only", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "2" + }, + "superseded_question": "In 2023, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 81)." }, { "id": "income.2023.head_farm_acc", @@ -27885,6 +27975,15 @@ "date": "2026-10-10", "specification_section": "16c", "note": "Max's ruling d1090 (2026-10-10; specification section 16c) on blockers B1 and B2: the seven role refusals and pension:2015.route.respondent_slots are replaced by the ratified family-unit exclusions (B1, section 16a option 1d-2, amending draft-3 line 160; B2, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019-2023), and the dependencies on the seven role entries are released. Each entry keeps its adjudication record (disposition F) and its superseded refusal text." + }, + { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendments": [ + "2" + ], + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 2 resolves the ten 2015-2023 family-file spouse age and sex records as slot metadata only, with a no-slot-inference rule. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index c26f34da9..619fa46d8 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "a97b66d1bea9eaf06a105153a411ee2adc301693bfe3332ebf698d07fe0a23bf", + "sha256": "9ec1bd0e8b2f3552facb5553323c26f2060cfad1c88543dba44a7f508f89f0c3", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 6a2b96fc0..e8f15337c 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. Until the registry commits for d637 (and, for code 88, d745) land, the 24 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata. Until the rest of them (and, for code 88, d745) land, the 14 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1837,6 +1837,12 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - After the second round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261010-070559-d1090-review-a2` and `20261010-070559-d1090-review-bc`, both REQUEST CHANGES; same folder): option 1e-1 does not specify B2's counting unit, so §16c records the person count only as a proposal pending Max's confirmation (d1242), states that the record ratifies no further counting requirement, and §17 item 8 cites the counts the options specify. The builder computes B1's person count only. The record test now checks the complete operative paragraphs and lines. The registry validator holds each exclusion to its exact ratified scope. New invented variants test that earlier dispositions keep precedence and that a code-88 refusal inside an excluded unit still refuses. The master record's per-route rows now state each entry's remaining dependencies (537 sentences removed, 16 corrected). - After the third-round review (GPT-6.1 Sol, Subfleet job `20261010-075932-d1090-review-r3`, REQUEST CHANGES, two should-fix findings; same folder): the record test pins every line of §16c and §20's status line, so no wording in them can change without the test changing, and 33 mutations each fail it. The registry validator requires every role entry's id to name its own wave and code, and an exclusion to sit on a present entry, so a rule cannot be moved or dropped under an unchanged id. +**`u2-draft-4`, Max's d637 rulings applied to the registries (from 2026-10-12):** + +- Each ratified amendment's registry effect (§16a, "Registry effect of the rulings") lands in its own reviewed commit. `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS`) pins each applied amendment's entries, ruling and gate fields, and `tests/track_u2/test_u2_mappings.py` checks every gate field these commits move against the registries the d1090 commit left, read from Git. Amendment 1's parts 1a–1c, and with them the six code-88 records, wait for Max's confirmation (d745). No line before §16a and no registry-cited line changes. +- Amendment 2: the ten 2015–2023 family-file spouse age and sex records (`income..wife_age` and `wife_sex`) are RESOLVED as slot metadata only. Under their no-slot-inference rule, the variables and their 'Head is female or single male' or 'Reference Person is female or single male' wording never decide whether a person occupies the spouse slot; the in-family code-20 or code-22 person does, and annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age. The builder reads the spouse age only for the family file's slot flag, which the income rows must find equal to the roster's occupant or they refuse; a new invented variant covers the direction this amendment concerns, a family-file 'no spouse' code beside a roster occupant. The loader's source preflight now refuses on 69 blockers, down from 74. The master record counts 1778 RESOLVED, 0 REFUSED and 14 TO VERIFY records. +- No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. + ## 20. Ratification and execution record **Pending.** diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index b0c8acc87..f13e05901 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,11 +2,11 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2026-09-28) to apply the independent adjudication `EV/phase2-20260927/out/u2-adjudicate.md` (SHA-256 `a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662`, verdict ERRORS FOUND). This record does not ratify, register or execute U2. No PSID records were read and no estimator was run. Milestone 1b writes the proposed revision `u2-draft-4` into the specification's new §16a; every line the registries cite keeps its draft-3 text and line number. -**1768 RESOLVED; 0 REFUSED; 24 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 24 TO VERIFY records are exactly the adjudication's genuinely ambiguous items (disposition A); each waits for a u2-draft-4 amendment and Max's ruling. +**1778 RESOLVED; 0 REFUSED; 14 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 14 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). | Registry | RESOLVED | REFUSED | TO VERIFY | |---|---:|---:|---:| -| income | 748 | 0 | 16 | +| income | 758 | 0 | 6 | | wealth | 234 | 0 | 0 | | individual | 59 | 0 | 0 | | pension | 647 | 0 | 1 | @@ -30,7 +30,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is proposed in amendment 1. | | 2015 pension P62A | TO VERIFY (adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5 admits the documentary intersection and refuses the disputed branch. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); application waits for amendment 5 | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. The ratified §15 contract still lists account/formula/dk, so the dependent P64/P65 entries stay blocked until Max rules on amendment 5. | -| Later spouse age/sex universes | TO VERIFY (adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; family spouse age/sex are slot metadata. | +| Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | | Historical combined spouse retirement crosswalk | TO VERIFY (adjudication A) | Amendment 3: the historical scalar equivalence stays non-executable; spouse retirement components remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D) | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics. | | Revised 2017 individual-weight construction | TO VERIFY (adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Options in amendment 6. | @@ -66,7 +66,17 @@ Max ruled on blockers B1 and B2 on 2026-10-10 (d1090), after PSID answered (`doc | Refusals replaced | 8 | None of the eight F refusals retains an `action` beginning `refuse_`; each has a `resolution` and a `ruling` naming d1090 and its option. The seven role rulings name their amended draft-3 lines; the pension ruling names amendment 5c. Each preserves its adjudication record (disposition F) and its refusal text as `superseded_refusal`. The per-route rows below state each entry's remaining dependencies | | Dependencies released | 554 entries | Every dependency on the seven ruled role entries is removed: 492 income entries (123 per wave in 2015, 2019, 2021 and 2023) and 62 pension entries (the 2015 spouse slot maps and the 2019–2023 respondent slots). 538 have no dependency left; 16 2015 spouse-slot pension entries still wait on `pension:2015.route.formula_unknown_checkpoint` | -The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cut_track_u2/sources.py`, `family_unit_exclusion`; `cohort.py`, `build_u2_cohort`) and counts each excluded observation under its named disposition. No PSID record was read and no count was made. The loader's source preflight still refuses: code 88 (amendment 1c, pending d745), the later waves' spouse age/sex metadata (amendment 2), the 2015 P62A checkpoint (amendment 5b) and the 2017 weight (amendment 6) stay TO VERIFY, and the 2017–2023 P64/P65 records wait on amendment 5's registry commit. +The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cut_track_u2/sources.py`, `family_unit_exclusion`; `cohort.py`, `build_u2_cohort`) and counts each excluded observation under its named disposition. No PSID record was read and no count was made. After that commit the loader's source preflight still refused, on 74 blockers: code 88 (amendment 1c, pending d745), the later waves' spouse age/sex metadata (amendment 2), the 2015 P62A checkpoint (amendment 5b) and the 2017 weight (amendment 6) stayed TO VERIFY, and the 2017–2023 P64/P65 records waited on amendment 5's registry commit. + +## Max's ruling d637 applied (registry commits from 2026-10-12) + +Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): amendments 2–5 ratified as written and amendment 6's option 1 ratified as an explicit amendment to draft-3 line 134. Each amendment's registry effect is its own reviewed commit, and `RATIFIED_AMENDMENTS` in `src/populace_dynamics/data/u2_source_registry.py` pins each applied amendment's entries, ruling and gate fields. Each ruled entry keeps its adjudication record and gains a `ruling`; an open question or refusal the ruling answers is kept as `superseded_question` or `superseded_refusal`. Amendment 1's parts 1a–1c, and the six code-88 records with them, wait for Max's confirmation (d745). + +| Amendment | Records | Registry effect | +|---|---:|---| +| 2: annuitant input precedence | 10 | `income:income.{2015,2017,2019,2021,2023}.{wife_age,wife_sex}` are RESOLVED as slot metadata (`usage` `slot_metadata_only`), with a no-slot-inference rule in `use_restriction`: the variables and their inapplicability wording never decide who occupies the spouse slot, and annuity lives use each person's recorded sex and derived income-year age. The builder reads the spouse age only for the family file's slot flag, and the income rows refuse any observation where it disagrees with the roster's code-20 or code-22 occupant | + +The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment 1c, pending d745), the 2015 P62A checkpoint and its dependents (11, amendment 5b), the 2017–2023 P64/P65 records and their inherited-route amendments (44, amendment 5a) and the 2017 weight with the individual weight that depends on it (2, amendment 6). ## One row per source route or field @@ -200,7 +210,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2015.fu_size` | RESOLVED | ER60016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1912`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 5 | | `income:income.2015.head_age` | RESOLVED | ER60017: AGE OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1913`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `income:income.2015.head_sex` | RESOLVED | ER60018: SEX OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1914`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | -| `income:income.2015.wife_age` | TO VERIFY | In 2015, how do the family-file spouse age/sex universe and spouse income slot treat individual code 90 and a male code 20? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 68). | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1915`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:68` | +| `income:income.2015.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1915`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:68` | | `income:income.2015.n_children` | RESOLVED | ER60021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1917`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `income:income.2015.head_labor` | RESOLVED | ER65216: LABOR INCOME OF HEAD-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1749`; `PSID/family/2015/FAM2015ER.sps:7112`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1943 | | `income:income.2015.head_farm` | RESOLVED | ER65195: FARM INCOME OF HEAD AND SPOUSE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1742`; `PSID/family/2015/FAM2015ER.sps:7091`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1938 | @@ -283,7 +293,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2015.ofum_asset_acc` | RESOLVED | ER65320: ACCURACY OF OTR FU MEMBR ASSET Y-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1784`; `PSID/family/2015/FAM2015ER.sps:7216`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1969 | | `income:income.2015.wife_va_pension` | RESOLVED | ER65290: SPOUSE VA PENSION-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7186`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962 | | `income:income.2015.wife_alimony` | RESOLVED | ER65306: SPOUSE ALIMONY-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1779`; `PSID/family/2015/FAM2015ER.sps:7202`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1966 | -| `income:income.2015.wife_sex` | TO VERIFY | In 2015, how do the family-file spouse age/sex universe and spouse income slot treat individual code 90 and a male code 20? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 69). | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1916`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:69` | +| `income:income.2015.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1916`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:69` | | `income:income.2015.head_farm_acc` | RESOLVED | ER65196: ACC FARM INCOME OF HEAD AND SPOUSE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1742`; `PSID/family/2015/FAM2015ER.sps:7092`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1938 | | `income:income.2015.head_tanf_acc` | RESOLVED | ER65255: ACCURACY OF HEAD INCOME FROM TANF-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1762`; `PSID/family/2015/FAM2015ER.sps:7151`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1954 | | `income:income.2015.head_other_welfare_acc` | RESOLVED | ER65259: ACCURACY OF HEAD OTHER WELFARE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1763`; `PSID/family/2015/FAM2015ER.sps:7155`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1954; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1955 | @@ -328,7 +338,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2017.fu_size` | RESOLVED | ER66016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1940`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_age` | RESOLVED | ER66017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1941`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_sex` | RESOLVED | ER66018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1942`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | -| `income:income.2017.wife_age` | TO VERIFY | In 2017, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 71). | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1943`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:71` | +| `income:income.2017.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1943`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:71` | | `income:income.2017.n_children` | RESOLVED | ER66021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1945`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | | `income:income.2017.head_labor` | RESOLVED | ER71293: LABOR INCOME OF REF PERSON-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1775`; `PSID/family/2017/FAM2017ER.sps:7217`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1996 | | `income:income.2017.head_farm` | RESOLVED | ER71272: FARM INCOME OF REF PERSN AND SPOUSE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1768`; `PSID/family/2017/FAM2017ER.sps:7196`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1990 | @@ -411,7 +421,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2017.ofum_asset_acc` | RESOLVED | ER71397: ACCURACY OF OTR FU MEMBR ASSET Y-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1809`; `PSID/family/2017/FAM2017ER.sps:7321`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2022 | | `income:income.2017.wife_va_pension` | RESOLVED | ER71367: SPOUSE VA PENSION-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1799`; `PSID/family/2017/FAM2017ER.sps:7291`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2014 | | `income:income.2017.wife_alimony` | RESOLVED | ER71383: SPOUSE ALIMONY-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1805`; `PSID/family/2017/FAM2017ER.sps:7307`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2018 | -| `income:income.2017.wife_sex` | TO VERIFY | In 2017, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 72). | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1944`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:72` | +| `income:income.2017.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1944`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:72` | | `income:income.2017.head_farm_acc` | RESOLVED | ER71273: ACC FARM INCOME OF RP AND SPOUSE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1768`; `PSID/family/2017/FAM2017ER.sps:7197`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1991 | | `income:income.2017.head_tanf_acc` | RESOLVED | ER71332: ACCURACY OF RP INCOME FROM TANF-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1788`; `PSID/family/2017/FAM2017ER.sps:7256`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2006 | | `income:income.2017.head_other_welfare_acc` | RESOLVED | ER71336: ACCURACY OF RP OTHER WELFARE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1789`; `PSID/family/2017/FAM2017ER.sps:7260`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2007 | @@ -456,7 +466,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2019.fu_size` | RESOLVED | ER72016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1944`; `PSID/family/2019/fam2019er_codebook.pdf` p. 5 | | `income:income.2019.head_age` | RESOLVED | ER72017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1945`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | | `income:income.2019.head_sex` | RESOLVED | ER72018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1946`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | -| `income:income.2019.wife_age` | TO VERIFY | In 2019, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 74). | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1947`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:74` | +| `income:income.2019.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1947`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:74` | | `income:income.2019.n_children` | RESOLVED | ER72021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1949`; `PSID/family/2019/fam2019er_codebook.pdf` p. 7 | | `income:income.2019.head_labor` | RESOLVED | ER77315: LABOR INCOME OF REF PERSON-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1782`; `PSID/family/2019/FAM2019ER.sps:7243`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1980 | | `income:income.2019.head_farm` | RESOLVED | ER77294: FARM INCOME OF REF PERSN AND SPOUSE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1775`; `PSID/family/2019/FAM2019ER.sps:7222`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1975 | @@ -539,7 +549,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2019.ofum_asset_acc` | RESOLVED | ER77419: ACCURACY OF OTR FU MEMBR ASSET Y-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1817`; `PSID/family/2019/FAM2019ER.sps:7347`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2007 | | `income:income.2019.wife_va_pension` | RESOLVED | ER77389: SPOUSE VA PENSION-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7317`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1999 | | `income:income.2019.wife_alimony` | RESOLVED | ER77405: SPOUSE ALIMONY-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1812`; `PSID/family/2019/FAM2019ER.sps:7333`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2003 | -| `income:income.2019.wife_sex` | TO VERIFY | In 2019, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 75). | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1948`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:75` | +| `income:income.2019.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1948`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:75` | | `income:income.2019.head_farm_acc` | RESOLVED | ER77295: ACC FARM INCOME OF RP AND SPOUSE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1775`; `PSID/family/2019/FAM2019ER.sps:7223`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1975 | | `income:income.2019.head_tanf_acc` | RESOLVED | ER77354: ACCURACY OF RP INCOME FROM TANF-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1795`; `PSID/family/2019/FAM2019ER.sps:7282`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1991 | | `income:income.2019.head_other_welfare_acc` | RESOLVED | ER77358: ACCURACY OF RP OTHER WELFARE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1796`; `PSID/family/2019/FAM2019ER.sps:7286`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1991; `PSID/family/2019/fam2019er_codebook.pdf` p. 1992 | @@ -584,7 +594,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2021.fu_size` | RESOLVED | ER78016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1396`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:224` | | `income:income.2021.head_age` | RESOLVED | ER78017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1397`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6 | | `income:income.2021.head_sex` | RESOLVED | ER78018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1398`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:248` | -| `income:income.2021.wife_age` | TO VERIFY | In 2021, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 77). | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1399`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:77` | +| `income:income.2021.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1399`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:77` | | `income:income.2021.n_children` | RESOLVED | ER78021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1401`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:261` | | `income:income.2021.head_labor` | RESOLVED | ER81642: LABOR INCOME OF REF PERSON-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1224`; `PSID/family/2021/FAM2021ER.sps:5022`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1344 | | `income:income.2021.head_farm` | RESOLVED | ER81621: FARM INCOME OF REF PERSN AND SPOUSE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1217`; `PSID/family/2021/FAM2021ER.sps:5001`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1339 | @@ -667,7 +677,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2021.ofum_asset_acc` | RESOLVED | ER81746: ACCURACY OF OTR FU MEMBR ASSET Y-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1259`; `PSID/family/2021/FAM2021ER.sps:5126`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1371; `PSID/family/2021/FAM2021ER_formats.sps:37106` | | `income:income.2021.wife_va_pension` | RESOLVED | ER81716: SPOUSE VA PENSION-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1249`; `PSID/family/2021/FAM2021ER.sps:5096`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1363 | | `income:income.2021.wife_alimony` | RESOLVED | ER81732: SPOUSE ALIMONY-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1254`; `PSID/family/2021/FAM2021ER.sps:5112`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1367 | -| `income:income.2021.wife_sex` | TO VERIFY | In 2021, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 78). | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1400`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:254`; `EV/phase2-20260927/out/u2-adjudicate.md:78` | +| `income:income.2021.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1400`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:254`; `EV/phase2-20260927/out/u2-adjudicate.md:78` | | `income:income.2021.head_farm_acc` | RESOLVED | ER81622: ACC FARM INCOME OF RP AND SPOUSE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1218`; `PSID/family/2021/FAM2021ER.sps:5002`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1339; `PSID/family/2021/FAM2021ER_formats.sps:36643` | | `income:income.2021.head_tanf_acc` | RESOLVED | ER81681: ACCURACY OF RP INCOME FROM TANF-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1237`; `PSID/family/2021/FAM2021ER.sps:5061`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1355; `PSID/family/2021/FAM2021ER_formats.sps:36889` | | `income:income.2021.head_other_welfare_acc` | RESOLVED | ER81685: ACCURACY OF RP OTHER WELFARE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1239`; `PSID/family/2021/FAM2021ER.sps:5065`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1355; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1356; `PSID/family/2021/FAM2021ER_formats.sps:36903` | @@ -712,7 +722,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2023.fu_size` | RESOLVED | ER82017: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1348`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:228` | | `income:income.2023.head_age` | RESOLVED | ER82018: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1349`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6 | | `income:income.2023.head_sex` | RESOLVED | ER82019: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1350`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:252` | -| `income:income.2023.wife_age` | TO VERIFY | In 2023, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 80). | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1351`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:80` | +| `income:income.2023.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1351`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:80` | | `income:income.2023.n_children` | RESOLVED | ER82022: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:18`; `PSID/family/2023/FAM2023ER.sps:1353`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:265` | | `income:income.2023.head_labor` | RESOLVED | ER85496: LABOR INCOME OF REF PERSON-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1176`; `PSID/family/2023/FAM2023ER.sps:4827`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1262 | | `income:income.2023.head_farm` | RESOLVED | ER85475: FARM INCOME OF REF PERSN AND SPOUSE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1169`; `PSID/family/2023/FAM2023ER.sps:4806`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1257 | @@ -795,7 +805,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `income:income.2023.ofum_asset_acc` | RESOLVED | ER85600: ACCURACY OF OTR FU MEMBR ASSET Y-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1210`; `PSID/family/2023/FAM2023ER.sps:4931`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1289; `PSID/family/2023/FAM2023ER_formats.sps:34690` | | `income:income.2023.wife_va_pension` | RESOLVED | ER85570: SPOUSE VA PENSION-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1200`; `PSID/family/2023/FAM2023ER.sps:4901`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1281 | | `income:income.2023.wife_alimony` | RESOLVED | ER85586: SPOUSE ALIMONY-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1206`; `PSID/family/2023/FAM2023ER.sps:4917`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1285 | -| `income:income.2023.wife_sex` | TO VERIFY | In 2023, how do the family-file spouse age/sex universe and spouse income slot treat individual codes 90 and 92? These layout and domain matches do not settle person-to-slot routing; require documentary resolution and the reviewed section 3 amendment before use. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 2, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 81). | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1352`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:258`; `EV/phase2-20260927/out/u2-adjudicate.md:81` | +| `income:income.2023.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1352`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:258`; `EV/phase2-20260927/out/u2-adjudicate.md:81` | | `income:income.2023.head_farm_acc` | RESOLVED | ER85476: ACC FARM INCOME OF RP AND SPOUSE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1169`; `PSID/family/2023/FAM2023ER.sps:4807`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1257; `PSID/family/2023/FAM2023ER_formats.sps:34227` | | `income:income.2023.head_tanf_acc` | RESOLVED | ER85535: ACCURACY OF RP INCOME FROM TANF-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1189`; `PSID/family/2023/FAM2023ER.sps:4866`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1273; `PSID/family/2023/FAM2023ER_formats.sps:34473` | | `income:income.2023.head_other_welfare_acc` | RESOLVED | ER85539: ACCURACY OF RP OTHER WELFARE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1190`; `PSID/family/2023/FAM2023ER.sps:4870`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1273; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1274; `PSID/family/2023/FAM2023ER_formats.sps:34487` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index f638bec4c..a47cc3591 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -371,3 +371,30 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Outside the repository: the registry edit script and the review records, copied to `EV/rulings-records-20261010/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. + +## Rulings applied to the registries (from 2026-10-12, d637) + +A Claude Code session (Opus 5.5, session `943a9dba-…`) applied Max's ruling d637 (2026-09-30; specification §16b) to the U2 source registries, one reviewed commit per ratified amendment. It is not a U2 forecaster or referee, and it computed nothing from real data. + +### Read + +- `EV/RESTRICTED-FILES.md` (SHA-256 `2e1b5c2a…`), first, to know what not to open: lines 1–120 and 200–235 (the exercise sections, most of the trust-fund test section, the NASI follow-ups, the mixed working files and the cleared list) and search hits elsewhere. +- `EV/rulings-records-20260930/registry-followup-brief.md` (`7d64f32a…`, with its 2026-10-12 update) and that folder's listing; `EV/rulings-records-20261010/README.md` (`d656e27e…`), `apply_d1090_registry.py` (`4b95db62…`) and that folder's listing. +- The chief-of-staff decision log, filtered for d637, d745 and d1090; the filter also matched d1242, whose one-line summary concerns B2's count unit. +- In the repository, at #524's head and then on this branch: the specification (header, §3's relationship amendment, §15's parameter block and §§16–20), the research record's §3, the master record (summary, blocker rows, the d1090 section and the rows of the entries changed), this file's 2026-10-10 section, the eight registries and `u1_identity.json`, `src/populace_dynamics/data/u2_source_registry.py`, the U2 package's `sources.py`, `loader.py`, `cohort.py`, `invented.py`, `rows.py` and `estimator.py` (in part), `scripts/track_u2_dry_run.py` (in part), the tests under `tests/track_u2/` that the brief names, and `tests/README-tiers.md` with `tests/tier_counts.json`. +- `git worktree list`, the state, checks and file lists of #524 and #525, and #525's diff to `sources.py` and `loader.py`. + +### Not opened + +No file on the restricted list was opened: no page of the Boomers 2004 report, no comparator, seal, values scan or result, and no PSID data file. PSID's 2026-10-07 reply and its screenshots were not opened; the research record's account of them was used. No issue #42 comment was listed or read, and no Subfleet job folder other than this session's own review runs was opened. This session's own tool-output files and scratchpad sit under the Claude Code project and temporary folders that the restricted list names for other sessions' transcripts; only this session's own files there were opened. + +### Tests + +The U2 tests ran with the PSID root staged. They read staged PSID documentation and invented records; the loader tests' audit hook refuses any open under the PSID data roots. The blocker counts reported for each commit come from `loader.source_preflight`'s own rule applied to the committed registries; it opens no PSID file. No raw PSID record was read and no statistic was computed on real data. + +### Files written + +- Amendment 2: `data/external/track_u2/income.json` (the ten later spouse age and sex records and a d637 `rulings_applied` record) and `u1_identity.json` (the specification pin); `docs/design/boomers2004_1946_55_comparison.md` (§17 item 2 and §19); `docs/design/u2_m1_source_adjudication.md` (summary counts and table, the spouse age/sex blocker row, the d1090 section's closing sentence, a d637 section and the ten rows); `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS` and its validation); `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the income byte pin and two docstrings) and `invented.py` (one new named variant); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py` and `test_u2_scripts.py`; `tests/tier_counts.json` and `tests/README-tiers.md`; and this section. +- Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. + +No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 7a343d954..868577e75 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -75,6 +75,40 @@ for name, scope in RATIFIED_EXCLUSIONS.items() for entry in scope["entries"] } +_D637 = { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", +} +_LATER_WAVES = (2015, 2017, 2019, 2021, 2023) +#: The u2-draft-4 amendments Max ratified on 2026-09-30 (d637; +#: specification section 16b) that a registry commit has applied, each +#: with its exact scope: the registry and entries it rules, the ruling +#: they record, the gate fields it fixes (``fields``, exact values) and +#: the fields it removes (``absent``). An entry that records a d637 +#: ruling must be in its amendment's scope and hold those values, and +#: every entry in a scope must record the ruling. Each registry commit +#: adds its amendment here (section 16a, "Registry effect of the +#: rulings"). +RATIFIED_AMENDMENTS: dict[str, dict[str, Any]] = { + "2": { + "registry": "income", + "entries": tuple( + f"income.{wave}.{route}" + for wave in _LATER_WAVES + for route in ("wife_age", "wife_sex") + ), + "ruling": {**_D637, "amendment": "2"}, + "fields": {"status": "RESOLVED", "usage": "slot_metadata_only"}, + "absent": ("action", "blocking_dependencies", "question"), + }, +} +#: The d637 amendment each ruled entry belongs to, by (registry, id). +_AMENDMENT_OF_ENTRY = { + (scope["registry"], entry): name + for name, scope in RATIFIED_AMENDMENTS.items() + for entry in scope["entries"] +} class SourceAdjudicationError(ValueError): @@ -133,6 +167,44 @@ def _ruling(entry: dict[str, Any]) -> None: ) +def _same(first: Any, second: Any) -> bool: + """Equal as JSON, so ``True`` never stands in for ``1``.""" + return json.dumps(first, sort_keys=True) == json.dumps( + second, sort_keys=True + ) + + +def _ratified_amendment(entry: dict[str, Any], name: str) -> None: + """A d637 ruling, held to its amendment's exact scope + (:data:`RATIFIED_AMENDMENTS`).""" + ruling = entry["ruling"] + amendment = ruling.get("amendment") + scope = RATIFIED_AMENDMENTS.get(amendment) + if scope is None: + raise SourceAdjudicationError( + f"Unratified d637 amendment for {entry['id']}" + ) + if _AMENDMENT_OF_ENTRY.get((name, entry["id"])) != amendment: + raise SourceAdjudicationError( + f"d637 amendment {amendment} is not ratified for " + f"{name}:{entry['id']}" + ) + if not _same(ruling, scope["ruling"]): + raise SourceAdjudicationError( + f"ruling is not the ratified one for {entry['id']}" + ) + for field, value in scope["fields"].items(): + if field not in entry or not _same(entry[field], value): + raise SourceAdjudicationError( + f"{field} is not the ratified one for {entry['id']}" + ) + for field in scope["absent"]: + if field in entry: + raise SourceAdjudicationError( + f"{field} remains after the d637 ruling for {entry['id']}" + ) + + def _family_unit_exclusion(entry: dict[str, Any]) -> None: """A ratified exclusion, held to its exact scope: the entry it may attach to, its condition, the income role and action it leaves and @@ -522,7 +594,23 @@ def validate_registry(document: dict[str, Any], *, expected_name: str) -> None: _fields(entry, {"requires_role_registry": bool}) if "ruling" in entry: _ruling(entry) + if entry["ruling"]["decision"] == "d637": + _ratified_amendment(entry, expected_name) _entry_payload(entry, expected_name) + # Every entry an applied d637 amendment rules records that ruling, so + # no ruled entry can drop its record and still pass. + for (name, key), amendment in _AMENDMENT_OF_ENTRY.items(): + if name != expected_name: + continue + entry = next((e for e in entries if e["id"] == key), None) + if entry is None: + raise SourceAdjudicationError( + f"Missing d637 amendment {amendment} entry {name}:{key}" + ) + if entry.get("ruling", {}).get("decision") != "d637": + raise SourceAdjudicationError( + f"{name}:{key} lacks its d637 amendment {amendment} ruling" + ) def load_registry(name: str) -> dict[str, Any]: diff --git a/src/populace_dynamics/uniform_cut_track_u2/invented.py b/src/populace_dynamics/uniform_cut_track_u2/invented.py index 2db11cdb7..ea4474b4d 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/invented.py +++ b/src/populace_dynamics/uniform_cut_track_u2/invented.py @@ -1415,6 +1415,23 @@ def _spouse_slot_disagreement_2019(base: cohort.U2Inputs) -> cohort.U2Inputs: ) +def _spouse_slot_occupant_without_family_spouse_2019( + base: cohort.U2Inputs, +) -> cohort.U2Inputs: + obs = _declared_u0(base) + occupied = obs[obs["wave"].eq(2019) & obs["spouse_slot_occupied_roster"]][ + "interview" + ].iloc[0] + income = base.family_income[2019].copy() + family = income["interview"].eq(occupied) + # The family file's "no Spouse/Partner in FU" code: age 0, slot empty. + income.loc[family, "wife_present"] = False + income["wife_age"] = income["wife_age"].mask(family) + return dataclasses.replace( + base, family_income={**base.family_income, 2019: income} + ) + + def _missing_head(base: cohort.U2Inputs) -> cohort.U2Inputs: obs = _declared_u0(base) target = obs[obs["relationship"].eq(50)].iloc[0] @@ -1523,6 +1540,9 @@ def _sex_unknown_target_in_excluded_unit_2019( "sex_unknown_target_in_excluded_unit_2019": ( _sex_unknown_target_in_excluded_unit_2019 ), + "spouse_slot_occupant_without_family_spouse_2019": ( + _spouse_slot_occupant_without_family_spouse_2019 + ), } _VARIANT_NOTES: dict[str, str] = { "code_88_first_year_cohabitor": ( @@ -1583,6 +1603,13 @@ def _sex_unknown_target_in_excluded_unit_2019( "removes has unknown sex: it keeps its sex-unknown disposition " "(section 16c precedence)" ), + "spouse_slot_occupant_without_family_spouse_2019": ( + "the family file of U0's first 2019 observation with a code-20 or " + "code-22 occupant codes the spouse age 0, 'no Spouse/Partner in " + "FU': the family file's spouse age never decides slot occupancy " + "(amendment 2, ratified by d637), so the slot flag and the roster " + "disagree and the income rows refuse (refusal case)" + ), } #: The generator's named variants (see :data:`_VARIANT_NOTES`). INVENTED_VARIANTS: tuple[str, ...] = tuple(_VARIANT_BUILDERS) diff --git a/src/populace_dynamics/uniform_cut_track_u2/loader.py b/src/populace_dynamics/uniform_cut_track_u2/loader.py index c38fd7754..f10b3cb43 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/loader.py +++ b/src/populace_dynamics/uniform_cut_track_u2/loader.py @@ -12,13 +12,15 @@ the support plan and the role of every amended relationship code -- must be RESOLVED with no blocking dependency, under the committed, pinned registries. It refuses at the committed registries: code 88 - (every wave), the later waves' spouse age/sex slot metadata, the - 2015 P62A checkpoint and the revised 2017 cross-section weight are - TO VERIFY, and every 2017-2023 P64/P65 record waits on the registry - commit for amendment 5. The d1090 registry commit (section 16c) - replaced the refused 2015 code-20 and 2019-2023 code-90/92 routes - with the ratified family-unit exclusions, which released the income - and pension entries that depended on them. + (every wave), the 2015 P62A checkpoint and the revised 2017 + cross-section weight are TO VERIFY, and every 2017-2023 P64/P65 + record waits on the registry commit for amendment 5. The d1090 + registry commit (section 16c) replaced the refused 2015 code-20 and + 2019-2023 code-90/92 routes with the ratified family-unit exclusions, + which released the income and pension entries that depended on them; + the d637 registry commits (section 16b) resolve the ratified + amendments one by one, starting with amendment 2's spouse age/sex + slot metadata. 2. **Source identity**: each staged setup file and codebook the registries cite is rehashed against the registry's recorded SHA-256, and every registry layout -- the family files' and the individual diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index b8495f362..1ed821573 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -223,10 +223,15 @@ #: the eight B1 and B2 refusals became the ratified family-unit #: exclusions and the role dependencies they carried were released #: (``test_d1090_moved_only_the_ruled_gate_fields`` pins exactly which -#: gate fields moved). +#: gate fields moved). Max's d637 ruling (2026-09-30, section 16b) moves +#: them again, one reviewed commit per ratified amendment +#: (``test_d637_moved_only_the_ruled_gate_fields`` pins every gate field +#: those commits moved against the registries d1090 left): amendment 2 +#: moved the income bytes (the later spouse age and sex records became +#: slot metadata). REGISTRY_SHA256: dict[str, str] = { "income": ( - "3d4e8742356ba06986dc7dd3c664ffb1d679498e133fde8801456a76d9ff7431" + "8cd237e291ef746c98cfa0454ebaca865420fc4e729f55da6d148c74f15107ae" ), "wealth": ( "3a22247849ea2eccbef6e522c0d74c901beb90195f92fd2e7b23b123fab6b816" @@ -252,9 +257,13 @@ } #: The family-file income concepts the U2 income rows need (the union -#: over the ten rows; section 4). ``wife_age`` gives the spouse-slot -#: presence flag (not 0: "no Spouse/Partner in FU"), which rows U3 (the -#: couple convention for new enrollment) and U4 (the unit size) read. +#: over the ten rows; section 4). ``wife_age`` gives the family file's +#: spouse-slot flag (not 0: "no Spouse/Partner in FU"), which rows U3 (the +#: couple convention for new enrollment) and U4 (the unit size) read once +#: :func:`.cohort.income_rows` has held it equal to the roster's code-20 +#: or code-22 occupant. The spouse age and sex are slot metadata only +#: (amendment 2, ratified by d637; section 16b): they never decide slot +#: occupancy, and a disagreement refuses. INCOME_CONCEPTS: tuple[str, ...] = ( "interview", "fu_size", @@ -1390,8 +1399,12 @@ def decode_income(frame: pd.DataFrame) -> pd.DataFrame: """Decode parsed income fields as the income rows need them. ``wife_present`` is ``wife_age`` not 0 ("no Spouse/Partner in FU"): - the designated spouse *income slot* is occupied (section 3); an age - of 999 (DK; NA) still marks the slot occupied. + the family file reports the designated spouse *income slot* occupied + (section 3); an age of 999 (DK; NA) still marks it occupied. This is + slot metadata only (amendment 2, ratified by d637): occupancy comes + from the roster's code-20 or code-22 person, and + :func:`.cohort.income_rows` refuses any observation whose flag + disagrees with it. """ out = frame.copy() diff --git a/tests/README-tiers.md b/tests/README-tiers.md index e731d42e8..a21f1176b 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,311 | -| `artifact` | 4,054 | -| `integration_psid` | 1,346 | +| `unit` | 7,338 | +| `artifact` | 4,093 | +| `integration_psid` | 1,386 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,539** | +| **Total** | **13,645** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 16829dfd6..8efd5bf54 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,9 +1,9 @@ { "schema_version": 1, "counts": { - "unit": 7335, - "artifact": 4080, - "integration_psid": 1384, + "unit": 7338, + "artifact": 4093, + "integration_psid": 1386, "reproduction_legacy": 520, "oracle_policyengine": 308 } diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index bb1d4143e..6774ee69a 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -56,9 +56,12 @@ def quoted_spans(markdown): # Registry: (RESOLVED mappings, REFUSED by disposition F and not replaced # by a ruling, TO VERIFY). Max's d1090 ruling (2026-10-10; spec section -# 16c) replaced all eight F refusals (roles 7, pension 1). +# 16c) replaced all eight F refusals (roles 7, pension 1). The d637 +# registry commits (2026-09-30 ruling; spec section 16b) resolve the A +# items of amendments 2, 3, 5 and 6: so far amendment 2's ten income +# records. EXPECTED_COUNTS = { - "income": (748, 0, 16), + "income": (758, 0, 6), "wealth": (234, 0, 0), "individual": (59, 0, 0), "pension": (647, 0, 1), @@ -68,6 +71,12 @@ def quoted_spans(markdown): "design": (2, 0, 0), } SSI_AND_CENSUS_RESOLVED = 26 +#: TO VERIFY records left: the adjudication's 24 A items less those the +#: d637 commits resolved (amendment 2: 10). +OPEN_ITEMS = 14 +#: Each d637 amendment a registry commit has applied (section 16b), with +#: the adjudication disposition its entries carry. +D637_APPLIED = {"2": "A"} D_ITEMS = { "wealth": { @@ -258,7 +267,11 @@ def test_status_counts_after_independent_adjudication(documents): for key in totals: totals[key] += counts[key] totals["RESOLVED"] += SSI_AND_CENSUS_RESOLVED - assert totals == {"RESOLVED": 1768, "REFUSED": 0, "TO VERIFY": 24} + assert totals == { + "RESOLVED": 1792 - OPEN_ITEMS, + "REFUSED": 0, + "TO VERIFY": OPEN_ITEMS, + } # Milestone 1: 1744 RESOLVED and 48 TO VERIFY over the same records. assert sum(totals.values()) == 1744 + 48 @@ -413,8 +426,10 @@ def test_d1090_replaced_every_refusal_with_its_exclusion( assert entry["income_role"] == "excluded" assert not entry["spouse_slot"] for name in ("roles", "income", "pension"): - (applied,) = documents[name]["rulings_applied"] - assert applied["decision"] == "d1090", name + # d1090's record comes first; the d637 commits append their own. + applied = documents[name]["rulings_applied"] + assert applied[0]["decision"] == "d1090", name + assert [r["decision"] for r in applied[1:]] in ([], ["d637"]), name def test_documentary_resolutions_carry_evidence(documents): @@ -451,7 +466,96 @@ def test_open_items_name_their_amendment(documents, monkeypatch): assert f"amendment {record['amendment']}" in entry["question"] with pytest.raises(registry.SourceAdjudicationError): registry.require_resolved(name, entry["id"]) - assert open_items == 24 + assert open_items == OPEN_ITEMS + + +def test_d637_rulings_are_recorded_on_their_entries(documents, monkeypatch): + """Each d637 amendment a registry commit applied (section 16b), on + exactly its scope: the ratified ruling, text naming d637, the + adjudication record kept, the answered question kept verbatim, and + the entry applies.""" + assert set(registry.RATIFIED_AMENDMENTS) == set(D637_APPLIED) + recorded = set() + for name, document in documents.items(): + for entry in document["entries"]: + if entry.get("ruling", {}).get("decision") == "d637": + recorded.add((name, entry["id"])) + scoped = { + (scope["registry"], key) + for scope in registry.RATIFIED_AMENDMENTS.values() + for key in scope["entries"] + } + assert recorded == scoped + for amendment, scope in registry.RATIFIED_AMENDMENTS.items(): + name = scope["registry"] + document = documents[name] + monkeypatch.setattr( + registry, "load_registry", lambda _n, d=document: d + ) + by_id = {e["id"]: e for e in document["entries"]} + number = int(amendment[0]) + for key in scope["entries"]: + entry = by_id[key] + ruling = entry["ruling"] + assert ( + ruling["decision"], + ruling["date"], + ruling["specification_section"], + ruling["amendment"], + ) == ("d637", "2026-09-30", "16b", amendment) + record = entry["adjudication"] + assert record["disposition"] == D637_APPLIED[amendment], key + assert record["amendment"] == number, key + assert entry["status"] == "RESOLVED", key + assert any( + "d637" in entry.get(field, "") + for field in ("resolution", "ruling_effect", "use_restriction") + ), key + if record["disposition"] == "A": + assert "question" not in entry, key + assert entry["superseded_question"].endswith( + f"needs u2-draft-4 amendment {number}, pending Max's " + f"ruling ({ADJUDICATION} line {record['line']})." + ), key + assert registry.require_resolved(name, key) == entry + for name in { + scope["registry"] for scope in registry.RATIFIED_AMENDMENTS.values() + }: + (applied,) = [ + r + for r in documents[name]["rulings_applied"] + if r["decision"] == "d637" + ] + assert applied["amendments"] == [ + a + for a, scope in registry.RATIFIED_AMENDMENTS.items() + if scope["registry"] == name + ], name + + +def test_d637_amendment_2_spouse_metadata_never_decides_the_slot(documents): + """Amendment 2 (section 16a; ratified by d637): the later family-file + spouse age and sex are slot metadata only. Each record quotes its own + inapplicability wording as wording that decides no slot, and names the + relationship code, recorded sex and derived age as what decides.""" + entries = {e["id"]: e for e in documents["income"]["entries"]} + for key in registry.RATIFIED_AMENDMENTS["2"]["entries"]: + entry = entries[key] + assert entry["usage"] == "slot_metadata_only" + domain = " ".join(entry["documented_domain"].split()) + wording = re.search(r"0 Inap\.: (.*?); no Spouse/Partner", domain) + assert wording, key + rule = entry["use_restriction"] + assert ( + f"('{wording.group(1)}') never decide whether a person occupies " + "the spouse slot" in rule + ), key + assert "the in-family code-20 or code-22 person" in rule + assert "recorded sex (ER32000) and derived income-year age" in rule + # 2013 is U1's wave: its spouse age and sex keep U1's mapping. + for route in ("wife_age", "wife_sex"): + entry = entries.get(f"income.2013.{route}") + assert entry is None or "usage" not in entry def test_corrected_specification_citations(documents): @@ -990,8 +1094,10 @@ def test_section_16a_recommendations_and_options(): "2. Each ruling is applied to the registries in its own reviewed " "commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 " "refusals with \N{SECTION SIGN}16c's exclusions and released their " - "dependents. Until the registry commits for d637 (and, for code 88, " - "d745) land, the 24 TO VERIFY records stay open and the " + "dependents. The d637 commits, one per ratified amendment, have so " + "far resolved amendment 2's ten later spouse age and sex records as " + "slot metadata. Until the rest of them (and, for code 88, d745) " + "land, the 14 TO VERIFY records stay open and the " "2017\N{EN DASH}2023 P64/P65 records stay blocked.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " @@ -1355,7 +1461,10 @@ def test_master_record_matches_registries(documents): for entry in document["entries"] } assert rows == expected - assert "**1768 RESOLVED; 0 REFUSED; 24 TO VERIFY**" in text + assert ( + f"**{1792 - OPEN_ITEMS} RESOLVED; 0 REFUSED; {OPEN_ITEMS} TO VERIFY**" + in text + ) # Each per-route row states exactly its entry's remaining blocking # dependencies (review of 8103881, finding 3: d1090 released 554). sentences = dict( diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 335f68b67..b28bbc762 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -585,3 +585,118 @@ def test_d1090_scope_mutations_would_move_the_operative_rule(): if e["present"] and "family_unit_exclusion" in e } assert moved != expected, name + + +# --------------------------------------------------------------------------- +# Max's d637 ruling (section 16b): each applied amendment, held to its exact +# scope (``u2_source_registry.RATIFIED_AMENDMENTS``) +# --------------------------------------------------------------------------- +def _copy_ruling(document, source, target): + """Put ``source``'s d637 ruling on ``target`` as well.""" + _entry(document, target)["ruling"] = copy.deepcopy( + _entry(document, source)["ruling"] + ) + + +#: (registry, mutation): each moves, drops, widens or rewrites an applied +#: d637 amendment, and each must fail validation. +D637_SCOPE_MUTATIONS = { + "a2_ruling_on_2013_spouse_age": ( + "income", + lambda d: _copy_ruling( + d, "income.2015.wife_age", "income.2013.wife_age" + ), + ), + "a2_ruling_on_a_slot_income_item": ( + "income", + lambda d: _copy_ruling( + d, "income.2019.wife_sex", "income.2019.wife_labor" + ), + ), + "a2_ruling_dropped": ( + "income", + lambda d: _entry(d, "income.2021.wife_age").pop("ruling"), + ), + "a2_decision_fabricated": ( + "income", + lambda d: _entry(d, "income.2023.wife_sex")["ruling"].update( + decision="d999" + ), + ), + "a2_amendment_renumbered": ( + "income", + lambda d: _entry(d, "income.2017.wife_age")["ruling"].update( + amendment="3" + ), + ), + "a2_ruling_dated_otherwise": ( + "income", + lambda d: _entry(d, "income.2017.wife_sex")["ruling"].update( + date="2026-10-10" + ), + ), + "a2_usage_widened": ( + "income", + lambda d: _entry(d, "income.2019.wife_age").update( + usage="slot_occupancy" + ), + ), + "a2_usage_dropped": ( + "income", + lambda d: _entry(d, "income.2015.wife_sex").pop("usage"), + ), + "a2_reopened": ( + "income", + lambda d: _entry(d, "income.2023.wife_age").update( + status="TO VERIFY", question="reopened" + ), + ), + "a2_question_kept": ( + "income", + lambda d: _entry(d, "income.2021.wife_sex").update(question="kept"), + ), + "a2_refusing_action": ( + "income", + lambda d: _entry(d, "income.2015.wife_age").update( + action="refuse_slot_inference" + ), + ), + "a2_blocked_again": ( + "income", + lambda d: _entry(d, "income.2019.wife_sex").update( + blocking_dependencies=["roles:2019.relationship.20"] + ), + ), +} + + +@pytest.mark.parametrize("mutation", sorted(D637_SCOPE_MUTATIONS)) +def test_d637_amendments_are_held_to_their_ratified_scope(mutation): + name, change = D637_SCOPE_MUTATIONS[mutation] + document = registry.load_registry(name) + registry.validate_registry(document, expected_name=name) + changed = copy.deepcopy(document) + change(changed) + assert json.dumps(changed) != json.dumps(document) + with pytest.raises(registry.SourceAdjudicationError): + registry.validate_registry(changed, expected_name=name) + + +def test_d637_scope_matches_the_committed_entries(): + """Every applied d637 amendment sits on exactly its scope's entries, in + its own registry, and no other entry records a d637 ruling.""" + scoped = { + (scope["registry"], key): amendment + for amendment, scope in registry.RATIFIED_AMENDMENTS.items() + for key in scope["entries"] + } + found = {} + for name in registry.REGISTRY_NAMES: + for entry in registry.load_registry(name)["entries"]: + ruling = entry.get("ruling", {}) + if ruling.get("decision") == "d637": + found[(name, entry["id"])] = ruling["amendment"] + assert found == scoped + for scope in registry.RATIFIED_AMENDMENTS.values(): + assert scope["ruling"]["decision"] == "d637" + assert len(scope["entries"]) == len(set(scope["entries"])) diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 8213337fc..4c8969abd 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -90,11 +90,15 @@ def test_registries_are_the_pinned_milestone_1_bytes(committed_registries): #: Max's d1090 ruling (2026-10-10, section 16c) moved the gate fields of -#: three registries in its own reviewed commit; the other five keep the -#: adjudicated projection. The registries before that commit are read -#: from Git (CI fetches full history) at master's last pre-d1090 commit. +#: three registries in its own reviewed commit; the other five kept the +#: adjudicated projection. The registries before and after that commit +#: are read from Git (CI fetches full history): at master's last pre-d1090 +#: commit and at the d1090 registry commit, whose registries are master's +#: until the first d637 registry commit (no later d1090 commit touched +#: them). D1090_MOVED = ("income", "pension", "roles") PRE_D1090_COMMIT = "1fadf50a70d1f66151597e27a81daa191ee37e4a" +D1090_COMMIT = "07916bba0ca25b73e213f97ebdba831a7f2005fa" _D1090_GATE_PROJECTION_SHA256 = { "income": ( "15c439a3282857caddbc911f8e1a133f1cb09ac00a90142699bf67e87d8a865e" @@ -119,10 +123,11 @@ def test_registries_are_the_pinned_milestone_1_bytes(committed_registries): D1090_DEPENDENCIES = frozenset(f"roles:{key}" for key in D1090_ROLES) -def _pre_d1090(name): +def _at(commit, name): + """Registry ``name`` as committed at ``commit`` (from Git history).""" import subprocess - blob = f"{PRE_D1090_COMMIT}:data/external/track_u2/{name}.json" + blob = f"{commit}:data/external/track_u2/{name}.json" found = subprocess.run( ["git", "-C", str(registry.REGISTRY_DIRECTORY), "show", blob], capture_output=True, @@ -131,6 +136,14 @@ def _pre_d1090(name): return found.stdout +def _pre_d1090(name): + return _at(PRE_D1090_COMMIT, name) + + +def _current(name): + return (registry.REGISTRY_DIRECTORY / f"{name}.json").read_bytes() + + def _projection_sha256(raw): projection = json.dumps( _gate_projection(json.loads(raw)), @@ -159,22 +172,23 @@ def test_milestone_1b_moved_only_citations_and_prose(): Every status, disposition, dependency, action, role and layout field is where the adjudicated registries put it: in the five registries - d1090 left alone today, and in the three it moved as they stood before - its commit. So every route that refused at 883ea48 refused until - d1090, and its moves are exactly those the next test pins. + d1090 left alone, as they stood at its commit, and in the three it + moved, as they stood before it. So every route that refused at + 883ea48 refused until d1090, and its moves are exactly those the next + test pins; the d637 commits' moves are pinned after it. """ assert set(_ADJUDICATED_GATE_PROJECTION_SHA256) == set( sources.REGISTRY_SHA256 ) for name, expected in _ADJUDICATED_GATE_PROJECTION_SHA256.items(): - raw = (registry.REGISTRY_DIRECTORY / f"{name}.json").read_bytes() + raw = _current(name) assert hashlib.sha256(raw).hexdigest() == sources.REGISTRY_SHA256[name] + at_d1090 = _projection_sha256(_at(D1090_COMMIT, name)) if name in D1090_MOVED: assert _projection_sha256(_pre_d1090(name)) == expected, name - current = _D1090_GATE_PROJECTION_SHA256[name] - assert _projection_sha256(raw) == current, name + assert at_d1090 == _D1090_GATE_PROJECTION_SHA256[name], name else: - assert _projection_sha256(raw) == expected, name + assert at_d1090 == expected, name def _changed_keys(before, after): @@ -183,11 +197,11 @@ def _changed_keys(before, after): def test_d1090_moved_only_the_ruled_gate_fields(): - """The d1090 registry commit (section 16c), field by field: the eight - ruled entries change only their action, refusal, role, exclusion, - resolution and ruling fields; every other entry changes only by losing - a dependency on a ruled role entry; no entry is added, dropped or - reordered, and nothing else moves.""" + """The d1090 registry commit (section 16c), field by field, read from + Git on both sides: the eight ruled entries change only their action, + refusal, role, exclusion, resolution and ruling fields; every other + entry changes only by losing a dependency on a ruled role entry; no + entry is added, dropped or reordered, and nothing else moves.""" ruled = { ("roles", "2015.relationship.20"): { "action", @@ -222,11 +236,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): released = {} for name in D1090_MOVED: before = _gate_projection(json.loads(_pre_d1090(name))) - after = _gate_projection( - json.loads( - (registry.REGISTRY_DIRECTORY / f"{name}.json").read_bytes() - ) - ) + after = _gate_projection(json.loads(_at(D1090_COMMIT, name))) top = _changed_keys(before, after) - {"entries"} assert top == ( {"rulings_applied", "status_scope"} @@ -265,6 +275,90 @@ def test_d1090_moved_only_the_ruled_gate_fields(): assert sum(released.values()) == 492 + 45 +#: Max's d637 ruling (2026-09-30, section 16b), applied one reviewed +#: registry commit per ratified amendment. The gate fields each ruled +#: entry changes, by amendment (prose keys excluded: ``question`` is +#: prose, so its move to ``superseded_question`` shows as the new key). +D637_RULED_FIELDS = { + "2": { + "status", + "usage", + "use_restriction", + "resolution", + "ruling", + "superseded_question", + }, +} +#: The dependencies each amendment released. +D637_RELEASED: dict[str, frozenset[str]] = {} +#: The top-level fields each registry's d637 commits changed. +D637_TOP = {"income": {"rulings_applied"}} +#: The amendments each registry's ``rulings_applied`` d637 record lists. +D637_RECORDED = {"income": ["2"]} + + +def test_d637_moved_only_the_ruled_gate_fields(): + """The d637 registry commits, field by field, against the registries + the d1090 commit left (read from Git): each ruled entry is exactly its + amendment's scope (``u2_source_registry.RATIFIED_AMENDMENTS``) and + changes exactly its amendment's fields; every other entry changes only + by losing a dependency an amendment released; no entry is added, + dropped or reordered, and nothing else moves.""" + ruled = { + (scope["registry"], key): amendment + for amendment, scope in registry.RATIFIED_AMENDMENTS.items() + for key in scope["entries"] + } + assert set(registry.RATIFIED_AMENDMENTS) == set(D637_RULED_FIELDS) + releasable = set().union(*D637_RELEASED.values()) + moved, released = set(), {} + for name in registry.REGISTRY_NAMES: + before = _gate_projection(json.loads(_at(D1090_COMMIT, name))) + after = _gate_projection(json.loads(_current(name))) + top = _changed_keys(before, after) - {"entries"} + assert top == D637_TOP.get(name, set()), name + if name in D637_RECORDED: + (record,) = [ + r for r in after["rulings_applied"] if r["decision"] == "d637" + ] + assert (record["date"], record["specification_section"]) == ( + "2026-09-30", + "16b", + ) + assert record["amendments"] == D637_RECORDED[name], name + assert after["rulings_applied"][:-1] == before.get( + "rulings_applied", [] + ), name + assert [e["id"] for e in before["entries"]] == [ + e["id"] for e in after["entries"] + ], name + for old, new in zip(before["entries"], after["entries"], strict=True): + changed = _changed_keys(old, new) + if not changed: + continue + key = (name, old["id"]) + moved.add(key) + if key in ruled: + assert changed == D637_RULED_FIELDS[ruled[key]], key + continue + assert changed == {"blocking_dependencies"}, key + dropped = set(old["blocking_dependencies"]) - set( + new.get("blocking_dependencies", []) + ) + assert dropped and dropped <= releasable, key + assert set(new.get("blocking_dependencies", [])) <= set( + old["blocking_dependencies"] + ), key + released[key] = not new.get("blocking_dependencies") + for entry in after["entries"]: + assert not set(entry.get("blocking_dependencies", [])) & ( + releasable + ), (name, entry["id"]) + # Every ruled entry moved, so no scope names an untouched entry. + assert set(ruled) <= moved + assert sum(released.values()) == len(released) == 0 + + def test_the_gate_projection_sees_a_moved_gate_field(): """A changed status is not prose: the projection hash moves.""" raw = (registry.REGISTRY_DIRECTORY / "weights.json").read_bytes() @@ -478,14 +572,26 @@ def test_crosswalks_are_never_executable_inputs(declared_gate): #: The first registry blocker each later wave's family-record specs meet #: (income concepts are read first, in :data:`sources.INCOME_CONCEPTS` -#: order): every later wave's spouse age slot metadata, still TO VERIFY -#: (amendment 2). Until the d1090 registry commit (section 16c), 2015's -#: income entries depended on the refused 2015 code-20 route and -#: 2019-2023's on the refused code-90/92 routes; those dependencies are -#: released. +#: order, then wealth, then the employer-DC items): the first P64 +#: disposition, which waits on the 2015 P62ACKPT checkpoint (amendment +#: 5b) or on 2017-2023's inherited-route amendment (5a). Until the d1090 +#: registry commit (section 16c), 2015's income entries depended on the +#: refused 2015 code-20 route and 2019-2023's on the refused code-90/92 +#: routes; until the d637 amendment-2 commit (section 16b), every later +#: wave first met its spouse age slot metadata, TO VERIFY. No income or +#: wealth entry blocks a later wave now. FIRST_BLOCKER = { - wave: f"income:income.{wave}.wife_age: TO VERIFY" - for wave in (2015, 2017, 2019, 2021, 2023) + 2015: ( + "pension:2015.head_prev1_dc_disposition: blocked by " + "pension:2015.route.formula_unknown_checkpoint" + ), + **{ + wave: ( + f"pension:{wave}.head_prev1_dc_disposition: blocked by " + f"pension:{wave}.route.inherited_route_amendment" + ) + for wave in (2017, 2019, 2021, 2023) + }, } @@ -546,7 +652,10 @@ def test_declared_gate_records_what_the_registry_gate_refuses( audit = gate.audit() assert audit["gate"] == sources.INVENTED_DECLARED refused = audit["would_refuse_under_registry_gate"] - assert "income:income.2019.wife_age" in refused + # Since the d637 amendment-2 commit (section 16b) no income or wealth + # entry is refused: the spouse age and sex are resolved slot metadata. + assert refused + assert not any(key.startswith(("income:", "wealth:")) for key in refused) # Since d1090 no reason is a ruled role entry (section 16c). assert not any( dependency in reason diff --git a/tests/track_u2/test_u2_roles.py b/tests/track_u2/test_u2_roles.py index 5e98ec846..97fb73673 100644 --- a/tests/track_u2/test_u2_roles.py +++ b/tests/track_u2/test_u2_roles.py @@ -839,6 +839,40 @@ def test_spouse_slot_disagreement_refuses(u2_inputs, declared): cohort.income_rows(built, inputs) +@pytest.mark.parametrize("context", ["declared", "registry"]) +def test_family_file_no_spouse_code_never_removes_the_occupant( + u2_inputs, declared, context +): + """Amendment 2 (ratified by d637, section 16b): the family file's + spouse age is slot metadata only. Its 0 code, 'no Spouse/Partner in + FU', never removes the roster's code-20 or code-22 occupant: the + roster keeps the occupant, the two disagree, and the income rows + refuse rather than let either source decide.""" + inputs = invented.invented_variant( + u2_inputs, "spouse_slot_occupant_without_family_spouse_2019" + ) + roles = ( + declared + if context == "declared" + else sources.RoleContext.from_registry() + ) + base = cohort.build_u2_cohort(u2_inputs, role_context=declared) + obs = base.observations + family = obs[obs["wave"].eq(2019) & obs["spouse_slot_occupied_roster"]][ + "interview" + ].iloc[0] + built = cohort.build_u2_cohort(inputs, role_context=roles) + rows = built.observations[ + built.observations["wave"].eq(2019) + & built.observations["interview"].eq(family) + ] + assert len(rows) and rows["spouse_slot_occupied_roster"].all() + income = inputs.family_income[2019] + assert not income.loc[income["interview"].eq(family), "wife_present"].any() + with pytest.raises(cohort.U2CohortError, match="spouse income slot"): + cohort.income_rows(built, inputs) + + def test_missing_head_refuses_the_annuity(u2_inputs, declared, u2_params): obs = cohort.build_u2_cohort(u2_inputs, role_context=declared).observations target = obs[obs["relationship"].eq(50)].iloc[0] diff --git a/tests/track_u2/test_u2_scripts.py b/tests/track_u2/test_u2_scripts.py index 930c53866..5228a8f45 100644 --- a/tests/track_u2/test_u2_scripts.py +++ b/tests/track_u2/test_u2_scripts.py @@ -161,6 +161,7 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): "code_88_in_excluded_unit_2019", "zero_weight_target_in_excluded_unit_2015", "sex_unknown_target_in_excluded_unit_2019", + "spouse_slot_occupant_without_family_spouse_2019", ] assert all(entry["regenerated"] for entry in variants.values()) build_refused = { @@ -182,6 +183,9 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): # family file's slot flag, so the income rows refuse. "ambiguous_legal_spouse": "U2CohortError", "spouse_slot_disagreement_2019": "U2CohortError", + # Amendment 2 (d637): the family file's "no spouse" code never + # removes the roster's occupant; the disagreement refuses. + "spouse_slot_occupant_without_family_spouse_2019": "U2CohortError", "missing_head": "AdjustedPovertyError", } for name, entry in variants.items(): From cf03f4328a3d4a40e74bd051db91f81af8e677ac Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:22:54 -0400 Subject: [PATCH 02/21] Apply d637 amendment 3 to the U2 source registries Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written. This is its registry commit. - income.json: the six income..wife_retirement_annuities crosswalk records (2013-2023) are RESOLVED as non-executable metadata. They keep kind historical_route_crosswalk and their usage, gain a use_restriction, a resolution and a ruling (d637, amendment 3), keep their adjudication record (A), and keep the answered question as superseded_question. No scalar equivalence is asserted with U1's combined item; the components stay inside total family money income and are never added twice. No row changes. - u2_source_registry: amendment 3's scope (entries, ruling, kind and usage fixed; no action or question). - sources: the income byte pin moves. - No loader blocker moves (69): no U2 run applies these records. The field adapter refuses them as inputs under the registry gate too, now that the gate passes them; a test checks every wave. - Master record: 1784 RESOLVED, 0 REFUSED, 8 TO VERIFY; the crosswalk blocker row, the d637 section and the six rows. Specification section 17 item 2 and section 19 record it; the spec is re-pinned. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 89 ++++++++++++++----- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 19 ++-- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 16 ++++ .../uniform_cut_track_u2/sources.py | 9 +- tests/README-tiers.md | 4 +- tests/tier_counts.json | 2 +- tests/track_u2/test_adjudication_applied.py | 15 ++-- tests/track_u2/test_source_registries.py | 32 +++++++ tests/track_u2/test_u2_mappings.py | 24 ++++- 12 files changed, 169 insertions(+), 47 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index e8407b703..24f8e85d5 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -4351,7 +4351,6 @@ "ER58101", "ER58103" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -4418,14 +4417,23 @@ "codebook_text": "ER58097: Wife/\"Wife's\" income from retirement pay or pensions.\nThe values for this variable represent the income from retirement pay or pensions in whole\ndollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: no Wife/\"Wife\" in FU (ER54305=5); did not receive\nincome from retirement pay or pensions in 2012\n\nER58099: Wife/\"Wife's\" income from annuities.\nThe values for this variable represent the income from annuities in whole dollars. All\nmissing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: no Wife/\"Wife\" in FU (ER54305=5); did not receive\nincome from annuities in 2012\n\nER58101: Wife/\"Wife\" income from IRAs in 2012.\nThe values for this variable represent the income from IRAs in whole dollars. All missing\ndata were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: no Wife/\"Wife\" in FU (ER54305=5); did not receive\nincome from IRAs in 2012\n\nER58103: Wife/\"Wife's\" income from other (retirement).\nThe values for this variable represent the income from other (retirement) in whole\ndollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: no Wife/\"Wife\" in FU (ER54305=5); did not receive\nincome from other (retirement) in 2012", "documented_domain": "Separate component domains apply; no scalar source variable is declared.", "usage": "Documentary crosswalk only; not an executable derived input", - "question": "The earlier U1 wife retirement/annuities item has no single 2013 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 67).", "blocking_dependencies": [], "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 67, "disposition": "A", "amendment": 3 - } + }, + "status": "RESOLVED", + "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "3" + }, + "superseded_question": "The earlier U1 wife retirement/annuities item has no single 2013 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 67)." }, { "id": "income.2015.interview", @@ -8940,7 +8948,6 @@ "ER65296", "ER65298" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -9007,13 +9014,22 @@ "codebook_text": "ER65292: Spouse's/Partner's Income from Retirement Pay or Pensions in 2014\nThe values for this variable represent Spouse's/Partner's income from retirement pay or\npensions, in 2014, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from retirement pay or\npensions in 2014; no Spouse/Partner in FU (ER61347=5)\n\nER65294: Spouse's/Partner's Income from Annuities in 2014\nThe values for this variable represent Spouse's/Partner's income from annuities, in 2014,\nin whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from annuities in 2014; no\nSpouse/Partner in FU (ER61347=5)\n\nER65296: Spouse's/Partner's income from IRAs in 2014.\nThe values for this variable represent Spouse's/Partner's income from IRAs, in 2014, in\nwhole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from IRAs in 2014; no\nSpouse/Partner in FU (ER61347=5)\n\nER65298: Spouse's/Partner's Income from Other Retirement in 2014\nThe values for this variable represent Spouse's/Partner's income from other retirement, in\n2014, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from other retirement in\n2014; no Spouse/Partner in FU (ER61347=5)", "documented_domain": "Separate component domains apply; no scalar source variable is declared.", "usage": "Documentary crosswalk only; not an executable derived input", - "question": "The earlier U1 wife retirement/annuities item has no single 2015 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 70).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 70, "disposition": "A", "amendment": 3 - } + }, + "status": "RESOLVED", + "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "3" + }, + "superseded_question": "The earlier U1 wife retirement/annuities item has no single 2015 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 70)." }, { "id": "income.2017.interview", @@ -13536,7 +13552,6 @@ "ER71373", "ER71375" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -13603,13 +13618,22 @@ "codebook_text": "ER71369: Spouse's/Partner's Income from Retirement Pay or Pensions in 2016\nThe values for this variable represent Spouse's/Partner's income from retirement pay or\npensions, in 2016, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from retirement pay or\npensions in 2016; no Spouse/Partner in FU (ER67399=5)\n\nER71371: Spouse's/Partner's Income from Annuities in 2016\nThe values for this variable represent Spouse's/Partner's income from annuities, in 2016,\nin whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from annuities in 2016; no\nSpouse/Partner in FU (ER67399=5)\n\nER71373: Spouse's/Partner's income from I.R.A.s in 2016.\nThe values for this variable represent Spouse's/Partner's income from IRAs, in 2016, in\nwhole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from IRAs in 2016; no\nSpouse/Partner in FU (ER67399=5)\n\nER71375: Spouse's/Partner's Income from Other Retirement in 2016\nThe values for this variable represent Spouse's/Partner's income from other retirement, in\n2016, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from other retirement in\n2016; no Spouse/Partner in FU (ER67399=5)", "documented_domain": "Separate component domains apply; no scalar source variable is declared.", "usage": "Documentary crosswalk only; not an executable derived input", - "question": "The earlier U1 wife retirement/annuities item has no single 2017 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 73).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 73, "disposition": "A", "amendment": 3 - } + }, + "status": "RESOLVED", + "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "3" + }, + "superseded_question": "The earlier U1 wife retirement/annuities item has no single 2017 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 73)." }, { "id": "income.2019.interview", @@ -18124,7 +18148,6 @@ "ER77395", "ER77397" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -18191,13 +18214,22 @@ "codebook_text": "ER77391: Spouse's/Partner's Income from Retirement Pay or Pensions in 2018\nThe values for this variable represent Spouse's/Partner's income from retirement pay or\npensions, in 2018, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from retirement pay or\npensions in 2018; no Spouse/Partner in FU (ER73422=5)\n\nER77393: Spouse's/Partner's Income from Annuities in 2018\nThe values for this variable represent Spouse's/Partner's income from annuities, in 2018,\nin whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from annuities in 2018; no\nSpouse/Partner in FU (ER73422=5)\n\nER77395: Spouse's/Partner's income from I.R.A.s in 2018.\nThe values for this variable represent Spouse's/Partner's income from IRAs, in 2018, in\nwhole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from IRAs in 2018; no\nSpouse/Partner in FU (ER73422=5)\n\nER77397: Spouse's/Partner's Income from Other Retirement in 2018\nThe values for this variable represent Spouse's/Partner's income from other retirement, in\n2018, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from other retirement in\n2018; no Spouse/Partner in FU (ER73422=5)", "documented_domain": "Separate component domains apply; no scalar source variable is declared.", "usage": "Documentary crosswalk only; not an executable derived input", - "question": "The earlier U1 wife retirement/annuities item has no single 2019 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 76).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 76, "disposition": "A", "amendment": 3 - } + }, + "status": "RESOLVED", + "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "3" + }, + "superseded_question": "The earlier U1 wife retirement/annuities item has no single 2019 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 76)." }, { "id": "income.2021.interview", @@ -22937,7 +22969,6 @@ "ER81722", "ER81724" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -23004,13 +23035,22 @@ "codebook_text": "ER81718: Spouse's/Partner's Income from Retirement Pay or Pensions in 2020\nThe values for this variable represent Spouse's/Partner's income from retirement pay or\npensions, in 2020, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from retirement pay or\npensions in 2020; no Spouse/Partner in FU (ER79524=5)\n\nER81720: Spouse's/Partner's Income from Annuities in 2020\nThe values for this variable represent Spouse's/Partner's income from annuities, in 2020,\nin whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from annuities in 2020; no\nSpouse/Partner in FU (ER79524=5)\n\nER81722: Spouse's/Partner's income from I.R.A.s in 2020.\nThe values for this variable represent Spouse's/Partner's income from IRAs, in 2020, in\nwhole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from IRAs in 2020; no\nSpouse/Partner in FU (ER79524=5)\n\nER81724: Spouse's/Partner's Income from Other Retirement in 2020\nThe values for this variable represent Spouse's/Partner's income from other retirement, in\n2020, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from other retirement in\n2020; no Spouse/Partner in FU (ER79524=5)", "documented_domain": "Separate component domains apply; no scalar source variable is declared.", "usage": "Documentary crosswalk only; not an executable derived input", - "question": "The earlier U1 wife retirement/annuities item has no single 2021 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 79).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 79, "disposition": "A", "amendment": 3 - } + }, + "status": "RESOLVED", + "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "3" + }, + "superseded_question": "The earlier U1 wife retirement/annuities item has no single 2021 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 79)." }, { "id": "income.2023.interview", @@ -27750,7 +27790,6 @@ "ER85576", "ER85578" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -27817,13 +27856,22 @@ "codebook_text": "ER85572: Spouse's/Partner's Income from Retirement Pay or Pensions in 2022\nThe values for this variable represent Spouse's/Partner's income from retirement pay or\npensions, in 2022, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from retirement pay or\npensions in 2022; no Spouse/Partner in FU (ER83493=5)\n\nER85574: Spouse's/Partner's Income from Annuities in 2022\nThe values for this variable represent Spouse's/Partner's income from annuities, in 2022,\nin whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from annuities in 2022; no\nSpouse/Partner in FU (ER83493=5)\n\nER85576: Spouse's/Partner's income from I.R.A.s in 2022.\nThe values for this variable represent Spouse's/Partner's income from IRAs, in 2022, in\nwhole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from IRAs in 2022; no\nSpouse/Partner in FU (ER83493=5)\n\nER85578: Spouse's/Partner's Income from Other Retirement in 2022\nThe values for this variable represent Spouse's/Partner's income from other retirement, in\n2022, in whole dollars. All missing data were assigned.\nValue/Range Code Value/Range Text\n1 - 999,997 Actual amount\n0 Inap.: did not receive income from other retirement in\n2022; no Spouse/Partner in FU (ER83493=5)", "documented_domain": "Separate component domains apply; no scalar source variable is declared.", "usage": "Documentary crosswalk only; not an executable derived input", - "question": "The earlier U1 wife retirement/annuities item has no single 2023 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 82).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 82, "disposition": "A", "amendment": 3 - } + }, + "status": "RESOLVED", + "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "3" + }, + "superseded_question": "The earlier U1 wife retirement/annuities item has no single 2023 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 82)." } ], "sources": [ @@ -27981,9 +28029,10 @@ "date": "2026-09-30", "specification_section": "16b", "amendments": [ - "2" + "2", + "3" ], - "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 2 resolves the ten 2015-2023 family-file spouse age and sex records as slot metadata only, with a no-slot-inference rule. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 2 resolves the ten 2015-2023 family-file spouse age and sex records as slot metadata only, with a no-slot-inference rule; amendment 3 resolves the six wife_retirement_annuities crosswalk records as non-executable metadata. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 619fa46d8..58e0469ee 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "9ec1bd0e8b2f3552facb5553323c26f2060cfad1c88543dba44a7f508f89f0c3", + "sha256": "39e2abc4d353e28a046e9444e6afeead55ab4072b601dc7f73746bb0f62a0316", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index e8f15337c..7f62aad44 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata. Until the rest of them (and, for code 88, d745) land, the 14 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata. Until the rest of them (and, for code 88, d745) land, the 8 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1841,6 +1841,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Each ratified amendment's registry effect (§16a, "Registry effect of the rulings") lands in its own reviewed commit. `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS`) pins each applied amendment's entries, ruling and gate fields, and `tests/track_u2/test_u2_mappings.py` checks every gate field these commits move against the registries the d1090 commit left, read from Git. Amendment 1's parts 1a–1c, and with them the six code-88 records, wait for Max's confirmation (d745). No line before §16a and no registry-cited line changes. - Amendment 2: the ten 2015–2023 family-file spouse age and sex records (`income..wife_age` and `wife_sex`) are RESOLVED as slot metadata only. Under their no-slot-inference rule, the variables and their 'Head is female or single male' or 'Reference Person is female or single male' wording never decide whether a person occupies the spouse slot; the in-family code-20 or code-22 person does, and annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age. The builder reads the spouse age only for the family file's slot flag, which the income rows must find equal to the roster's occupant or they refuse; a new invented variant covers the direction this amendment concerns, a family-file 'no spouse' code beside a roster occupant. The loader's source preflight now refuses on 69 blockers, down from 74. The master record counts 1778 RESOLVED, 0 REFUSED and 14 TO VERIFY records. +- Amendment 3: the six `income..wife_retirement_annuities` crosswalk records (2013–2023) are RESOLVED as non-executable metadata. They keep their crosswalk kind, which the field adapter refuses as an input under either gate; no scalar equivalence is asserted between U1's combined item and the later separately documented spouse pension, annuity, IRA and other-retirement items, which stay inside total family money income under §4 and are never added a second time. No row changes and no loader blocker moves (69). The master record counts 1784 RESOLVED, 0 REFUSED and 8 TO VERIFY records. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index f13e05901..49a25dde6 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,11 +2,11 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2026-09-28) to apply the independent adjudication `EV/phase2-20260927/out/u2-adjudicate.md` (SHA-256 `a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662`, verdict ERRORS FOUND). This record does not ratify, register or execute U2. No PSID records were read and no estimator was run. Milestone 1b writes the proposed revision `u2-draft-4` into the specification's new §16a; every line the registries cite keeps its draft-3 text and line number. -**1778 RESOLVED; 0 REFUSED; 14 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 14 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). +**1784 RESOLVED; 0 REFUSED; 8 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 8 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). | Registry | RESOLVED | REFUSED | TO VERIFY | |---|---:|---:|---:| -| income | 758 | 0 | 6 | +| income | 764 | 0 | 0 | | wealth | 234 | 0 | 0 | | individual | 59 | 0 | 0 | | pension | 647 | 0 | 1 | @@ -31,7 +31,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | 2015 pension P62A | TO VERIFY (adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5 admits the documentary intersection and refuses the disputed branch. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); application waits for amendment 5 | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. The ratified §15 contract still lists account/formula/dk, so the dependent P64/P65 entries stay blocked until Max rules on amendment 5. | | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | -| Historical combined spouse retirement crosswalk | TO VERIFY (adjudication A) | Amendment 3: the historical scalar equivalence stays non-executable; spouse retirement components remain inside total family income and are never added again. | +| Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D) | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics. | | Revised 2017 individual-weight construction | TO VERIFY (adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Options in amendment 6. | | 2023 weight documentation | RESOLVED | Original June 2026 report captured and SHA-256 pinned in `tests/data/track_u2/psid_sources/weights23_manifest.json`. | @@ -75,6 +75,7 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | Amendment | Records | Registry effect | |---|---:|---| | 2: annuitant input precedence | 10 | `income:income.{2015,2017,2019,2021,2023}.{wife_age,wife_sex}` are RESOLVED as slot metadata (`usage` `slot_metadata_only`), with a no-slot-inference rule in `use_restriction`: the variables and their inapplicability wording never decide who occupies the spouse slot, and annuity lives use each person's recorded sex and derived income-year age. The builder reads the spouse age only for the family file's slot flag, and the income rows refuse any observation where it disagrees with the roster's code-20 or code-22 occupant | +| 3: historical spouse-retirement crosswalk | 6 | `income:income.{2013,2015,2017,2019,2021,2023}.wife_retirement_annuities` are RESOLVED as non-executable metadata: their `kind` stays `historical_route_crosswalk`, which the field adapter refuses as an input under either gate, and a `use_restriction` states that no input or row reads them and no scalar equivalence is inferred. No row changes and no loader blocker moves: no U2 run applies these records | The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment 1c, pending d745), the 2015 P62A checkpoint and its dependents (11, amendment 5b), the 2017–2023 P64/P65 records and their inherited-route amendments (44, amendment 5a) and the 2017 weight with the individual weight that depends on it (2, amendment 6). @@ -205,7 +206,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `income:income.2013.wife_annuities_acc` | RESOLVED | ER58100: ACCURACY OF WIFE ANNUITIES-2012. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1710`; `PSID/family/2013/FAM2013ER.sps:6923`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826 | | `income:income.2013.wife_iras_acc` | RESOLVED | ER58102: ACCURACY OF WIFE IRAS-2012. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6925`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826 | | `income:income.2013.wife_other_retirement_acc` | RESOLVED | ER58104: ACCURACY OF WIFE OTHER RETIREMENT-2012. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1712`; `PSID/family/2013/FAM2013ER.sps:6927`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1827 | -| `income:income.2013.wife_retirement_annuities` | TO VERIFY | The earlier U1 wife retirement/annuities item has no single 2013 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 67). | `PSID/family/2013/FAM2013ER.sps:1709`; `PSID/family/2013/FAM2013ER.sps:6920`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1825; `PSID/family/2013/FAM2013ER.sps:1710`; `PSID/family/2013/FAM2013ER.sps:6922`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6924`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6926`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1827; `EV/phase2-20260927/out/u2-adjudicate.md:67` | +| `income:income.2013.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2013/FAM2013ER.sps:1709`; `PSID/family/2013/FAM2013ER.sps:6920`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1825; `PSID/family/2013/FAM2013ER.sps:1710`; `PSID/family/2013/FAM2013ER.sps:6922`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6924`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6926`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1827; `EV/phase2-20260927/out/u2-adjudicate.md:67` | | `income:income.2015.interview` | RESOLVED | ER60002: 2015 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:11`; `PSID/family/2015/FAM2015ER.sps:1898`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 2 | | `income:income.2015.fu_size` | RESOLVED | ER60016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1912`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 5 | | `income:income.2015.head_age` | RESOLVED | ER60017: AGE OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1913`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | @@ -333,7 +334,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `income:income.2015.wife_other_retirement_acc` | RESOLVED | ER65299: ACCURACY OF SPOUSE OTHER RETIREMENT-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1777`; `PSID/family/2015/FAM2015ER.sps:7195`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1964 | | `income:income.2015.wife_va_pension_acc` | RESOLVED | ER65291: ACCURACY OF SPOUSE VA PENSION-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7187`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962 | | `income:income.2015.wife_alimony_acc` | RESOLVED | ER65307: ACCURACY OF SPOUSE ALIMONY-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1779`; `PSID/family/2015/FAM2015ER.sps:7203`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1966 | -| `income:income.2015.wife_retirement_annuities` | TO VERIFY | The earlier U1 wife retirement/annuities item has no single 2015 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 70). | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7188`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962; `PSID/family/2015/FAM2015ER.sps:1775`; `PSID/family/2015/FAM2015ER.sps:7190`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7192`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7194`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1964; `EV/phase2-20260927/out/u2-adjudicate.md:70` | +| `income:income.2015.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7188`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962; `PSID/family/2015/FAM2015ER.sps:1775`; `PSID/family/2015/FAM2015ER.sps:7190`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7192`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7194`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1964; `EV/phase2-20260927/out/u2-adjudicate.md:70` | | `income:income.2017.interview` | RESOLVED | ER66002: 2017 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:11`; `PSID/family/2017/FAM2017ER.sps:1926`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2 | | `income:income.2017.fu_size` | RESOLVED | ER66016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1940`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_age` | RESOLVED | ER66017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1941`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | @@ -461,7 +462,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `income:income.2017.wife_other_retirement_acc` | RESOLVED | ER71376: ACCURACY OF SPOUSE OTHER RETIREMENT-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1802`; `PSID/family/2017/FAM2017ER.sps:7300`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016 | | `income:income.2017.wife_va_pension_acc` | RESOLVED | ER71368: ACCURACY OF SPOUSE VA PENSION-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1800`; `PSID/family/2017/FAM2017ER.sps:7292`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2014 | | `income:income.2017.wife_alimony_acc` | RESOLVED | ER71384: ACCURACY OF SPOUSE ALIMONY-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1805`; `PSID/family/2017/FAM2017ER.sps:7308`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2018 | -| `income:income.2017.wife_retirement_annuities` | TO VERIFY | The earlier U1 wife retirement/annuities item has no single 2017 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 73). | `PSID/family/2017/FAM2017ER.sps:1800`; `PSID/family/2017/FAM2017ER.sps:7293`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7295`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7297`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `PSID/family/2017/FAM2017ER.sps:1802`; `PSID/family/2017/FAM2017ER.sps:7299`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `EV/phase2-20260927/out/u2-adjudicate.md:73` | +| `income:income.2017.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2017/FAM2017ER.sps:1800`; `PSID/family/2017/FAM2017ER.sps:7293`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7295`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7297`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `PSID/family/2017/FAM2017ER.sps:1802`; `PSID/family/2017/FAM2017ER.sps:7299`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `EV/phase2-20260927/out/u2-adjudicate.md:73` | | `income:income.2019.interview` | RESOLVED | ER72002: 2019 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:11`; `PSID/family/2019/FAM2019ER.sps:1930`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2 | | `income:income.2019.fu_size` | RESOLVED | ER72016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1944`; `PSID/family/2019/fam2019er_codebook.pdf` p. 5 | | `income:income.2019.head_age` | RESOLVED | ER72017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1945`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | @@ -589,7 +590,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `income:income.2019.wife_other_retirement_acc` | RESOLVED | ER77398: ACCURACY OF SPOUSE OTHER RETIREMENT-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1810`; `PSID/family/2019/FAM2019ER.sps:7326`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001 | | `income:income.2019.wife_va_pension_acc` | RESOLVED | ER77390: ACCURACY OF SPOUSE VA PENSION-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7318`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1999 | | `income:income.2019.wife_alimony_acc` | RESOLVED | ER77406: ACCURACY OF SPOUSE ALIMONY-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1812`; `PSID/family/2019/FAM2019ER.sps:7334`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2003 | -| `income:income.2019.wife_retirement_annuities` | TO VERIFY | The earlier U1 wife retirement/annuities item has no single 2019 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 76). | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7319`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1808`; `PSID/family/2019/FAM2019ER.sps:7321`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7323`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7325`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `EV/phase2-20260927/out/u2-adjudicate.md:76` | +| `income:income.2019.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7319`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1808`; `PSID/family/2019/FAM2019ER.sps:7321`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7323`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7325`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `EV/phase2-20260927/out/u2-adjudicate.md:76` | | `income:income.2021.interview` | RESOLVED | ER78002: 2021 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:11`; `PSID/family/2021/FAM2021ER.sps:1382`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 2 | | `income:income.2021.fu_size` | RESOLVED | ER78016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1396`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:224` | | `income:income.2021.head_age` | RESOLVED | ER78017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1397`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6 | @@ -717,7 +718,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `income:income.2021.wife_other_retirement_acc` | RESOLVED | ER81725: ACCURACY OF SPOUSE OTHER RETIREMENT-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1252`; `PSID/family/2021/FAM2021ER.sps:5105`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `PSID/family/2021/FAM2021ER_formats.sps:37043` | | `income:income.2021.wife_va_pension_acc` | RESOLVED | ER81717: ACCURACY OF SPOUSE VA PENSION-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1249`; `PSID/family/2021/FAM2021ER.sps:5097`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1363; `PSID/family/2021/FAM2021ER_formats.sps:37015` | | `income:income.2021.wife_alimony_acc` | RESOLVED | ER81733: ACCURACY OF SPOUSE ALIMONY-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1255`; `PSID/family/2021/FAM2021ER.sps:5113`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1367; `PSID/family/2021/FAM2021ER_formats.sps:37071` | -| `income:income.2021.wife_retirement_annuities` | TO VERIFY | The earlier U1 wife retirement/annuities item has no single 2021 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 79). | `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5098`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5100`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1251`; `PSID/family/2021/FAM2021ER.sps:5102`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `PSID/family/2021/FAM2021ER.sps:1252`; `PSID/family/2021/FAM2021ER.sps:5104`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `EV/phase2-20260927/out/u2-adjudicate.md:79` | +| `income:income.2021.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5098`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5100`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1251`; `PSID/family/2021/FAM2021ER.sps:5102`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `PSID/family/2021/FAM2021ER.sps:1252`; `PSID/family/2021/FAM2021ER.sps:5104`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `EV/phase2-20260927/out/u2-adjudicate.md:79` | | `income:income.2023.interview` | RESOLVED | ER82002: 2023 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:11`; `PSID/family/2023/FAM2023ER.sps:1333`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 2 | | `income:income.2023.fu_size` | RESOLVED | ER82017: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1348`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:228` | | `income:income.2023.head_age` | RESOLVED | ER82018: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1349`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6 | @@ -845,7 +846,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `income:income.2023.wife_other_retirement_acc` | RESOLVED | ER85579: ACCURACY OF SPOUSE OTHER RETIREMENT-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1203`; `PSID/family/2023/FAM2023ER.sps:4910`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `PSID/family/2023/FAM2023ER_formats.sps:34627` | | `income:income.2023.wife_va_pension_acc` | RESOLVED | ER85571: ACCURACY OF SPOUSE VA PENSION-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1201`; `PSID/family/2023/FAM2023ER.sps:4902`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1281; `PSID/family/2023/FAM2023ER_formats.sps:34599` | | `income:income.2023.wife_alimony_acc` | RESOLVED | ER85587: ACCURACY OF SPOUSE ALIMONY-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1206`; `PSID/family/2023/FAM2023ER.sps:4918`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1285; `PSID/family/2023/FAM2023ER_formats.sps:34655` | -| `income:income.2023.wife_retirement_annuities` | TO VERIFY | The earlier U1 wife retirement/annuities item has no single 2023 counterpart. Later documentation identifies pensions, annuities, IRAs and other retirement separately but does not establish which reproduce that earlier combined item's IRA coverage. Do not infer a scalar historical equivalence. U2 uses the separately documented later fields under its inherited spouse-retirement retention convention. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 3, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 82). | `PSID/family/2023/FAM2023ER.sps:1201`; `PSID/family/2023/FAM2023ER.sps:4903`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4905`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4907`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `PSID/family/2023/FAM2023ER.sps:1203`; `PSID/family/2023/FAM2023ER.sps:4909`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `EV/phase2-20260927/out/u2-adjudicate.md:82` | +| `income:income.2023.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2023/FAM2023ER.sps:1201`; `PSID/family/2023/FAM2023ER.sps:4903`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4905`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4907`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `PSID/family/2023/FAM2023ER.sps:1203`; `PSID/family/2023/FAM2023ER.sps:4909`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `EV/phase2-20260927/out/u2-adjudicate.md:82` | | `wealth:wealth.2013.wealth1` | RESOLVED | ER58209: IMP WEALTH W/O EQUITY (WEALTH1) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1747`; `PSID/family/2013/FAM2013ER.sps:7032`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1851 | | `wealth:wealth.2013.wealth1_acc` | RESOLVED | ER58210: ACC WEALTH W/O EQUITY (WEALTH1) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1747`; `PSID/family/2013/FAM2013ER.sps:7033`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1852 | | `wealth:wealth.2013.wealth2` | RESOLVED | ER58211: IMP WEALTH W/ EQUITY (WEALTH2) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1747`; `PSID/family/2013/FAM2013ER.sps:7034`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1852 | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index a47cc3591..86b49be6f 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -395,6 +395,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation ### Files written - Amendment 2: `data/external/track_u2/income.json` (the ten later spouse age and sex records and a d637 `rulings_applied` record) and `u1_identity.json` (the specification pin); `docs/design/boomers2004_1946_55_comparison.md` (§17 item 2 and §19); `docs/design/u2_m1_source_adjudication.md` (summary counts and table, the spouse age/sex blocker row, the d1090 section's closing sentence, a d637 section and the ten rows); `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS` and its validation); `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the income byte pin and two docstrings) and `invented.py` (one new named variant); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py` and `test_u2_scripts.py`; `tests/tier_counts.json` and `tests/README-tiers.md`; and this section. +- Amendment 3: `income.json` (the six crosswalk records and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the crosswalk blocker row, the d637 section and the six rows); `u2_source_registry.py` (amendment 3's scope) and `sources.py` (the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 868577e75..99b7bb034 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -102,6 +102,22 @@ "fields": {"status": "RESOLVED", "usage": "slot_metadata_only"}, "absent": ("action", "blocking_dependencies", "question"), }, + "3": { + "registry": "income", + "entries": tuple( + f"income.{wave}.wife_retirement_annuities" + for wave in (2013, *_LATER_WAVES) + ), + "ruling": {**_D637, "amendment": "3"}, + "fields": { + "status": "RESOLVED", + "kind": "historical_route_crosswalk", + "usage": ( + "Documentary crosswalk only; not an executable derived input" + ), + }, + "absent": ("action", "question"), + }, } #: The d637 amendment each ruled entry belongs to, by (registry, id). _AMENDMENT_OF_ENTRY = { diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 1ed821573..67d29f63e 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -226,12 +226,13 @@ #: gate fields moved). Max's d637 ruling (2026-09-30, section 16b) moves #: them again, one reviewed commit per ratified amendment #: (``test_d637_moved_only_the_ruled_gate_fields`` pins every gate field -#: those commits moved against the registries d1090 left): amendment 2 -#: moved the income bytes (the later spouse age and sex records became -#: slot metadata). +#: those commits moved against the registries d1090 left): amendments 2 +#: and 3 moved the income bytes (the later spouse age and sex records +#: became slot metadata; the spouse-retirement crosswalks became +#: non-executable metadata). REGISTRY_SHA256: dict[str, str] = { "income": ( - "8cd237e291ef746c98cfa0454ebaca865420fc4e729f55da6d148c74f15107ae" + "ac8310a166a66fa71a39d19cb79dec0dda5c41ff5a7033e535d70ebffe87204c" ), "wealth": ( "3a22247849ea2eccbef6e522c0d74c901beb90195f92fd2e7b23b123fab6b816" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index a21f1176b..576f7932a 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -40,8 +40,8 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| | `unit` | 7,338 | -| `artifact` | 4,093 | +| `artifact` | 4,098 | | `integration_psid` | 1,386 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,645** | +| **Total** | **13,650** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 8efd5bf54..d2aa6f878 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -2,7 +2,7 @@ "schema_version": 1, "counts": { "unit": 7338, - "artifact": 4093, + "artifact": 4098, "integration_psid": 1386, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 6774ee69a..6b33e466b 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -59,9 +59,9 @@ def quoted_spans(markdown): # 16c) replaced all eight F refusals (roles 7, pension 1). The d637 # registry commits (2026-09-30 ruling; spec section 16b) resolve the A # items of amendments 2, 3, 5 and 6: so far amendment 2's ten income -# records. +# records and amendment 3's six. EXPECTED_COUNTS = { - "income": (758, 0, 6), + "income": (764, 0, 0), "wealth": (234, 0, 0), "individual": (59, 0, 0), "pension": (647, 0, 1), @@ -72,11 +72,11 @@ def quoted_spans(markdown): } SSI_AND_CENSUS_RESOLVED = 26 #: TO VERIFY records left: the adjudication's 24 A items less those the -#: d637 commits resolved (amendment 2: 10). -OPEN_ITEMS = 14 +#: d637 commits resolved (amendment 2: 10; amendment 3: 6). +OPEN_ITEMS = 8 #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. -D637_APPLIED = {"2": "A"} +D637_APPLIED = {"2": "A", "3": "A"} D_ITEMS = { "wealth": { @@ -1096,8 +1096,9 @@ def test_section_16a_recommendations_and_options(): "refusals with \N{SECTION SIGN}16c's exclusions and released their " "dependents. The d637 commits, one per ratified amendment, have so " "far resolved amendment 2's ten later spouse age and sex records as " - "slot metadata. Until the rest of them (and, for code 88, d745) " - "land, the 14 TO VERIFY records stay open and the " + "slot metadata and amendment 3's six spouse-retirement crosswalks " + "as non-executable metadata. Until the rest of them (and, for code " + "88, d745) land, the 8 TO VERIFY records stay open and the " "2017\N{EN DASH}2023 P64/P65 records stay blocked.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index b28bbc762..48bab5d54 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -661,6 +661,38 @@ def _copy_ruling(document, source, target): action="refuse_slot_inference" ), ), + "a3_ruling_on_a_component": ( + "income", + lambda d: _copy_ruling( + d, + "income.2017.wife_retirement_annuities", + "income.2017.wife_annuities", + ), + ), + "a3_made_executable": ( + "income", + lambda d: _entry(d, "income.2019.wife_retirement_annuities").update( + kind="variable" + ), + ), + "a3_usage_rewritten": ( + "income", + lambda d: _entry(d, "income.2013.wife_retirement_annuities").update( + usage="Executable scalar crosswalk" + ), + ), + "a3_ruling_dropped": ( + "income", + lambda d: _entry(d, "income.2023.wife_retirement_annuities").pop( + "ruling" + ), + ), + "a3_ruling_as_amendment_2": ( + "income", + lambda d: _entry(d, "income.2015.wife_retirement_annuities")[ + "ruling" + ].update(amendment="2"), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 4c8969abd..745628118 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -288,13 +288,20 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "ruling", "superseded_question", }, + "3": { + "status", + "use_restriction", + "resolution", + "ruling", + "superseded_question", + }, } #: The dependencies each amendment released. D637_RELEASED: dict[str, frozenset[str]] = {} #: The top-level fields each registry's d637 commits changed. D637_TOP = {"income": {"rulings_applied"}} #: The amendments each registry's ``rulings_applied`` d637 record lists. -D637_RECORDED = {"income": ["2"]} +D637_RECORDED = {"income": ["2", "3"]} def test_d637_moved_only_the_ruled_gate_fields(): @@ -554,11 +561,24 @@ def test_wealth1_identity_counts_a_broken_identity(u2_inputs, declared_gate): ) -def test_crosswalks_are_never_executable_inputs(declared_gate): +def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): with pytest.raises(sources.U2SourceRefusal, match="documentary"): sources.field_specs( "income", 2019, ["wife_retirement_annuities"], declared_gate ) + # Amendment 3 (ratified by d637, section 16b) resolved every + # spouse-retirement crosswalk as non-executable metadata: the registry + # gate now passes the entry, and the adapter still refuses it as an + # input in every wave. + for wave in sources.SUPPORT_WAVES: + key = f"income.{wave}.wife_retirement_annuities" + assert registry.require_resolved("income", key)["kind"] == ( + "historical_route_crosswalk" + ) + with pytest.raises(sources.U2SourceRefusal, match="documentary"): + sources.field_specs( + "income", wave, ["wife_retirement_annuities"], registry_gate + ) with pytest.raises(sources.U2SourceRefusal, match="documentary"): sources.field_specs( "wealth", From c1c567c29a230f301b96fdb1bcb4556276b98d27 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:26:53 -0400 Subject: [PATCH 03/21] Apply d637 amendment 4 to the U2 source registries Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 4 (wealth accuracy-flag erratum) as written. Its registry effect is "None; already recorded": the nine corrected targets were resolved from documents by the independent adjudication (D). This commit records the ruling where the registries and master record still said "pending Max's ruling". - wealth.json: the nine flags (2013-2017 wealth2_acc and checking_saving_acc, 2019-2023 wealth2_acc) record the ruling (d637, amendment 4), and their use_restriction names the ratification in place of "pending Max's ruling". Resolutions, targets and the preserved codebook wording are unchanged. A d637 rulings_applied record is added. - u2_source_registry: amendment 4's scope pins the nine entries and their exact use_restriction. - sources: the wealth byte pin moves. - No U2 path reads these nine flags (only wealth1_acc, in diagnostics); no count, row or loader blocker moves (69). - Master record: the wealth blocker row, the d637 section and the nine rows. Specification section 17 item 2 and section 19 record it; the spec is re-pinned. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- data/external/track_u2/wealth.json | 85 ++++++++++++++++--- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 21 ++--- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 20 +++++ .../uniform_cut_track_u2/sources.py | 5 +- tests/README-tiers.md | 4 +- tests/data/test_track_u2_income_wealth.py | 8 ++ tests/tier_counts.json | 2 +- tests/track_u2/test_adjudication_applied.py | 9 +- tests/track_u2/test_source_registries.py | 28 ++++++ tests/track_u2/test_u2_mappings.py | 10 ++- 13 files changed, 165 insertions(+), 33 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 58e0469ee..e274552fd 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "39e2abc4d353e28a046e9444e6afeead55ab4072b601dc7f73746bb0f62a0316", + "sha256": "2be8c56cdcbd0e5cbf5a4764fbefb1e170c5c2427dfe7b6ddfecfe3cc75bf9bf", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/data/external/track_u2/wealth.json b/data/external/track_u2/wealth.json index e974506c3..37d33776d 100644 --- a/data/external/track_u2/wealth.json +++ b/data/external/track_u2/wealth.json @@ -743,12 +743,18 @@ "codebook_states_accuracy_of": "ER58209", "resolved_accuracy_for_variable": "ER58211" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 88, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -864,12 +870,18 @@ "codebook_states_accuracy_of": "ER581614", "resolved_accuracy_for_variable": "ER58161" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 89, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -2158,12 +2170,18 @@ "codebook_states_accuracy_of": "ER65406", "resolved_accuracy_for_variable": "ER65408" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 90, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -2279,12 +2297,18 @@ "codebook_states_accuracy_of": "ER653584", "resolved_accuracy_for_variable": "ER65358" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 91, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -3577,12 +3601,18 @@ "codebook_states_accuracy_of": "ER71483", "resolved_accuracy_for_variable": "ER71485" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 92, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -3702,12 +3732,18 @@ "codebook_states_accuracy_of": "ER714354", "resolved_accuracy_for_variable": "ER71435" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 93, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -5032,12 +5068,18 @@ "codebook_states_accuracy_of": "ER77509", "resolved_accuracy_for_variable": "ER77511" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 94, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -6516,12 +6558,18 @@ "codebook_states_accuracy_of": "ER81836", "resolved_accuracy_for_variable": "ER81838" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 95, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -8080,12 +8128,18 @@ "codebook_states_accuracy_of": "ER85690", "resolved_accuracy_for_variable": "ER85692" }, - "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 96, "disposition": "D", "amendment": 4 + }, + "use_restriction": "Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4" } }, { @@ -9010,5 +9064,16 @@ "bytes": 26564, "applied_by": "u2-m1b", "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." - } + }, + "rulings_applied": [ + { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendments": [ + "4" + ], + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 4 records the ruling on the nine corrected wealth accuracy-flag targets, whose use is limited to imputation diagnostics. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." + } + ] } diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 7f62aad44..39620203d 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata. Until the rest of them (and, for code 88, d745) land, the 8 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, and recorded amendment 4's ruling on the nine corrected wealth accuracy flags. Until the rest of them (and, for code 88, d745) land, the 8 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1842,6 +1842,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Each ratified amendment's registry effect (§16a, "Registry effect of the rulings") lands in its own reviewed commit. `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS`) pins each applied amendment's entries, ruling and gate fields, and `tests/track_u2/test_u2_mappings.py` checks every gate field these commits move against the registries the d1090 commit left, read from Git. Amendment 1's parts 1a–1c, and with them the six code-88 records, wait for Max's confirmation (d745). No line before §16a and no registry-cited line changes. - Amendment 2: the ten 2015–2023 family-file spouse age and sex records (`income..wife_age` and `wife_sex`) are RESOLVED as slot metadata only. Under their no-slot-inference rule, the variables and their 'Head is female or single male' or 'Reference Person is female or single male' wording never decide whether a person occupies the spouse slot; the in-family code-20 or code-22 person does, and annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age. The builder reads the spouse age only for the family file's slot flag, which the income rows must find equal to the roster's occupant or they refuse; a new invented variant covers the direction this amendment concerns, a family-file 'no spouse' code beside a roster occupant. The loader's source preflight now refuses on 69 blockers, down from 74. The master record counts 1778 RESOLVED, 0 REFUSED and 14 TO VERIFY records. - Amendment 3: the six `income..wife_retirement_annuities` crosswalk records (2013–2023) are RESOLVED as non-executable metadata. They keep their crosswalk kind, which the field adapter refuses as an input under either gate; no scalar equivalence is asserted between U1's combined item and the later separately documented spouse pension, annuity, IRA and other-retirement items, which stay inside total family money income under §4 and are never added a second time. No row changes and no loader blocker moves (69). The master record counts 1784 RESOLVED, 0 REFUSED and 8 TO VERIFY records. +- Amendment 4: the nine corrected wealth accuracy flags (adjudication D) already held their resolution; each now records the ruling, and its use restriction (imputation diagnostics only; the correction never changes an amount or a sample-inclusion decision) names the ratification in place of "pending Max's ruling", in `data/external/track_u2/wealth.json` and the master record's rows. No U2 path reads these flags, and no count or loader blocker moves. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index 49a25dde6..fc2144174 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -32,7 +32,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | 2017–2023 pension P64/P65 contract | RESOLVED (D); application waits for amendment 5 | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. The ratified §15 contract still lists account/formula/dk, so the dependent P64/P65 entries stay blocked until Max rules on amendment 5. | | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | -| Wealth accuracy-variable references | RESOLVED (D) | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics. | +| Wealth accuracy-variable references | RESOLVED (D); amendment 4 ratified by Max's ruling d637 | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics, and the corrections never change an amount or a sample-inclusion decision. | | Revised 2017 individual-weight construction | TO VERIFY (adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Options in amendment 6. | | 2023 weight documentation | RESOLVED | Original June 2026 report captured and SHA-256 pinned in `tests/data/track_u2/psid_sources/weights23_manifest.json`. | | SSI capture and notices | RESOLVED | Eleven annual notices, 88 historical CFR snapshots, seven revision-pinned YAML snapshots, constant checks and exact 2012 equality. `docs/design/u2_m1_ssi_adjudication.md` supplies all source hashes. The adjudication reconfirmed all 106 source hashes. | @@ -76,6 +76,7 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame |---|---:|---| | 2: annuitant input precedence | 10 | `income:income.{2015,2017,2019,2021,2023}.{wife_age,wife_sex}` are RESOLVED as slot metadata (`usage` `slot_metadata_only`), with a no-slot-inference rule in `use_restriction`: the variables and their inapplicability wording never decide who occupies the spouse slot, and annuity lives use each person's recorded sex and derived income-year age. The builder reads the spouse age only for the family file's slot flag, and the income rows refuse any observation where it disagrees with the roster's code-20 or code-22 occupant | | 3: historical spouse-retirement crosswalk | 6 | `income:income.{2013,2015,2017,2019,2021,2023}.wife_retirement_annuities` are RESOLVED as non-executable metadata: their `kind` stays `historical_route_crosswalk`, which the field adapter refuses as an input under either gate, and a `use_restriction` states that no input or row reads them and no scalar equivalence is inferred. No row changes and no loader blocker moves: no U2 run applies these records | +| 4: wealth accuracy-flag erratum | 9 | The nine corrected flags (`wealth:wealth.{2013,2015,2017}.{wealth2_acc,checking_saving_acc}` and `wealth:wealth.{2019,2021,2023}.wealth2_acc`) already held their documentary resolution (adjudication D); each now records the ruling, and its `use_restriction` (imputation diagnostics only; the correction never changes amounts or sample inclusion) names the ratification in place of "pending Max's ruling". No U2 path reads these nine flags, and no row or loader blocker moves | The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment 1c, pending d745), the 2015 P62A checkpoint and its dependents (11, amendment 5b), the 2017–2023 P64/P65 records and their inherited-route amendments (44, amendment 5a) and the 2017 weight with the individual weight that depends on it (2, amendment 6). @@ -866,10 +867,10 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `wealth:wealth.2013.other_real_estate_asset` | RESOLVED | ER58165: IMP VAL OTH REAL ESTATE ASSET (W2A) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1732`; `PSID/family/2013/FAM2013ER.sps:6988`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1842 | | `wealth:wealth.2013.other_real_estate_debt` | RESOLVED | ER58167: IMP VAL OTH REAL ESTATE DEBT (W2B) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1733`; `PSID/family/2013/FAM2013ER.sps:6990`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1842 | | `wealth:wealth.2013.other_debt` | RESOLVED | ER58205: IMP VAL OTHER DEBT (W38B7) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1745`; `PSID/family/2013/FAM2013ER.sps:7028`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1850 | -| `wealth:wealth.2013.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2013' identifies the flag's amount as ER58211 ('IMP WEALTH W/ EQUITY (WEALTH2) 2013'); the prose reference ER58209 is WEALTH1, the excluding-equity amount, while the flag's own parenthetical reads 'CONSTRUCTED WEALTH VAR, INCLUDING EQUITY'. The flag is the accuracy code for ER58211. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2013/FAM2013ER.sps:1748`; `PSID/family/2013/FAM2013ER.sps:7035`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1852; `EV/phase2-20260927/out/u2-adjudicate.md:88` | +| `wealth:wealth.2013.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2013' identifies the flag's amount as ER58211 ('IMP WEALTH W/ EQUITY (WEALTH2) 2013'); the prose reference ER58209 is WEALTH1, the excluding-equity amount, while the flag's own parenthetical reads 'CONSTRUCTED WEALTH VAR, INCLUDING EQUITY'. The flag is the accuracy code for ER58211. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2013/FAM2013ER.sps:1748`; `PSID/family/2013/FAM2013ER.sps:7035`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1852; `EV/phase2-20260927/out/u2-adjudicate.md:88` | | `wealth:wealth.2013.home_equity_acc` | RESOLVED | ER58208: ACC VALUE HOME EQUITY 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1746`; `PSID/family/2013/FAM2013ER.sps:7031`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1851 | | `wealth:wealth.2013.vehicles_acc` | RESOLVED | ER58174: ACC VALUE VEHICLES (W6) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1735`; `PSID/family/2013/FAM2013ER.sps:6997`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1844 | -| `wealth:wealth.2013.checking_saving_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC VAL CHECKING/SAVING (W28) 2013' identifies the flag's amount as ER58161 ('IMP VAL CHECKING/SAVING (W28) 2013'); the prose reference ER581614 is not a variable in FAM2013ER.sps (zero occurrences). The flag is the accuracy code for ER58161. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2013/FAM2013ER.sps:1731`; `PSID/family/2013/FAM2013ER.sps:6985`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1841; `EV/phase2-20260927/out/u2-adjudicate.md:89` | +| `wealth:wealth.2013.checking_saving_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC VAL CHECKING/SAVING (W28) 2013' identifies the flag's amount as ER58161 ('IMP VAL CHECKING/SAVING (W28) 2013'); the prose reference ER581614 is not a variable in FAM2013ER.sps (zero occurrences). The flag is the accuracy code for ER58161. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2013/FAM2013ER.sps:1731`; `PSID/family/2013/FAM2013ER.sps:6985`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1841; `EV/phase2-20260927/out/u2-adjudicate.md:89` | | `wealth:wealth.2013.stocks_acc` | RESOLVED | ER58172: ACC VALUE STOCKS (W16) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1734`; `PSID/family/2013/FAM2013ER.sps:6995`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1843 | | `wealth:wealth.2013.other_assets_acc` | RESOLVED | ER58178: ACC VALUE OTH ASSETS (W34) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1736`; `PSID/family/2013/FAM2013ER.sps:7001`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1845 | | `wealth:wealth.2013.ira_annuity_acc` | RESOLVED | ER58182: ACC VALUE ANNUITY/IRA (W22) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1738`; `PSID/family/2013/FAM2013ER.sps:7005`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1846 | @@ -904,10 +905,10 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `wealth:wealth.2015.other_real_estate_asset` | RESOLVED | ER65362: IMP VAL OTH REAL ESTATE ASSET (W2A) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1798`; `PSID/family/2015/FAM2015ER.sps:7258`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1981 | | `wealth:wealth.2015.other_real_estate_debt` | RESOLVED | ER65364: IMP VAL OTH REAL ESTATE DEBT (W2B) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1798`; `PSID/family/2015/FAM2015ER.sps:7260`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1981 | | `wealth:wealth.2015.other_debt` | RESOLVED | ER65402: IMP VAL OTHER DEBT (W38B7) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1811`; `PSID/family/2015/FAM2015ER.sps:7298`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1991 | -| `wealth:wealth.2015.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2015' identifies the flag's amount as ER65408 ('IMP WEALTH W/ EQUITY (WEALTH2) 2015'); the prose reference ER65406 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER65408. The flag is the accuracy code for ER65408. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2015/FAM2015ER.sps:1813`; `PSID/family/2015/FAM2015ER.sps:7305`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1992; `EV/phase2-20260927/out/u2-adjudicate.md:90` | +| `wealth:wealth.2015.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2015' identifies the flag's amount as ER65408 ('IMP WEALTH W/ EQUITY (WEALTH2) 2015'); the prose reference ER65406 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER65408. The flag is the accuracy code for ER65408. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2015/FAM2015ER.sps:1813`; `PSID/family/2015/FAM2015ER.sps:7305`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1992; `EV/phase2-20260927/out/u2-adjudicate.md:90` | | `wealth:wealth.2015.home_equity_acc` | RESOLVED | ER65405: ACC VALUE HOME EQUITY 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1812`; `PSID/family/2015/FAM2015ER.sps:7301`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1991 | | `wealth:wealth.2015.vehicles_acc` | RESOLVED | ER65371: ACC VALUE VEHICLES (W6) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1801`; `PSID/family/2015/FAM2015ER.sps:7267`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1983 | -| `wealth:wealth.2015.checking_saving_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC VAL CHECKING/SAVING (W28) 2015' identifies the flag's amount as ER65358 ('IMP VAL CHECKING/SAVING (W28) 2015'); the prose reference ER653584 is not a variable in FAM2015ER.sps (zero occurrences). The flag is the accuracy code for ER65358. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2015/FAM2015ER.sps:1797`; `PSID/family/2015/FAM2015ER.sps:7255`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1980; `EV/phase2-20260927/out/u2-adjudicate.md:91` | +| `wealth:wealth.2015.checking_saving_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC VAL CHECKING/SAVING (W28) 2015' identifies the flag's amount as ER65358 ('IMP VAL CHECKING/SAVING (W28) 2015'); the prose reference ER653584 is not a variable in FAM2015ER.sps (zero occurrences). The flag is the accuracy code for ER65358. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2015/FAM2015ER.sps:1797`; `PSID/family/2015/FAM2015ER.sps:7255`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1980; `EV/phase2-20260927/out/u2-adjudicate.md:91` | | `wealth:wealth.2015.stocks_acc` | RESOLVED | ER65369: ACC VALUE STOCKS (W16) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1800`; `PSID/family/2015/FAM2015ER.sps:7265`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1983 | | `wealth:wealth.2015.other_assets_acc` | RESOLVED | ER65375: ACC VALUE OTH ASSETS (W34) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1802`; `PSID/family/2015/FAM2015ER.sps:7271`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1984 | | `wealth:wealth.2015.ira_annuity_acc` | RESOLVED | ER65379: ACC VALUE ANNUITY/IRA (W22) 2015. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1803`; `PSID/family/2015/FAM2015ER.sps:7275`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1985 | @@ -942,10 +943,10 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `wealth:wealth.2017.other_real_estate_asset` | RESOLVED | ER71439: IMP VAL OTH REAL ESTATE ASSET (W2A) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1823`; `PSID/family/2017/FAM2017ER.sps:7363`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2033 | | `wealth:wealth.2017.other_real_estate_debt` | RESOLVED | ER71441: IMP VAL OTH REAL ESTATE DEBT (W2B) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1824`; `PSID/family/2017/FAM2017ER.sps:7365`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2034 | | `wealth:wealth.2017.other_debt` | RESOLVED | ER71479: IMP VAL OTHER DEBT (W38B7) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1837`; `PSID/family/2017/FAM2017ER.sps:7403`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2044 | -| `wealth:wealth.2017.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2017' identifies the flag's amount as ER71485 ('IMP WEALTH W/ EQUITY (WEALTH2) 2017'); the prose reference ER71483 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER71485. The flag is the accuracy code for ER71485. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2017/FAM2017ER.sps:1839`; `PSID/family/2017/FAM2017ER.sps:7410`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2045; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2046; `EV/phase2-20260927/out/u2-adjudicate.md:92` | +| `wealth:wealth.2017.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2017' identifies the flag's amount as ER71485 ('IMP WEALTH W/ EQUITY (WEALTH2) 2017'); the prose reference ER71483 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER71485. The flag is the accuracy code for ER71485. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2017/FAM2017ER.sps:1839`; `PSID/family/2017/FAM2017ER.sps:7410`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2045; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2046; `EV/phase2-20260927/out/u2-adjudicate.md:92` | | `wealth:wealth.2017.home_equity_acc` | RESOLVED | ER71482: ACC VALUE HOME EQUITY 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1838`; `PSID/family/2017/FAM2017ER.sps:7406`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2044 | | `wealth:wealth.2017.vehicles_acc` | RESOLVED | ER71448: ACC VALUE VEHICLES (W6) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1826`; `PSID/family/2017/FAM2017ER.sps:7372`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2036 | -| `wealth:wealth.2017.checking_saving_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC VAL CHECKING/SAVING (W28) 2017' identifies the flag's amount as ER71435 ('IMP VAL CHECKING/SAVING (W28) 2017'); the prose reference ER714354 is not a variable in FAM2017ER.sps (zero occurrences). The flag is the accuracy code for ER71435. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2017/FAM2017ER.sps:1822`; `PSID/family/2017/FAM2017ER.sps:7360`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2033; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2032; `EV/phase2-20260927/out/u2-adjudicate.md:93` | +| `wealth:wealth.2017.checking_saving_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC VAL CHECKING/SAVING (W28) 2017' identifies the flag's amount as ER71435 ('IMP VAL CHECKING/SAVING (W28) 2017'); the prose reference ER714354 is not a variable in FAM2017ER.sps (zero occurrences). The flag is the accuracy code for ER71435. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2017/FAM2017ER.sps:1822`; `PSID/family/2017/FAM2017ER.sps:7360`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2033; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2032; `EV/phase2-20260927/out/u2-adjudicate.md:93` | | `wealth:wealth.2017.stocks_acc` | RESOLVED | ER71446: ACC VALUE STOCKS (W16) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1826`; `PSID/family/2017/FAM2017ER.sps:7370`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2035 | | `wealth:wealth.2017.other_assets_acc` | RESOLVED | ER71452: ACC VALUE OTH ASSETS (W34) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1828`; `PSID/family/2017/FAM2017ER.sps:7376`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2037 | | `wealth:wealth.2017.ira_annuity_acc` | RESOLVED | ER71456: ACC VALUE ANNUITY/IRA (W22) 2017. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1829`; `PSID/family/2017/FAM2017ER.sps:7380`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2038 | @@ -981,7 +982,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `wealth:wealth.2019.other_real_estate_debt` | RESOLVED | ER77467: IMP VAL OTH REAL ESTATE DEBT (W2B) 2019. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1833`; `PSID/family/2019/FAM2019ER.sps:7395`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2020 | | `wealth:wealth.2019.other_debt` | RESOLVED | ER77505: IMP VAL OTHER DEBT (W38B7) 2019. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1845`; `PSID/family/2019/FAM2019ER.sps:7433`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2029 | | `wealth:wealth.2019.cd_bonds_treasury` | RESOLVED | ER77461: IMP VAL CD/BONDS/TB (W28) 2019. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1831`; `PSID/family/2019/FAM2019ER.sps:7389`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2018 | -| `wealth:wealth.2019.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2019' identifies the flag's amount as ER77511 ('IMP WEALTH W/ EQUITY (WEALTH2) 2019'); the prose reference ER77509 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER77511. The flag is the accuracy code for ER77511. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2019/FAM2019ER.sps:1848`; `PSID/family/2019/FAM2019ER.sps:7440`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2031; `EV/phase2-20260927/out/u2-adjudicate.md:94` | +| `wealth:wealth.2019.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2019' identifies the flag's amount as ER77511 ('IMP WEALTH W/ EQUITY (WEALTH2) 2019'); the prose reference ER77509 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER77511. The flag is the accuracy code for ER77511. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2019/FAM2019ER.sps:1848`; `PSID/family/2019/FAM2019ER.sps:7440`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2031; `EV/phase2-20260927/out/u2-adjudicate.md:94` | | `wealth:wealth.2019.home_equity_acc` | RESOLVED | ER77508: ACC VALUE HOME EQUITY 2019. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1846`; `PSID/family/2019/FAM2019ER.sps:7436`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2030 | | `wealth:wealth.2019.vehicles_acc` | RESOLVED | ER77474: ACC VALUE VEHICLES (W6) 2019. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1835`; `PSID/family/2019/FAM2019ER.sps:7402`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2022 | | `wealth:wealth.2019.checking_saving_acc` | RESOLVED | ER77458: ACC VAL CHECKING/SAVING (W28A) 2019. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1830`; `PSID/family/2019/FAM2019ER.sps:7386`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2017 | @@ -1021,7 +1022,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `wealth:wealth.2021.other_real_estate_debt` | RESOLVED | ER81794: IMP VAL OTH REAL ESTATE DEBT (W2B) 2021. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1275`; `PSID/family/2021/FAM2021ER.sps:5174`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1384 | | `wealth:wealth.2021.other_debt` | RESOLVED | ER81832: IMP VAL OTHER DEBT (W38B7) 2021. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1288`; `PSID/family/2021/FAM2021ER.sps:5212`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1393 | | `wealth:wealth.2021.cd_bonds_treasury` | RESOLVED | ER81788: IMP VAL CD/BONDS/TB (W28) 2021. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1273`; `PSID/family/2021/FAM2021ER.sps:5168`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1382 | -| `wealth:wealth.2021.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2021' identifies the flag's amount as ER81838 ('IMP WEALTH W/ EQUITY (WEALTH2) 2021'); the prose reference ER81836 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER81838. The flag is the accuracy code for ER81838. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2021/FAM2021ER.sps:1290`; `PSID/family/2021/FAM2021ER.sps:5219`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1395; `PSID/family/2021/FAM2021ER_formats.sps:37472`; `EV/phase2-20260927/out/u2-adjudicate.md:95` | +| `wealth:wealth.2021.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2021' identifies the flag's amount as ER81838 ('IMP WEALTH W/ EQUITY (WEALTH2) 2021'); the prose reference ER81836 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER81838. The flag is the accuracy code for ER81838. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2021/FAM2021ER.sps:1290`; `PSID/family/2021/FAM2021ER.sps:5219`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1395; `PSID/family/2021/FAM2021ER_formats.sps:37472`; `EV/phase2-20260927/out/u2-adjudicate.md:95` | | `wealth:wealth.2021.home_equity_acc` | RESOLVED | ER81835: ACC VALUE HOME EQUITY 2021. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1289`; `PSID/family/2021/FAM2021ER.sps:5215`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1394; `PSID/family/2021/FAM2021ER_formats.sps:37460` | | `wealth:wealth.2021.vehicles_acc` | RESOLVED | ER81801: ACC VALUE VEHICLES (W6) 2021. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1277`; `PSID/family/2021/FAM2021ER.sps:5181`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1386; `PSID/family/2021/FAM2021ER_formats.sps:37310` | | `wealth:wealth.2021.checking_saving_acc` | RESOLVED | ER81785: ACC VAL CHECKING/SAVING (W28A) 2021. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1272`; `PSID/family/2021/FAM2021ER.sps:5165`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1381; `PSID/family/2021/FAM2021ER_formats.sps:37244` | @@ -1061,7 +1062,7 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `wealth:wealth.2023.other_real_estate_debt` | RESOLVED | ER85648: IMP VAL OTH REAL ESTATE DEBT (W2B) 2023. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1226`; `PSID/family/2023/FAM2023ER.sps:4979`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1302 | | `wealth:wealth.2023.other_debt` | RESOLVED | ER85686: IMP VAL OTHER DEBT (W38B7) 2023. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1239`; `PSID/family/2023/FAM2023ER.sps:5017`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1311 | | `wealth:wealth.2023.cd_bonds_treasury` | RESOLVED | ER85642: IMP VAL CD/BONDS/TB (W28) 2023. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1224`; `PSID/family/2023/FAM2023ER.sps:4973`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1300 | -| `wealth:wealth.2023.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2023' identifies the flag's amount as ER85692 ('IMP WEALTH W/ EQUITY (WEALTH2) 2023'); the prose reference ER85690 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER85692. The flag is the accuracy code for ER85692. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, pending Max's ruling). | `PSID/family/2023/FAM2023ER.sps:1241`; `PSID/family/2023/FAM2023ER.sps:5024`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1313; `PSID/family/2023/FAM2023ER_formats.sps:35056`; `EV/phase2-20260927/out/u2-adjudicate.md:96` | +| `wealth:wealth.2023.wealth2_acc` | RESOLVED | Independent adjudication D (amendment 4): the setup label 'ACC WEALTH W/ EQUITY (WEALTH2) 2023' identifies the flag's amount as ER85692 ('IMP WEALTH W/ EQUITY (WEALTH2) 2023'); the prose reference ER85690 is WEALTH1, while the flag's own parenthetical reads 'Constructed Wealth Variable, Including Equity', which describes ER85692. The flag is the accuracy code for ER85692. The contradictory literal codebook wording is kept in codebook_text and source_wording_conflict. This establishes the intended association, not PSID's imputation algorithm. Imputation diagnostics only; the correction never changes amounts or sample inclusion (u2-draft-4 amendment 4, ratified by Max on 2026-09-30: d637, specification section 16b). | `PSID/family/2023/FAM2023ER.sps:1241`; `PSID/family/2023/FAM2023ER.sps:5024`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1313; `PSID/family/2023/FAM2023ER_formats.sps:35056`; `EV/phase2-20260927/out/u2-adjudicate.md:96` | | `wealth:wealth.2023.home_equity_acc` | RESOLVED | ER85689: ACC VALUE HOME EQUITY 2023. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1240`; `PSID/family/2023/FAM2023ER.sps:5020`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1312; `PSID/family/2023/FAM2023ER_formats.sps:35044` | | `wealth:wealth.2023.vehicles_acc` | RESOLVED | ER85655: ACC VALUE VEHICLES (W6) 2023. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1229`; `PSID/family/2023/FAM2023ER.sps:4986`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1304; `PSID/family/2023/FAM2023ER_formats.sps:34894` | | `wealth:wealth.2023.checking_saving_acc` | RESOLVED | ER85639: ACC VAL CHECKING/SAVING (W28A) 2023. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1223`; `PSID/family/2023/FAM2023ER.sps:4970`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1299; `PSID/family/2023/FAM2023ER_formats.sps:34828` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 86b49be6f..0a63202ee 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -396,6 +396,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 2: `data/external/track_u2/income.json` (the ten later spouse age and sex records and a d637 `rulings_applied` record) and `u1_identity.json` (the specification pin); `docs/design/boomers2004_1946_55_comparison.md` (§17 item 2 and §19); `docs/design/u2_m1_source_adjudication.md` (summary counts and table, the spouse age/sex blocker row, the d1090 section's closing sentence, a d637 section and the ten rows); `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS` and its validation); `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the income byte pin and two docstrings) and `invented.py` (one new named variant); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py` and `test_u2_scripts.py`; `tests/tier_counts.json` and `tests/README-tiers.md`; and this section. - Amendment 3: `income.json` (the six crosswalk records and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the crosswalk blocker row, the d637 section and the six rows); `u2_source_registry.py` (amendment 3's scope) and `sources.py` (the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. +- Amendment 4: `wealth.json` (the nine flags and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the wealth blocker row, the d637 section and the nine rows); `u2_source_registry.py` (amendment 4's scope) and `sources.py` (the wealth byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`, and `tests/data/test_track_u2_income_wealth.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 99b7bb034..58594d539 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -118,6 +118,26 @@ }, "absent": ("action", "question"), }, + "4": { + "registry": "wealth", + "entries": tuple( + f"wealth.{wave}.{route}" + for wave in (2013, *_LATER_WAVES) + for route in ("wealth2_acc", "checking_saving_acc") + if route == "wealth2_acc" or wave < 2019 + ), + "ruling": {**_D637, "amendment": "4"}, + "fields": { + "status": "RESOLVED", + "use_restriction": ( + "Imputation diagnostics only; the correction never changes " + "amounts or sample inclusion (u2-draft-4 amendment 4, " + "ratified by Max on 2026-09-30: d637, specification section " + "16b)." + ), + }, + "absent": ("action", "blocking_dependencies", "question"), + }, } #: The d637 amendment each ruled entry belongs to, by (registry, id). _AMENDMENT_OF_ENTRY = { diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 67d29f63e..038f174a3 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -229,13 +229,14 @@ #: those commits moved against the registries d1090 left): amendments 2 #: and 3 moved the income bytes (the later spouse age and sex records #: became slot metadata; the spouse-retirement crosswalks became -#: non-executable metadata). +#: non-executable metadata) and amendment 4 the wealth bytes (the nine +#: corrected accuracy flags record the ruling). REGISTRY_SHA256: dict[str, str] = { "income": ( "ac8310a166a66fa71a39d19cb79dec0dda5c41ff5a7033e535d70ebffe87204c" ), "wealth": ( - "3a22247849ea2eccbef6e522c0d74c901beb90195f92fd2e7b23b123fab6b816" + "e2d24c2665db23c24f9313dd44876c35a22b7e67b4d0acfad1c65b48c64e6602" ), "individual": ( "4eab8e0abc69668c287310931baa049b8546d3f3e265a106f1b945b948c7649e" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 576f7932a..0d7543bf9 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -40,8 +40,8 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| | `unit` | 7,338 | -| `artifact` | 4,098 | +| `artifact` | 4,102 | | `integration_psid` | 1,386 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,650** | +| **Total** | **13,654** | diff --git a/tests/data/test_track_u2_income_wealth.py b/tests/data/test_track_u2_income_wealth.py index 7f451f66f..add59c10b 100644 --- a/tests/data/test_track_u2_income_wealth.py +++ b/tests/data/test_track_u2_income_wealth.py @@ -115,6 +115,14 @@ def test_documentary_accuracy_conflicts_are_preserved(): amount = by_id[entry["id"].removesuffix("_acc")] assert amount["variable"] == target assert "never changes amounts" in entry["use_restriction"] + # Max ratified amendment 4 on 2026-09-30 (d637; spec 16b). + assert "pending Max's ruling" not in entry["use_restriction"] + assert entry["ruling"] == { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "4", + } resolved += 1 assert resolved == 9 diff --git a/tests/tier_counts.json b/tests/tier_counts.json index d2aa6f878..23a30eb70 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -2,7 +2,7 @@ "schema_version": 1, "counts": { "unit": 7338, - "artifact": 4098, + "artifact": 4102, "integration_psid": 1386, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 6b33e466b..115a2065d 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -76,7 +76,7 @@ def quoted_spans(markdown): OPEN_ITEMS = 8 #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. -D637_APPLIED = {"2": "A", "3": "A"} +D637_APPLIED = {"2": "A", "3": "A", "4": "D"} D_ITEMS = { "wealth": { @@ -1097,9 +1097,10 @@ def test_section_16a_recommendations_and_options(): "dependents. The d637 commits, one per ratified amendment, have so " "far resolved amendment 2's ten later spouse age and sex records as " "slot metadata and amendment 3's six spouse-retirement crosswalks " - "as non-executable metadata. Until the rest of them (and, for code " - "88, d745) land, the 8 TO VERIFY records stay open and the " - "2017\N{EN DASH}2023 P64/P65 records stay blocked.", + "as non-executable metadata, and recorded amendment 4's ruling on " + "the nine corrected wealth accuracy flags. Until the rest of them " + "(and, for code 88, d745) land, the 8 TO VERIFY records stay open " + "and the 2017\N{EN DASH}2023 P64/P65 records stay blocked.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 48bab5d54..2f541263f 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -693,6 +693,34 @@ def _copy_ruling(document, source, target): "ruling" ].update(amendment="2"), ), + "a4_ruling_on_wealth1_acc": ( + "wealth", + lambda d: _copy_ruling( + d, "wealth.2013.wealth2_acc", "wealth.2013.wealth1_acc" + ), + ), + "a4_restriction_reverted": ( + "wealth", + lambda d: _entry(d, "wealth.2015.checking_saving_acc").update( + use_restriction=_entry(d, "wealth.2015.checking_saving_acc")[ + "use_restriction" + ].replace( + "ratified by Max on 2026-09-30: d637, specification section " + "16b", + "pending Max's ruling", + ) + ), + ), + "a4_restriction_widened": ( + "wealth", + lambda d: _entry(d, "wealth.2023.wealth2_acc").update( + use_restriction="Imputation and sample selection" + ), + ), + "a4_ruling_dropped": ( + "wealth", + lambda d: _entry(d, "wealth.2019.wealth2_acc").pop("ruling"), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 745628118..de263f7c0 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -295,13 +295,19 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "ruling", "superseded_question", }, + # Amendment 4's nine D items already held their resolution: the + # ruling and the ratified restriction wording move. + "4": {"use_restriction", "ruling"}, } #: The dependencies each amendment released. D637_RELEASED: dict[str, frozenset[str]] = {} #: The top-level fields each registry's d637 commits changed. -D637_TOP = {"income": {"rulings_applied"}} +D637_TOP = { + "income": {"rulings_applied"}, + "wealth": {"rulings_applied"}, +} #: The amendments each registry's ``rulings_applied`` d637 record lists. -D637_RECORDED = {"income": ["2", "3"]} +D637_RECORDED = {"income": ["2", "3"], "wealth": ["4"]} def test_d637_moved_only_the_ruled_gate_fields(): From 00c0a32f858e912079008abfc59339fc8d2002d4 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:32:03 -0400 Subject: [PATCH 04/21] Follow d637 amendment 5 in the declared U2 employer-DC route Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written, including its replacement for section 15's income_concept.employer_dc.previous_routes. Before the registry commits for 5b and 5a release the 2015-2023 routes, the declared route, which only invented data use, moves to the ratified one, so the registry and declared gates will agree on every wave. - sources: DECLARED_PREVIOUS_ACCOUNT_ITEMS holds the fragment's account_items_by_wave (2013 account, formula, DK; 2015-2023 account only) and DC_ITEM_PLAN_TYPES its plan-type codes (5, 1, 8); declared_dc_route(wave) builds the declared route, and dc_route's declared branch uses it. DECLARED_DC_ROUTE_TYPES keeps only the wave-independent current-job and combined-plan types. - invented: the DC frames use the declared route of their wave, so the invented 2015-2023 formula and DK previous-plan items become off-route (counted under that disposition, never zero-filled). - Section 15's text is not edited: sections 3-15 keep draft 3's text until section 20 step 5 materializes the final parameter block (sections 16b, 16c; section 17 item 9), and where a ratified amendment and sections 3-15 differ the amendment governs. A new test holds the declared route equal to the fragment parsed from section 16a, and section 15's block to draft 3's generic route, so the materialization must change both together. Section 19 records the choice. - The package docstring no longer calls section 16a's amendments pending. Tests: the declared account-item route is checked per wave and plan type; the U1 2013 equality is kept. No registry entry changes; the loader still refuses on 69 blockers. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1b_exposure.md | 1 + .../uniform_cut_track_u2/__init__.py | 9 +- .../uniform_cut_track_u2/invented.py | 18 ++-- .../uniform_cut_track_u2/sources.py | 86 ++++++++++++----- tests/README-tiers.md | 4 +- tests/tier_counts.json | 2 +- tests/track_u2/test_u2_employer_dc.py | 95 +++++++++++++++---- 9 files changed, 159 insertions(+), 59 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index e274552fd..36752c980 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "2be8c56cdcbd0e5cbf5a4764fbefb1e170c5c2427dfe7b6ddfecfe3cc75bf9bf", + "sha256": "e496a49affc0c56bd9736f398c2f58c2c4bfe087d2aafe3c9fdb8f572c579285", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 39620203d..ff2bcecce 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1843,6 +1843,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 2: the ten 2015–2023 family-file spouse age and sex records (`income..wife_age` and `wife_sex`) are RESOLVED as slot metadata only. Under their no-slot-inference rule, the variables and their 'Head is female or single male' or 'Reference Person is female or single male' wording never decide whether a person occupies the spouse slot; the in-family code-20 or code-22 person does, and annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age. The builder reads the spouse age only for the family file's slot flag, which the income rows must find equal to the roster's occupant or they refuse; a new invented variant covers the direction this amendment concerns, a family-file 'no spouse' code beside a roster occupant. The loader's source preflight now refuses on 69 blockers, down from 74. The master record counts 1778 RESOLVED, 0 REFUSED and 14 TO VERIFY records. - Amendment 3: the six `income..wife_retirement_annuities` crosswalk records (2013–2023) are RESOLVED as non-executable metadata. They keep their crosswalk kind, which the field adapter refuses as an input under either gate; no scalar equivalence is asserted between U1's combined item and the later separately documented spouse pension, annuity, IRA and other-retirement items, which stay inside total family money income under §4 and are never added a second time. No row changes and no loader blocker moves (69). The master record counts 1784 RESOLVED, 0 REFUSED and 8 TO VERIFY records. - Amendment 4: the nine corrected wealth accuracy flags (adjudication D) already held their resolution; each now records the ruling, and its use restriction (imputation diagnostics only; the correction never changes an amount or a sample-inclusion decision) names the ratification in place of "pending Max's ruling", in `data/external/track_u2/wealth.json` and the master record's rows. No U2 path reads these flags, and no count or loader blocker moves. +- Amendment 5, before its registry commits: the declared employer-DC route, which only invented data use, follows the ratified replacement fragment printed in §16a. A previous plan's account items count for an account plan (P46 = 5) in every wave and, in 2013 only, for a formula or DK plan too; otherwise they are off-route. §15's own `previous_routes` keeps draft 3's text, because §§3–15 keep it until §20 step 5 materializes the final parameter block (§§16b and 16c; §17 item 9), and where a ratified amendment and §§3–15 differ the amendment governs. A test holds the declared route equal to the §16a fragment and §15's block to draft 3's generic route, so the materialization must change both together. The invented population's 2015–2023 formula and DK previous-plan items become off-route. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 0a63202ee..4213b6df4 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -397,6 +397,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 2: `data/external/track_u2/income.json` (the ten later spouse age and sex records and a d637 `rulings_applied` record) and `u1_identity.json` (the specification pin); `docs/design/boomers2004_1946_55_comparison.md` (§17 item 2 and §19); `docs/design/u2_m1_source_adjudication.md` (summary counts and table, the spouse age/sex blocker row, the d1090 section's closing sentence, a d637 section and the ten rows); `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS` and its validation); `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the income byte pin and two docstrings) and `invented.py` (one new named variant); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py` and `test_u2_scripts.py`; `tests/tier_counts.json` and `tests/README-tiers.md`; and this section. - Amendment 3: `income.json` (the six crosswalk records and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the crosswalk blocker row, the d637 section and the six rows); `u2_source_registry.py` (amendment 3's scope) and `sources.py` (the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 4: `wealth.json` (the nine flags and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the wealth blocker row, the d637 section and the nine rows); `u2_source_registry.py` (amendment 4's scope) and `sources.py` (the wealth byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`, and `tests/data/test_track_u2_income_wealth.py`; and this section. +- Amendment 5, builder: `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the declared employer-DC route by wave and `declared_dc_route`), `invented.py` (the invented DC frames use it), `__init__.py` (docstring); the specification's §19 and `u1_identity.json`; `tests/track_u2/test_u2_employer_dc.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/uniform_cut_track_u2/__init__.py b/src/populace_dynamics/uniform_cut_track_u2/__init__.py index e2e5bdcd7..8f612e042 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/__init__.py +++ b/src/populace_dynamics/uniform_cut_track_u2/__init__.py @@ -1,8 +1,13 @@ """Held-out target U2: Boomers 2004, 1946-55 column, uniform 13% cut. Specification ``docs/design/boomers2004_1946_55_comparison.md`` -(``u2-draft-4``), sections 3-15; section 16a's amendments are proposals -pending Max's ruling and change nothing here. U2 extends U1's static +(``u2-draft-4``), sections 3-15, with section 16a's amendments as Max +ruled on them: d637 (section 16b) ratified amendments 2-5 and amendment +6's option 1, and d1090 (section 16c) ruled on blockers B1 and B2. Each +ruling reaches this package through the source registries, one reviewed +commit per amendment, and through the declared rules where the declared +interpretation changes; amendment 1's parts 1a-1c await his +confirmation (d745) and change nothing here. U2 extends U1's static measurement (:mod:`populace_dynamics.uniform_cut_track_u`) to the 1946-55 birth cohort on the 2013-2023 PSID waves. It never edits a U1 module: U1's constants, defaults, registered behaviour, parameter captures and diff --git a/src/populace_dynamics/uniform_cut_track_u2/invented.py b/src/populace_dynamics/uniform_cut_track_u2/invented.py index ea4474b4d..6ba18a73a 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/invented.py +++ b/src/populace_dynamics/uniform_cut_track_u2/invented.py @@ -1047,12 +1047,14 @@ def _dc_raw(family: Mapping[str, Any], wave: int) -> dict[str, int]: "head_prev1_combo_amount": 9_000, "head_prev1_dc_disposition": 3, "head_prev1_dc_amount": 9_000, - # A formula plan's account items (declared route). + # A formula plan's account items: on the declared route + # in 2013 only, off-route from 2015 (amendment 5). "head_prev2_type": 1, "head_prev2_dc_disposition": 3, "head_prev2_dc_amount": 15_000, - # A DK-type plan left to accumulate, and an account plan - # rolled over into an IRA (excluded). + # A DK-type plan left to accumulate (on the declared + # route in 2013 only, off-route from 2015), and an account + # plan rolled over into an IRA (excluded). "wife_prev1_type": 8, "wife_prev1_dc_disposition": 3, "wife_prev1_dc_amount": 12_000, @@ -1091,15 +1093,7 @@ def _dc_frame(families: list[dict], wave: int) -> pd.DataFrame: for concept in raw.columns if concept.endswith("_amount") } - route = sources.DcRoute( - wave, - sources.DECLARED_DC_ROUTE_TYPES["current_account"], - sources.DECLARED_DC_ROUTE_TYPES["previous_both"], - sources.DECLARED_DC_ROUTE_TYPES["previous_account_items"], - 3, - 2, - "section_15_declared_route_invented_only", - ) + route = sources.declared_dc_route(wave) return sources.employer_dc_balances(raw, route, widths) diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 038f174a3..28b14d43d 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -22,7 +22,10 @@ with :class:`U2SourceRefusal`; nothing falls back to another route. * ``invented_declared`` (invented data only): applies the *declared* interpretation the specification writes down (the section 3 role table - and the section 15 routes) to every documented layout, so an invented + and the section 15 routes, amended where Max's rulings differ: section + 16c's exclusions and amendment 5's previous-plan route, see + :data:`DECLARED_PREVIOUS_ACCOUNT_ITEMS`) to every documented layout, so + an invented dry run can exercise each mapping and role branch. It records, for every entry it applies, the blockers the ``registry`` gate would refuse on. It is not a fallback, and nothing on the real-data path accepts @@ -96,8 +99,8 @@ them and :func:`encode_fixed_width` writes invented records in the same layout (tests and the invented dry run). :func:`dc_route` builds the employer-DC route of a wave from the pension registry's route entries -(registry gate) or from the section 15 declared route (invented only), -and :func:`employer_dc_balances` applies it. +(registry gate) or from the declared route (invented only; +:func:`declared_dc_route`), and :func:`employer_dc_balances` applies it. Nothing here opens a survey record: the adapters parse the lines they are given, and :mod:`.loader` decides whether any record may be read. @@ -122,7 +125,9 @@ __all__ = [ "ADMINISTRATIVE_BIRTH_SUPPORT_CODES", "DECLARED", + "DC_ITEM_PLAN_TYPES", "DECLARED_DC_ROUTE_TYPES", + "DECLARED_PREVIOUS_ACCOUNT_ITEMS", "FAMILY_UNIT_EXCLUSIONS", "GATES", "INCOME_CONCEPTS", @@ -153,6 +158,7 @@ "check_invented_records", "check_overlaps", "dc_route", + "declared_dc_route", "decode_income", "declared_role_rules", "employer_dc_balances", @@ -320,13 +326,34 @@ "vehicles", "home_equity", ) -#: Declared previous-plan account-item types (section 15 -#: ``employer_dc.previous_routes.account_items``: account, formula, DK) in -#: the 2011-and-later plan-type codes every U2 wave uses (5, 1, 8). +#: Declared current-job account types and previous combined-plan types +#: (section 15 ``employer_dc``), the same in every wave. DECLARED_DC_ROUTE_TYPES: dict[str, tuple[int, ...]] = { "current_account": (5, 7), "previous_both": (7,), - "previous_account_items": (5, 1, 8), +} +#: Section 15's previous-plan item names in the 2011-and-later plan-type +#: codes every U2 wave uses (P46): account 5, formula 1, DK 8. +DC_ITEM_PLAN_TYPES: dict[str, int] = {"account": 5, "formula": 1, "dk": 8} +#: Declared previous-plan account items by wave. Section 16a amendment +#: 5, ratified as written by Max's d637 ruling (section 16b), replaces +#: section 15's generic ``previous_routes.account_items`` (account, +#: formula, DK) with its ``account_items_by_wave`` fragment: 2013 keeps +#: the route inherited from U1; 2015-2023 admit account plans (P46 = 5) +#: only, so a formula, combined, DK, refused or NA plan's account items +#: are off-route, and an amount reached only through 2015's refused +#: P62ACKPT branch is too (5b). Where a ratified amendment and sections +#: 3-15 differ the amendment governs (section 16b); section 15's own text +#: keeps draft 3's wording until section 20 step 5 materializes the final +#: parameter block. ``tests/track_u2/test_u2_employer_dc.py`` holds this +#: table equal to the fragment in section 16a. +DECLARED_PREVIOUS_ACCOUNT_ITEMS: dict[int, tuple[str, ...]] = { + 2013: ("account", "formula", "dk"), + 2015: ("account",), + 2017: ("account",), + 2019: ("account",), + 2021: ("account",), + 2023: ("account",), } _COUNTED_DISPOSITION = 3 _EXCLUDED_IRA_DISPOSITION = 2 @@ -1543,12 +1570,12 @@ def dc_route(wave: int, gate: SourceGate) -> DcRoute: (current job; previous combined; previous DC-only; the checkpoint's formula/unknown route; IRA rollovers; duplicate and off-route rule). Any route still TO VERIFY or blocked refuses -- today 2015's - checkpoint and respondent slots and 2017-2023's inherited-route - amendment -- and so do disposition codes on which the IRA-rollover, - previous-combined and previous-DC-only entries disagree. Invented - declared gate: the section 15 route (current account or combined - plan; "both" items of a combined plan; account items of an account, - formula or DK plan), recording the registry blockers. + checkpoint and 2017-2023's inherited-route amendment -- and so do + disposition codes on which the IRA-rollover, previous-combined and + previous-DC-only entries disagree. Invented declared gate: + :func:`declared_dc_route` (current account or combined plan; "both" + items of a combined plan; account items of the wave's declared + previous-plan types), recording the registry blockers. """ wave = int(wave) @@ -1569,16 +1596,7 @@ def dc_route(wave: int, gate: SourceGate) -> DcRoute: if wave >= 2017: gate.require("pension", f"{wave}.route.inherited_route_amendment") if gate.kind == INVENTED_DECLARED: - types = DECLARED_DC_ROUTE_TYPES - return DcRoute( - wave, - types["current_account"], - types["previous_both"], - types["previous_account_items"], - _COUNTED_DISPOSITION, - _EXCLUDED_IRA_DISPOSITION, - "section_15_declared_route_invented_only", - ) + return declared_dc_route(wave) rollover = entries["ira_rollovers"] combined = entries["previous_combined"] dc_only = entries["previous_dc_only"] @@ -1618,6 +1636,28 @@ def dc_route(wave: int, gate: SourceGate) -> DcRoute: ) +def declared_dc_route(wave: int) -> DcRoute: + """The declared employer-DC route of ``wave`` (invented data only): + section 15's route with amendment 5's previous-plan account items + (:data:`DECLARED_PREVIOUS_ACCOUNT_ITEMS`).""" + + wave = int(wave) + if wave not in DECLARED_PREVIOUS_ACCOUNT_ITEMS: + raise U2SourceRefusal(f"no declared employer-DC route for {wave}") + return DcRoute( + wave, + DECLARED_DC_ROUTE_TYPES["current_account"], + DECLARED_DC_ROUTE_TYPES["previous_both"], + tuple( + DC_ITEM_PLAN_TYPES[item] + for item in DECLARED_PREVIOUS_ACCOUNT_ITEMS[wave] + ), + _COUNTED_DISPOSITION, + _EXCLUDED_IRA_DISPOSITION, + "section_15_declared_route_invented_only", + ) + + def _amount_codes(width: int) -> dict[str, int]: ceiling = 10 ** int(width) return {"top": ceiling - 3, "dk": ceiling - 2, "na": ceiling - 1} diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 0d7543bf9..77ccd6ebe 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,338 | +| `unit` | 7,360 | | `artifact` | 4,102 | | `integration_psid` | 1,386 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,654** | +| **Total** | **13,676** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 23a30eb70..70ab92ae6 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,7 +1,7 @@ { "schema_version": 1, "counts": { - "unit": 7338, + "unit": 7360, "artifact": 4102, "integration_psid": 1386, "reproduction_legacy": 520, diff --git a/tests/track_u2/test_u2_employer_dc.py b/tests/track_u2/test_u2_employer_dc.py index 24c04b52b..9cbd1acce 100644 --- a/tests/track_u2/test_u2_employer_dc.py +++ b/tests/track_u2/test_u2_employer_dc.py @@ -50,20 +50,7 @@ def _widths(frame: pd.DataFrame) -> dict[str, int]: def _declared(wave: int = 2019) -> sources.DcRoute: - return sources.DcRoute( - wave, - *( - sources.DECLARED_DC_ROUTE_TYPES[key] - for key in ( - "current_account", - "previous_both", - "previous_account_items", - ) - ), - 3, - 2, - "test", - ) + return sources.declared_dc_route(wave) def _balance(route: sources.DcRoute | None = None, **values: int) -> dict: @@ -97,15 +84,82 @@ def test_combined_previous_plan_counts_its_both_items_once(): assert out["employer_dc_off_route_items"] == 0 -@pytest.mark.parametrize("plan_type", [5, 1, 8]) -def test_declared_account_items_route(plan_type): +@pytest.mark.parametrize("wave", sources.SUPPORT_WAVES) +@pytest.mark.parametrize("plan_type", [5, 1, 8, 9]) +def test_declared_account_items_route(wave, plan_type): + """Amendment 5 (ratified by d637): a previous plan's account items + count for an account plan in every wave and, in 2013 only, for a + formula or DK plan too (U1's inherited route); otherwise they are + off-route, counted under that disposition and never zero-filled.""" out = _balance( + _declared(wave), wife_prev2_type=plan_type, wife_prev2_dc_disposition=3, wife_prev2_dc_amount=15_000, ) - assert out["employer_dc_previous"] == 15_000 - assert out["employer_dc_items"] == 1 + counted = plan_type == 5 or (wave == 2013 and plan_type in (1, 8)) + assert out["employer_dc_previous"] == (15_000 if counted else 0) + assert out["employer_dc_items"] == int(counted) + assert out["employer_dc_off_route_items"] == int(not counted) + + +def _section_16a_fragment() -> dict: + """Amendment 5's ``previous_routes`` fragment as section 16a prints it.""" + import json + import re + + from populace_dynamics.uniform_cut_track_u2 import identity + + text = identity.SPECIFICATION_PATH.read_text(encoding="utf-8") + section = text[text.index("## 16a. ") : text.index("## 16b. ")] + lead = ( + "Proposed replacement for \N{SECTION SIGN}15 " + "`income_concept.employer_dc.previous_routes`:" + ) + after = section[section.index(lead) :] + (block,) = re.findall(r"```json\n(.*?)\n```", after, re.S)[:1] + return json.loads("{" + block + "}")["previous_routes"] + + +def test_declared_route_follows_the_ratified_amendment_5_fragment(): + """Max's d637 ruling ratified amendment 5 as written (section 16b), + so the declared route is section 16a's ``previous_routes`` fragment: + the same items per wave, with 2013 equal to section 15's generic + route. Section 15's block keeps draft 3's generic route until section + 20 step 5 materializes the final parameter block; this test then has + to change with it.""" + from populace_dynamics.uniform_cut_track_u2 import rows + + fragment = _section_16a_fragment() + assert fragment["both_items"] == ["both"] + assert { + int(wave): tuple(items) + for wave, items in fragment["account_items_by_wave"].items() + } == sources.DECLARED_PREVIOUS_ACCOUNT_ITEMS + assert set(sources.DECLARED_PREVIOUS_ACCOUNT_ITEMS) == set( + sources.SUPPORT_WAVES + ) + assert fragment["refused_branches"] == { + "2015": "P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF" + } + block = rows.specification_block()["income_concept"]["employer_dc"] + assert block["previous_routes"] == { + "both_items": ["both"], + "account_items": ["account", "formula", "dk"], + } + assert tuple(block["previous_routes"]["account_items"]) == ( + sources.DECLARED_PREVIOUS_ACCOUNT_ITEMS[2013] + ) + for wave in sources.SUPPORT_WAVES: + route = sources.declared_dc_route(wave) + assert route.previous_account_items == tuple( + sources.DC_ITEM_PLAN_TYPES[item] + for item in sources.DECLARED_PREVIOUS_ACCOUNT_ITEMS[wave] + ) + assert route.current_account == (5, 7) + assert route.previous_both == (7,) + with pytest.raises(sources.U2SourceRefusal): + sources.declared_dc_route(2011) def test_rollover_into_an_ira_is_excluded_and_counted(): @@ -131,7 +185,9 @@ def test_other_dispositions_count_nothing(disposition): def test_off_route_and_unreported_and_top_codes(): dk = 10**8 - 2 top = 10**9 - 3 + # 2013's route, where a DK-type plan's account items are on route. out = _balance( + _declared(2013), head_prev1_type=9, head_prev1_dc_disposition=3, head_prev1_dc_amount=4_000, @@ -157,6 +213,9 @@ def test_2013_registry_route_equals_the_declared_route_and_u1_rule( ) assert set(route.previous_both) == {7} assert set(route.previous_account_items) == {5, 1, 8} + assert route.previous_account_items == ( + sources.declared_dc_route(2013).previous_account_items + ) # The same items through U1's 2013 rule give the same balance. frame = _items( head_current_type=5, From 700e5c2badaa2a078a1bc9c08bb030ccf455e7fc Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:36:22 -0400 Subject: [PATCH 05/21] Apply d637 amendment 5b to the U2 source registries Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. This is the registry commit for 5b, the 2015 checkpoint (section 16a, "Registry effect of the rulings": "Resolve the 2015 checkpoint as 5b"). - pension.json: 2015.route.formula_unknown_checkpoint is RESOLVED as the documentary intersection: accepted plan type 5 (P46=5 reaches P64/P65; P46=7 reaches P48/P49 through previous_combined), rejected 1, 7, 8 and 9, the refused branch "P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF" (the fragment's refused_branches entry) and the named disposition off_route. It keeps its adjudication record (A), gains a resolution and a ruling (d637, amendment 5b, replacing draft-3 lines 1173-1176), and keeps its question and refusal as superseded_question and superseded_refusal. The 34 entries that depended on it are released, none with a dependency left. A d637 rulings_applied record is added. - u2_source_registry: 5b's scope pins the entry, its accepted and rejected types, refused branch and disposition, and forbids a conditional route, an action, a refusal or a dependency. - sources: the pension byte pin moves. - The registry gate now applies 2015's family-record specs and employer-DC route; a new differential holds the registry route equal to the declared route wherever the gate applies it (2013, 2015). - The loader's source preflight now refuses on 58 blockers (69 before): code 88 12 (d745), 5a 44, amendment 6 2. - Master record: 1785 RESOLVED, 0 REFUSED, 7 TO VERIFY; the P62A blocker row, the d637 section and 35 rows. Specification section 17 item 2 and section 19 record it; the spec is re-pinned. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/pension.json | 212 ++++++------------ data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 79 +++---- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 23 ++ .../uniform_cut_track_u2/loader.py | 11 +- .../uniform_cut_track_u2/sources.py | 11 +- tests/README-tiers.md | 6 +- tests/tier_counts.json | 4 +- tests/track_u2/test_adjudication_applied.py | 23 +- tests/track_u2/test_pension_registry.py | 29 +++ tests/track_u2/test_source_registries.py | 44 ++++ tests/track_u2/test_u2_employer_dc.py | 23 +- tests/track_u2/test_u2_mappings.py | 79 ++++--- tests/track_u2/test_u2_scripts.py | 6 +- 16 files changed, 323 insertions(+), 233 deletions(-) diff --git a/data/external/track_u2/pension.json b/data/external/track_u2/pension.json index 4a0d32476..16069f345 100644 --- a/data/external/track_u2/pension.json +++ b/data/external/track_u2/pension.json @@ -5038,10 +5038,7 @@ "codebook_documentation": "P52. (Now about the part of your pension where benefits are based on a formula.)\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose (your/his/her) benefits, or what? [ENTER ALL THAT APPLY]--\nFIRST MENTION FOR FIRST PENSION FROM FORMER EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER61932=2); no\npensions through a former employer (ER61988=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER61988=8 or 9); defined contribution retirement plan\n(ER61990=5); DK, NA, or RF type of retirement plan\n(ER61990=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62010", @@ -5078,10 +5075,7 @@ "codebook_documentation": "P52. (Now about the part of your pension where benefits are based on a formula.)\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose (your/his/her) benefits, or what?--SECOND MENTION FOR FIRST\nPENSION FROM FORMER EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: only one mention; has never worked for money\n(ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); defined\ncontribution retirement plan (ER61990=5); DK, NA, or RF\ntype of retirement plan (ER61990=8 or 9); DK, NA, or RF\nto first mention (ER62009=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62011", @@ -5122,10 +5116,7 @@ "codebook_documentation": "P52. (Now about the part of your pension where benefits are based on a formula.)\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose (your/his/her) benefits, or what?--THIRD MENTION FOR FIRST\nPENSION FROM FORMER EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than three mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); defined\ncontribution retirement plan (ER61990=5); DK, NA, or RF\ntype of retirement plan (ER61990=8 or 9); DK, NA, or RF\nto first mention (ER62009=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62012", @@ -5162,10 +5153,7 @@ "codebook_documentation": "P52. (Now about the part of your pension where benefits are based on a formula.)\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose (your/his/her) benefits, or what?--FOURTH MENTION FOR FIRST\nPENSION FROM FORMER EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than four mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); defined\ncontribution retirement plan (ER61990=5); DK, NA, or RF\ntype of retirement plan (ER61990=8 or 9); DK, NA, or RF\nto first mention (ER62009=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62013", @@ -5206,10 +5194,7 @@ "codebook_documentation": "P52. (Now about the part of your pension where benefits are based on a formula.)\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose (your/his/her) benefits, or what?--FIFTH MENTION FOR FIRST\nPENSION FROM FORMER EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than five mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); defined\ncontribution retirement plan (ER61990=5); DK, NA, or RF\ntype of retirement plan (ER61990=8 or 9); DK, NA, or RF\nto first mention (ER62009=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62014", @@ -5246,10 +5231,7 @@ "codebook_documentation": "P52. (Now about the part of your pension where benefits are based on a formula.)\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose (your/his/her) benefits, or what?--SIXTH MENTION FOR FIRST\nPENSION FROM FORMER EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than six mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); defined\ncontribution retirement plan (ER61990=5); DK, NA, or RF\ntype of retirement plan (ER61990=8 or 9); DK, NA, or RF\nto first mention (ER62009=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62042", @@ -5405,10 +5387,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an IRA, leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--FIRST PENSION FROM FORMER\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER61932=2); no\npensions through a former employer (ER61988=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER61988=8 or 9); defined benefit retirement plan or\ncombination plan (ER61990=1 or 7); NA or RF type of\nretirement plan (ER61990=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_prev1_dc_amount", @@ -5462,10 +5441,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + } }, { "id": "2015.head_er62058", @@ -5978,10 +5954,7 @@ "codebook_documentation": "P52. Now about the part of (your/his/her) pension where benefits are based on a formula.\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose your benefits, or what? [ENTER ALL THAT APPLY]--FIRST MENTION\nFOR SECOND PENSION FROM PREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER61932=2); no\npensions through a former employer (ER61988=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER61988=8 or 9); no second pension through a former\nemployer (ER62069=5); defined contribution plan\n(ER62070=5); NA, RF type of retirement plan (ER62070=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62090", @@ -6022,10 +5995,7 @@ "codebook_documentation": "P52. Now about the part of (your/his/her) pension where benefits are based on a formula.\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose your benefits, or what?--SECOND MENTION FOR SECOND PENSION\nFROM PREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: only one mention; has never worked for money\n(ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); no second\npension through a former employer (ER62069=5); defined\ncontribution plan (ER62070=5); NA, RF type of retirement\nplan (ER62070=9); DK, NA, or RF to first mention\n(ER62089=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62091", @@ -6062,10 +6032,7 @@ "codebook_documentation": "P52. Now about the part of (your/his/her) pension where benefits are based on a formula.\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose your benefits, or what?--THIRD MENTION FOR SECOND PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than three mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); no second\npension through a former employer (ER62069=5); defined\ncontribution plan (ER62070=5); NA, RF type of retirement\nplan (ER62070=9); DK, NA, or RF to first mention\n(ER62089=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62092", @@ -6102,10 +6069,7 @@ "codebook_documentation": "P52. Now about the part of (your/his/her) pension where benefits are based on a formula.\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose your benefits, or what?--FOURTH MENTION FOR SECOND PENSION\nFROM PREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than four mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); no second\npension through a former employer (ER62069=5); defined\ncontribution plan (ER62070=5); NA, RF type of retirement\nplan (ER62070=9); DK, NA, or RF to first mention\n(ER62089=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62093", @@ -6142,10 +6106,7 @@ "codebook_documentation": "P52. Now about the part of (your/his/her) pension where benefits are based on a formula.\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose your benefits, or what?--FIFTH MENTION FOR SECOND PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than five mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); no second\npension through a former employer (ER62069=5); defined\ncontribution plan (ER62070=5); NA, RF type of retirement\nplan (ER62070=9); DK, NA, or RF to first mention\n(ER62089=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62094", @@ -6182,10 +6143,7 @@ "codebook_documentation": "P52. Now about the part of (your/his/her) pension where benefits are based on a formula.\n(Do/Does) (you/he/she) expect to receive benefits from this plan in the future, (are/is)\n(you/he/she) receiving them now, did (you/he/she) get a cash settlement when (you/he/she)\nleft, did (you/he/she) lose your benefits, or what?--SIXTH MENTION FOR SECOND PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than six mentions; has never worked for\nmoney (ER61932=2); no pensions through a former employer\n(ER61988=5); DK, NA, or RF whether had any pensions\nthrough a former employer (ER61988=8 or 9); no second\npension through a former employer (ER62069=5); defined\ncontribution plan (ER62070=5); NA, RF type of retirement\nplan (ER62070=9); DK, NA, or RF to first mention\n(ER62089=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_er62122", @@ -6337,10 +6295,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an IRA, leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER61932=2); no\npensions through a former employer (ER61988=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER61988=8 or 9); no second pension through a former\nemployer (ER62069=5); defined benefit retirement plan or\ncombination plan (ER62070=1 or 7); NA or RF type of\nretirement plan (ER62070=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.head_prev2_dc_amount", @@ -6398,10 +6353,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + } }, { "id": "2015.head_er62138", @@ -7383,10 +7335,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (you/she) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what? [ENTER ALL THAT APPLY]--FIRST MENTION FOR\nFIRST PENSION FROM PREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: no Spouse/Partner in FU (ER61347=5); never\nworked for money (ER60667=5); no pensions through a\nformer employer (ER62205=5); DK, NA, or RF whether have\npensions through a former employer (ER62205=8 or 9);\ndefined contribution plan (ER62207=5); NA or RF type of\npension or retirement plan (ER62207=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62227", @@ -7423,10 +7372,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (you/she) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what?--SECOND MENTION FOR FIRST PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: only one mention; no Spouse/Partner in FU\n(ER61347=5); never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); defined contribution plan (ER62207=5);\nNA or RF type of pension or retirement plan (ER62207=9);\nDK, NA, or RF to first mention (ER62226=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62228", @@ -7467,10 +7413,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (you/she) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what?--THIRD MENTION FOR FIRST PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than three mentions; no Spouse/Partner in\nFU (ER61347=5); never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); defined contribution plan (ER62207=5);\nNA or RF type of pension or retirement plan (ER62207=9);\nDK, NA, or RF to first mention (ER62226=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62229", @@ -7507,10 +7450,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (you/she) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what?--FOURTH MENTION FOR FIRST PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than four mentions; no Spouse/Partner in FU\n(ER61347=5); never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); defined contribution plan (ER62207=5);\nNA or RF type of pension or retirement plan (ER62207=9);\nDK, NA, or RF to first mention (ER62226=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62230", @@ -7551,10 +7491,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (you/she) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what?--FIFTH MENTION FOR FIRST PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than five mentions; no Spouse/Partner in FU\n(ER61347=5); never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); defined contribution plan (ER62207=5);\nNA or RF type of pension or retirement plan (ER62207=9);\nDK, NA, or RF to first mention (ER62226=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62231", @@ -7591,10 +7528,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (you/she) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what?--SIXTH MENTION FOR FIRST PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than six mentions; no Spouse/Partner in FU\n(ER61347=5); never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); defined contribution plan (ER62207=5);\nNA or RF type of pension or retirement plan (ER62207=9);\nDK, NA, or RF to first mention (ER62226=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62259", @@ -7746,10 +7680,7 @@ "codebook_documentation": "P64. When (you/she) left that employer, did (you/she) transfer (your/her) account to a\nnew employer, roll it over into an IRA, leave it to accumulate in (your/her) old plan,\nconvert it to an annuity or what?--FIRST PENSION FROM PREVIOUS EMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: no Spouse/Partner in FU (ER61347=5); has never\nworked for money (ER60667=5); no pensions through a\nformer employer (ER62205=5); DK, NA, or RF whether have\npensions through a former employer (ER62205=8 or 9);\ndefined benefit or combination plan (ER62207=1 or 7); NA\nor RF type of pension or retirement plan (ER62207=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_prev1_dc_amount", @@ -7803,10 +7734,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + } }, { "id": "2015.wife_er62275", @@ -8311,10 +8239,7 @@ "codebook_documentation": "P52. Now about the part of her pension where benefits are based on a formula. (Do/Does)\n(you/she) expect to receive benefits from this plan in the future, (are/is) (you/she)\nreceiving them now, did (you/she) get a cash settlement when (you/she) left, did (you/she)\nlose (your/her) benefits, or what? [ENTER ALL THAT APPLY]--FIRST MENTION FOR SECOND\nPENSION FROM PREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: no Spouse/Partner in FU (ER61347=5); has never\nworked for money (ER60667=5); no pensions through a\nformer employer (ER62205=5); DK, NA, or RF whether have\npensions through a former employer (ER62205=8 or 9); no\nsecond pension through a former employer (ER62286=5);\nretirement plan defined contribution plan (ER62287=5);\nNA, RF type of retirement plan (ER62287=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62307", @@ -8355,10 +8280,7 @@ "codebook_documentation": "P52. Now about the part of (your/her) pension where benefits are based on a formula.\n(Do/Does) (you/she) expect to receive benefits from this plan in the future, (are/is)\n(you/she) receiving them now, did (she/you) get a cash settlement when (you/she) left, did\n(you/she) lose (your/her) benefits, or what?--SECOND MENTION FOR SECOND PENSION FROM\nPREVIOUS EMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: only one mention; no Spouse/Partner in FU\n(ER61347=5); has never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); no second pension through a former\nemployer (ER62286=5); retirement plan defined\ncontribution plan (ER62287=5); NA, RF type of retirement\nplan (ER62287=9); DK, NA, or RF to first mention\n(ER62306=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62308", @@ -8395,10 +8317,7 @@ "codebook_documentation": "P52. Now about the part of her pension where benefits are based on a formula. (Do/Does)\n(you/she) expect to receive benefits from this plan in the future, (are/is) (you/she)\nreceiving them now, did (you/she) get a cash settlement when (you/she) left, did (you/she)\nlose (your/her) benefits, or what?--THIRD MENTION FOR SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than three mentions; no Spouse/Partner in\nFU (ER61347=5); has never worked for money (ER60667=5);\nno pensions through a former employer (ER62205=5); DK,\nNA, or RF whether have pensions through a former employer\n(ER62205=8 or 9); no second pension through a former\nemployer (ER62286=5); retirement plan defined\ncontribution plan (ER62287=5); NA, RF type of retirement\nplan (ER62287=9); DK, NA, or RF to first mention\n(ER62306=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62309", @@ -8439,10 +8358,7 @@ "codebook_documentation": "P52. Now about the part of her pension where benefits are based on a formula. (Do/Does)\n(you/she) expect to receive benefits from this plan in the future, (are/is) (you/she)\nreceiving them now, did (you/she) get a cash settlement when (you/she) left, did (you/she)\nlose (your/her) benefits, or what?--FOURTH MENTION FOR SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than four mentions; no Spouse/Partner in FU\n(ER61347=5); has never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); no second pension through a former\nemployer (ER62286=5); retirement plan defined\ncontribution plan (ER62287=5); NA, RF type of retirement\nplan (ER62287=9); DK, NA, or RF to first mention\n(ER62306=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62310", @@ -8479,10 +8395,7 @@ "codebook_documentation": "P52. Now about the part of her pension where benefits are based on a formula. (Do/Does)\n(you/she) expect to receive benefits from this plan in the future, (are/is) (you/she)\nreceiving them now, did (you/she) get a cash settlement when (you/she) left, did (you/she)\nlose (your/her) benefits, or what?--FIFTH MENTION FOR SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than five mentions; no Spouse/Partner in FU\n(ER61347=5); has never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); no second pension through a former\nemployer (ER62286=5); retirement plan defined\ncontribution plan (ER62287=5); NA, RF type of retirement\nplan (ER62287=9); DK, NA, or RF to first mention\n(ER62306=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62311", @@ -8523,10 +8436,7 @@ "codebook_documentation": "P52. Now about the part of her pension where benefits are based on a formula. (Do/Does)\n(you/she) expect to receive benefits from this plan in the future, (are/is) (you/she)\nreceiving them now, did (you/she) get a cash settlement when (you/she) left, did (you/she)\nlose (your/her) benefits, or what?--SIXTH MENTION FOR SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Expect future benefits\n2 Receiving benefits now\n3 Received cash settlement\n4 Rolled over into IRA\n5 Lost benefits\n7 Other\n0 Inap.: fewer than six mentions; no Spouse/Partner in FU\n(ER61347=5); has never worked for money (ER60667=5); no\npensions through a former employer (ER62205=5); DK, NA,\nor RF whether have pensions through a former employer\n(ER62205=8 or 9); no second pension through a former\nemployer (ER62286=5); retirement plan defined\ncontribution plan (ER62287=5); NA, RF type of retirement\nplan (ER62287=9); DK, NA, or RF to first mention\n(ER62306=8 or 9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_er62339", @@ -8686,10 +8596,7 @@ "codebook_documentation": "P64. When (you/she) left that employer, did (you/she) transfer (your/her) account to a\nnew employer, roll it over into an IRA, leave it to accumulate in (your/her) old plan,\nconvert it to an annuity or what?--SECOND PENSION FROM PREVIOUS EMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: actual amount reported; future benefits estimated\nas percent of pay; no Spouse/Partner in FU (ER61347=5);\nhas never worked for money (ER60667=5); no pensions\nthrough a former employer (ER62205=5); DK, NA, or RF\nwhether have pensions through a former employer\n(ER62205=8 or 9); no second pension through a former\nemployer (ER62286=5); retirement plan defined benefit or\ncombination plan (ER62287=1 or 7); NA, RF type of\nretirement plan (ER62287=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2015.wife_prev2_dc_amount", @@ -8743,10 +8650,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + } }, { "id": "2015.wife_er62355", @@ -9009,10 +8913,7 @@ 5 ], "counted_disposition": 3, - "excluded_ira_disposition": 2, - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "excluded_ira_disposition": 2 }, { "id": "2015.route.checkpoint_documentation", @@ -9036,7 +8937,6 @@ "id": "2015.route.formula_unknown_checkpoint", "wave": 2015, "kind": "route", - "status": "TO VERIFY", "citations": [ { "file": "PSID/documentation/capture1/q2015.pdf", @@ -9065,14 +8965,40 @@ ], "finding": "The 2015 P62ACKPT heading requires P46=1 AND P52=1 AND P62AMT=DK/RF, but its displayed branch label says only P62AMT=DK/RF. P64/P65 codebook Inap. descriptions exclude formula and combined types.", "required_amendment": "Replace generic inherited previous_routes with a wave-specific 2015 P62ACKPT predicate after source adjudication.", - "question": "Does the 2015 instrument route only formula plans expecting benefits with P62AMT DK/RF, or any P62AMT DK/RF (including unknown or combined plans)? Obtain a PSID documentary clarification of heading versus branch label and the codebook conflict; preregister the exact accepted plan types and expectation gate before executing. No observed category may resolve this. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 5, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 123).", - "refusal": "Do not execute this pension route until independent documentary adjudication and any stated preregistration amendment are complete.", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 123, "disposition": "A", "amendment": 5 - } + }, + "status": "RESOLVED", + "accepted_plan_types": [ + 5 + ], + "rejected_plan_types": [ + 1, + 7, + 8, + 9 + ], + "condition": "DC-only plan (P46=5), the documentary intersection of the 2015 P62ACKPT heading, its branch label and the FAM2015ER P65 universe", + "refused_branch": "P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF", + "off_route_disposition": "off_route", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5b the 2015 route admits the documentary intersection: P46=5 (DC only) reaches P64/P65, and P46=7 (combined) reaches P48/P49 (pension:2015.route.previous_combined). The disputed P62ACKPT branch, whose heading reads 'P46=1 AND P52=1 AND P62AMT= DK/RF' while its branch label reads only 'P62AMT =DK/RF' (q2015 p. 164), is refused pending PSID clarification: an amount reached only through it is off-route under the named disposition off_route (section 15 employer_dc.excluded), never zero-filled. Formula (1), combined (7), DK (8) and NA or refused (9) plan types are off-route for P64/P65. refused_branch records amendment 5's section 15 fragment entry refused_branches for 2015. The adjudication's question and the route's refusal are kept as superseded_question and superseded_refusal.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "5b", + "replaces_draft_3_lines": [ + 1173, + 1174, + 1175, + 1176 + ] + }, + "superseded_question": "Does the 2015 instrument route only formula plans expecting benefits with P62AMT DK/RF, or any P62AMT DK/RF (including unknown or combined plans)? Obtain a PSID documentary clarification of heading versus branch label and the codebook conflict; preregister the exact accepted plan types and expectation gate before executing. No observed category may resolve this. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 5, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 123).", + "superseded_refusal": "Do not execute this pension route until independent documentary adjudication and any stated preregistration amendment are complete." }, { "id": "2015.route.ira_rollovers", @@ -9126,10 +9052,7 @@ ], "finding": "Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts.", "duplicate_exclusion": "P65 when P46=7", - "requires_resolved_route": "2015.route.formula_unknown_checkpoint", - "blocking_dependencies": [ - "pension:2015.route.formula_unknown_checkpoint" - ] + "requires_resolved_route": "2015.route.formula_unknown_checkpoint" }, { "id": "2015.route.amounts_brackets_top_codes", @@ -26693,6 +26616,15 @@ "date": "2026-10-10", "specification_section": "16c", "note": "Max's ruling d1090 (2026-10-10; specification section 16c) on blockers B1 and B2: the seven role refusals and pension:2015.route.respondent_slots are replaced by the ratified family-unit exclusions (B1, section 16a option 1d-2, amending draft-3 line 160; B2, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019-2023), and the dependencies on the seven role entries are released. Each entry keeps its adjudication record (disposition F) and its superseded refusal text." + }, + { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendments": [ + "5b" + ], + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 5b resolves the 2015 P62ACKPT checkpoint as the documentary intersection, refuses the disputed branch and releases its dependents. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 36752c980..bf5a20696 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "e496a49affc0c56bd9736f398c2f58c2c4bfe087d2aafe3c9fdb8f572c579285", + "sha256": "1a53b2961d6abe12ca5144debeffff8fb85e22cfa4d51e79edf425b433fd47a6", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index ff2bcecce..ae5d9e823 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, and recorded amendment 4's ruling on the nine corrected wealth accuracy flags. Until the rest of them (and, for code 88, d745) land, the 8 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, and resolved the 2015 P62ACKPT checkpoint under 5b, releasing its dependents. Until the rest of them (and, for code 88, d745) land, the 7 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1844,6 +1844,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 3: the six `income..wife_retirement_annuities` crosswalk records (2013–2023) are RESOLVED as non-executable metadata. They keep their crosswalk kind, which the field adapter refuses as an input under either gate; no scalar equivalence is asserted between U1's combined item and the later separately documented spouse pension, annuity, IRA and other-retirement items, which stay inside total family money income under §4 and are never added a second time. No row changes and no loader blocker moves (69). The master record counts 1784 RESOLVED, 0 REFUSED and 8 TO VERIFY records. - Amendment 4: the nine corrected wealth accuracy flags (adjudication D) already held their resolution; each now records the ruling, and its use restriction (imputation diagnostics only; the correction never changes an amount or a sample-inclusion decision) names the ratification in place of "pending Max's ruling", in `data/external/track_u2/wealth.json` and the master record's rows. No U2 path reads these flags, and no count or loader blocker moves. - Amendment 5, before its registry commits: the declared employer-DC route, which only invented data use, follows the ratified replacement fragment printed in §16a. A previous plan's account items count for an account plan (P46 = 5) in every wave and, in 2013 only, for a formula or DK plan too; otherwise they are off-route. §15's own `previous_routes` keeps draft 3's text, because §§3–15 keep it until §20 step 5 materializes the final parameter block (§§16b and 16c; §17 item 9), and where a ratified amendment and §§3–15 differ the amendment governs. A test holds the declared route equal to the §16a fragment and §15's block to draft 3's generic route, so the materialization must change both together. The invented population's 2015–2023 formula and DK previous-plan items become off-route. +- Amendment 5b: the 2015 P62ACKPT checkpoint (`pension:2015.route.formula_unknown_checkpoint`) is RESOLVED as the documentary intersection. P46 = 5 reaches P64/P65 and P46 = 7 reaches P48/P49; the disputed branch, 'P46=1 AND P52=1 AND P62AMT=DK/RF' as the §16a fragment's `refused_branches` names it, is refused, so an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. The 34 entries that waited on the checkpoint are released. The registry gate now applies 2015's employer-DC route, and a differential test holds it equal to the declared route. The loader's source preflight now refuses on 58 blockers. The master record counts 1785 RESOLVED, 0 REFUSED and 7 TO VERIFY records. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index fc2144174..df0d49564 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,14 +2,14 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2026-09-28) to apply the independent adjudication `EV/phase2-20260927/out/u2-adjudicate.md` (SHA-256 `a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662`, verdict ERRORS FOUND). This record does not ratify, register or execute U2. No PSID records were read and no estimator was run. Milestone 1b writes the proposed revision `u2-draft-4` into the specification's new §16a; every line the registries cite keeps its draft-3 text and line number. -**1784 RESOLVED; 0 REFUSED; 8 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 8 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). +**1785 RESOLVED; 0 REFUSED; 7 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 7 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). | Registry | RESOLVED | REFUSED | TO VERIFY | |---|---:|---:|---:| | income | 764 | 0 | 0 | | wealth | 234 | 0 | 0 | | individual | 59 | 0 | 0 | -| pension | 647 | 0 | 1 | +| pension | 648 | 0 | 0 | | roles | 30 | 0 | 6 | | support | 16 | 0 | 0 | | weights | 6 | 0 | 1 | @@ -28,7 +28,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Code 90, 2015–2023 | 2015 and 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | 2015 and 2017 OFUM collection is documented (q2015 pp. 89, 104, 166; q2017 pp. 5, 95; FAQ question 74). From 2019 the Big OFUM gate admits only CYAQRTH=301-882 or 951-982, so 901/902 get no Section G income questions (q2019 p. 103; q2021 p. 205; q2023 p. 203); whether their income enters the aggregates is not documented online, and PSID's 2026-10-07 answer did not say (research record §2). Max ruled on 2026-10-10 (d1090; specification §16c, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019–2023): every 2019–2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under `uncooperative spouse or partner in family unit`. | | Code 92, 2017–2023 | 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | | Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is proposed in amendment 1. | -| 2015 pension P62A | TO VERIFY (adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5 admits the documentary intersection and refuses the disputed branch. | +| 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); application waits for amendment 5 | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. The ratified §15 contract still lists account/formula/dk, so the dependent P64/P65 entries stay blocked until Max rules on amendment 5. | | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | @@ -77,8 +77,9 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 2: annuitant input precedence | 10 | `income:income.{2015,2017,2019,2021,2023}.{wife_age,wife_sex}` are RESOLVED as slot metadata (`usage` `slot_metadata_only`), with a no-slot-inference rule in `use_restriction`: the variables and their inapplicability wording never decide who occupies the spouse slot, and annuity lives use each person's recorded sex and derived income-year age. The builder reads the spouse age only for the family file's slot flag, and the income rows refuse any observation where it disagrees with the roster's code-20 or code-22 occupant | | 3: historical spouse-retirement crosswalk | 6 | `income:income.{2013,2015,2017,2019,2021,2023}.wife_retirement_annuities` are RESOLVED as non-executable metadata: their `kind` stays `historical_route_crosswalk`, which the field adapter refuses as an input under either gate, and a `use_restriction` states that no input or row reads them and no scalar equivalence is inferred. No row changes and no loader blocker moves: no U2 run applies these records | | 4: wealth accuracy-flag erratum | 9 | The nine corrected flags (`wealth:wealth.{2013,2015,2017}.{wealth2_acc,checking_saving_acc}` and `wealth:wealth.{2019,2021,2023}.wealth2_acc`) already held their documentary resolution (adjudication D); each now records the ruling, and its `use_restriction` (imputation diagnostics only; the correction never changes amounts or sample inclusion) names the ratification in place of "pending Max's ruling". No U2 path reads these nine flags, and no row or loader blocker moves | +| 5b: the 2015 P62ACKPT checkpoint | 1 + 34 released | `pension:2015.route.formula_unknown_checkpoint` is RESOLVED: accepted plan type 5 (DC only), rejected 1, 7, 8 and 9, the refused branch `P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF` (amendment 5's section 15 fragment, `refused_branches`) and the named disposition `off_route`; its question and refusal are kept as `superseded_question` and `superseded_refusal`. The 34 entries that depended on it (for each person's two previous plans, the six P52 status mentions and the P64 and P65 items, and the `previous_dc_only` and `duplicate_and_off_route` routes) are released, none with a dependency left. The registry gate now applies 2015's employer-DC route, equal to the declared route | -The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment 1c, pending d745), the 2015 P62A checkpoint and its dependents (11, amendment 5b), the 2017–2023 P64/P65 records and their inherited-route amendments (44, amendment 5a) and the 2017 weight with the individual weight that depends on it (2, amendment 6). +The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment 1c, pending d745), the 2017–2023 P64/P65 records and their inherited-route amendments (44, amendment 5a) and the 2017 weight with the individual weight that depends on it (2, amendment 6). ## One row per source route or field @@ -1264,17 +1265,17 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `pension:2015.head_er61999` | RESOLVED | ER61999: P49C WTR AMT GE 100,000-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3895`; `PSID/family/2015/FAM2015ER.sps:677`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 666 | | `pension:2015.head_er62000` | RESOLVED | ER62000: P49D WTR AMT GE 200,000-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3896`; `PSID/family/2015/FAM2015ER.sps:677`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 666 | | `pension:2015.head_er62001` | RESOLVED | ER62001: P49E WTR AMT GE 20,000-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3897`; `PSID/family/2015/FAM2015ER.sps:677`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 667 | -| `pension:2015.head_er62009` | RESOLVED | ER62009: P52 STATUS PREV PNSN MEN1-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3905`; `PSID/family/2015/FAM2015ER.sps:680`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 670; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 671 | -| `pension:2015.head_er62010` | RESOLVED | ER62010: P52 STATUS PREV PNSN MEN2-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3906`; `PSID/family/2015/FAM2015ER.sps:680`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 671 | -| `pension:2015.head_er62011` | RESOLVED | ER62011: P52 STATUS PREV PNSN MEN3-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3907`; `PSID/family/2015/FAM2015ER.sps:681`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 671; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 672 | -| `pension:2015.head_er62012` | RESOLVED | ER62012: P52 STATUS PREV PNSN MEN4-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3908`; `PSID/family/2015/FAM2015ER.sps:681`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 672 | -| `pension:2015.head_er62013` | RESOLVED | ER62013: P52 STATUS PREV PNSN MEN5-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3909`; `PSID/family/2015/FAM2015ER.sps:681`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 672; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 673 | -| `pension:2015.head_er62014` | RESOLVED | ER62014: P52 STATUS PREV PNSN MEN6-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3910`; `PSID/family/2015/FAM2015ER.sps:682`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 673 | +| `pension:2015.head_er62009` | RESOLVED | ER62009: P52 STATUS PREV PNSN MEN1-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3905`; `PSID/family/2015/FAM2015ER.sps:680`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 670; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 671 | +| `pension:2015.head_er62010` | RESOLVED | ER62010: P52 STATUS PREV PNSN MEN2-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3906`; `PSID/family/2015/FAM2015ER.sps:680`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 671 | +| `pension:2015.head_er62011` | RESOLVED | ER62011: P52 STATUS PREV PNSN MEN3-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3907`; `PSID/family/2015/FAM2015ER.sps:681`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 671; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 672 | +| `pension:2015.head_er62012` | RESOLVED | ER62012: P52 STATUS PREV PNSN MEN4-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3908`; `PSID/family/2015/FAM2015ER.sps:681`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 672 | +| `pension:2015.head_er62013` | RESOLVED | ER62013: P52 STATUS PREV PNSN MEN5-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3909`; `PSID/family/2015/FAM2015ER.sps:681`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 672; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 673 | +| `pension:2015.head_er62014` | RESOLVED | ER62014: P52 STATUS PREV PNSN MEN6-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3910`; `PSID/family/2015/FAM2015ER.sps:682`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 673 | | `pension:2015.head_er62042` | RESOLVED | ER62042: P62 AMT EXPECT REC PREV PNSN-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3938`; `PSID/family/2015/FAM2015ER.sps:691`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 685 | | `pension:2015.head_er62043` | RESOLVED | ER62043: P62 PREV PNSN PER-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3939`; `PSID/family/2015/FAM2015ER.sps:691`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 685 | | `pension:2015.head_er62044` | RESOLVED | ER62044: P62 PCT EXPECT REC PREV PNSN-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3940`; `PSID/family/2015/FAM2015ER.sps:692`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 685; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 686 | -| `pension:2015.head_prev1_dc_disposition` | RESOLVED | ER62056: P64 WHAT DID W/PREV PNSN-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3952`; `PSID/family/2015/FAM2015ER.sps:696`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 691; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692 | -| `pension:2015.head_prev1_dc_amount` | RESOLVED | ER62057: P65 ACCT AMT PREV PNSN NOW-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3953`; `PSID/family/2015/FAM2015ER.sps:696`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692 | +| `pension:2015.head_prev1_dc_disposition` | RESOLVED | ER62056: P64 WHAT DID W/PREV PNSN-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3952`; `PSID/family/2015/FAM2015ER.sps:696`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 691; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692 | +| `pension:2015.head_prev1_dc_amount` | RESOLVED | ER62057: P65 ACCT AMT PREV PNSN NOW-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3953`; `PSID/family/2015/FAM2015ER.sps:696`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692 | | `pension:2015.head_er62058` | RESOLVED | ER62058: P65B WTR AMT GE 50,000-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3954`; `PSID/family/2015/FAM2015ER.sps:696`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692 | | `pension:2015.head_er62059` | RESOLVED | ER62059: P65C WTR AMT GE 100,000-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3955`; `PSID/family/2015/FAM2015ER.sps:697`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 693 | | `pension:2015.head_er62060` | RESOLVED | ER62060: P65D WTR AMT GE 200,000-#1 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3956`; `PSID/family/2015/FAM2015ER.sps:697`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 693 | @@ -1287,17 +1288,17 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `pension:2015.head_er62079` | RESOLVED | ER62079: P49C WTR AMT GE 100,000-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3975`; `PSID/family/2015/FAM2015ER.sps:703`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 702 | | `pension:2015.head_er62080` | RESOLVED | ER62080: P49D WTR AMT GE 200,000-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3976`; `PSID/family/2015/FAM2015ER.sps:704`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 702; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 703 | | `pension:2015.head_er62081` | RESOLVED | ER62081: P49E WTR AMT GE 20,000-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3977`; `PSID/family/2015/FAM2015ER.sps:704`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 703 | -| `pension:2015.head_er62089` | RESOLVED | ER62089: P52 STATUS PREV PNSN MEN1-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3985`; `PSID/family/2015/FAM2015ER.sps:707`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 707 | -| `pension:2015.head_er62090` | RESOLVED | ER62090: P52 STATUS PREV PNSN MEN2-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3986`; `PSID/family/2015/FAM2015ER.sps:707`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 707; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 708 | -| `pension:2015.head_er62091` | RESOLVED | ER62091: P52 STATUS PREV PNSN MEN3-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3987`; `PSID/family/2015/FAM2015ER.sps:707`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 708 | -| `pension:2015.head_er62092` | RESOLVED | ER62092: P52 STATUS PREV PNSN MEN4-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3988`; `PSID/family/2015/FAM2015ER.sps:708`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 709 | -| `pension:2015.head_er62093` | RESOLVED | ER62093: P52 STATUS PREV PNSN MEN5-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3989`; `PSID/family/2015/FAM2015ER.sps:708`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 709 | -| `pension:2015.head_er62094` | RESOLVED | ER62094: P52 STATUS PREV PNSN MEN6-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:3990`; `PSID/family/2015/FAM2015ER.sps:708`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 710 | +| `pension:2015.head_er62089` | RESOLVED | ER62089: P52 STATUS PREV PNSN MEN1-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3985`; `PSID/family/2015/FAM2015ER.sps:707`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 707 | +| `pension:2015.head_er62090` | RESOLVED | ER62090: P52 STATUS PREV PNSN MEN2-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3986`; `PSID/family/2015/FAM2015ER.sps:707`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 707; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 708 | +| `pension:2015.head_er62091` | RESOLVED | ER62091: P52 STATUS PREV PNSN MEN3-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3987`; `PSID/family/2015/FAM2015ER.sps:707`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 708 | +| `pension:2015.head_er62092` | RESOLVED | ER62092: P52 STATUS PREV PNSN MEN4-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3988`; `PSID/family/2015/FAM2015ER.sps:708`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 709 | +| `pension:2015.head_er62093` | RESOLVED | ER62093: P52 STATUS PREV PNSN MEN5-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3989`; `PSID/family/2015/FAM2015ER.sps:708`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 709 | +| `pension:2015.head_er62094` | RESOLVED | ER62094: P52 STATUS PREV PNSN MEN6-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:3990`; `PSID/family/2015/FAM2015ER.sps:708`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 710 | | `pension:2015.head_er62122` | RESOLVED | ER62122: P62 AMT EXPECT REC PREV PNSN-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4018`; `PSID/family/2015/FAM2015ER.sps:718`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 722; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 723 | | `pension:2015.head_er62123` | RESOLVED | ER62123: P62 PREV PNSN PER-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4019`; `PSID/family/2015/FAM2015ER.sps:718`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 723 | | `pension:2015.head_er62124` | RESOLVED | ER62124: P62 PCT EXPECT REC PREV PNSN-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4020`; `PSID/family/2015/FAM2015ER.sps:718`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 723 | -| `pension:2015.head_prev2_dc_disposition` | RESOLVED | ER62136: P64 WHAT DID W/PREV PNSN-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4032`; `PSID/family/2015/FAM2015ER.sps:722`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 730 | -| `pension:2015.head_prev2_dc_amount` | RESOLVED | ER62137: P65 ACCT AMT PREV PNSN NOW-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4033`; `PSID/family/2015/FAM2015ER.sps:723`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 730; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 731 | +| `pension:2015.head_prev2_dc_disposition` | RESOLVED | ER62136: P64 WHAT DID W/PREV PNSN-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4032`; `PSID/family/2015/FAM2015ER.sps:722`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 730 | +| `pension:2015.head_prev2_dc_amount` | RESOLVED | ER62137: P65 ACCT AMT PREV PNSN NOW-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4033`; `PSID/family/2015/FAM2015ER.sps:723`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 730; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 731 | | `pension:2015.head_er62138` | RESOLVED | ER62138: P65B WTR AMT GE 50,000-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4034`; `PSID/family/2015/FAM2015ER.sps:723`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 731 | | `pension:2015.head_er62139` | RESOLVED | ER62139: P65C WTR AMT GE 100,000-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4035`; `PSID/family/2015/FAM2015ER.sps:723`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 731; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 732 | | `pension:2015.head_er62140` | RESOLVED | ER62140: P65D WTR AMT GE 200,000-#2 - HD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4036`; `PSID/family/2015/FAM2015ER.sps:724`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 732 | @@ -1322,17 +1323,17 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `pension:2015.wife_er62216` | RESOLVED | ER62216: P49C WTR AMT GE 100,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4112`; `PSID/family/2015/FAM2015ER.sps:749`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 766; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 767 | | `pension:2015.wife_er62217` | RESOLVED | ER62217: P49D WTR AMT GE 200,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4113`; `PSID/family/2015/FAM2015ER.sps:749`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 767 | | `pension:2015.wife_er62218` | RESOLVED | ER62218: P49E WTR AMT GE 20,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4114`; `PSID/family/2015/FAM2015ER.sps:750`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 767; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 768 | -| `pension:2015.wife_er62226` | RESOLVED | ER62226: P52 STATUS PREV PNSN MEN1-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4122`; `PSID/family/2015/FAM2015ER.sps:752`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 771; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 772 | -| `pension:2015.wife_er62227` | RESOLVED | ER62227: P52 STATUS PREV PNSN MEN2-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4123`; `PSID/family/2015/FAM2015ER.sps:753`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 772 | -| `pension:2015.wife_er62228` | RESOLVED | ER62228: P52 STATUS PREV PNSN MEN3-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4124`; `PSID/family/2015/FAM2015ER.sps:753`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 772; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 773 | -| `pension:2015.wife_er62229` | RESOLVED | ER62229: P52 STATUS PREV PNSN MEN4-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4125`; `PSID/family/2015/FAM2015ER.sps:753`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 773 | -| `pension:2015.wife_er62230` | RESOLVED | ER62230: P52 STATUS PREV PNSN MEN5-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4126`; `PSID/family/2015/FAM2015ER.sps:754`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 773; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 774 | -| `pension:2015.wife_er62231` | RESOLVED | ER62231: P52 STATUS PREV PNSN MEN6-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4127`; `PSID/family/2015/FAM2015ER.sps:754`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 774 | +| `pension:2015.wife_er62226` | RESOLVED | ER62226: P52 STATUS PREV PNSN MEN1-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4122`; `PSID/family/2015/FAM2015ER.sps:752`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 771; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 772 | +| `pension:2015.wife_er62227` | RESOLVED | ER62227: P52 STATUS PREV PNSN MEN2-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4123`; `PSID/family/2015/FAM2015ER.sps:753`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 772 | +| `pension:2015.wife_er62228` | RESOLVED | ER62228: P52 STATUS PREV PNSN MEN3-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4124`; `PSID/family/2015/FAM2015ER.sps:753`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 772; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 773 | +| `pension:2015.wife_er62229` | RESOLVED | ER62229: P52 STATUS PREV PNSN MEN4-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4125`; `PSID/family/2015/FAM2015ER.sps:753`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 773 | +| `pension:2015.wife_er62230` | RESOLVED | ER62230: P52 STATUS PREV PNSN MEN5-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4126`; `PSID/family/2015/FAM2015ER.sps:754`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 773; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 774 | +| `pension:2015.wife_er62231` | RESOLVED | ER62231: P52 STATUS PREV PNSN MEN6-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4127`; `PSID/family/2015/FAM2015ER.sps:754`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 774 | | `pension:2015.wife_er62259` | RESOLVED | ER62259: P62 AMT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4155`; `PSID/family/2015/FAM2015ER.sps:763`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 786 | | `pension:2015.wife_er62260` | RESOLVED | ER62260: P62 PREV PNSN PER-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4156`; `PSID/family/2015/FAM2015ER.sps:764`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 787 | | `pension:2015.wife_er62261` | RESOLVED | ER62261: P62 PCT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4157`; `PSID/family/2015/FAM2015ER.sps:764`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 787 | -| `pension:2015.wife_prev1_dc_disposition` | RESOLVED | ER62273: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4169`; `PSID/family/2015/FAM2015ER.sps:768`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 792; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793 | -| `pension:2015.wife_prev1_dc_amount` | RESOLVED | ER62274: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4170`; `PSID/family/2015/FAM2015ER.sps:768`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793 | +| `pension:2015.wife_prev1_dc_disposition` | RESOLVED | ER62273: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4169`; `PSID/family/2015/FAM2015ER.sps:768`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 792; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793 | +| `pension:2015.wife_prev1_dc_amount` | RESOLVED | ER62274: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4170`; `PSID/family/2015/FAM2015ER.sps:768`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793 | | `pension:2015.wife_er62275` | RESOLVED | ER62275: P65B WTR AMT GE 50,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4171`; `PSID/family/2015/FAM2015ER.sps:769`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793 | | `pension:2015.wife_er62276` | RESOLVED | ER62276: P65C WTR AMT GE 100,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4172`; `PSID/family/2015/FAM2015ER.sps:769`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 794 | | `pension:2015.wife_er62277` | RESOLVED | ER62277: P65D WTR AMT GE 200,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4173`; `PSID/family/2015/FAM2015ER.sps:769`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 794 | @@ -1345,28 +1346,28 @@ The loader's source preflight now refuses on 69 blockers: code 88 (12, amendment | `pension:2015.wife_er62296` | RESOLVED | ER62296: P49C WTR AMT GE 100,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4192`; `PSID/family/2015/FAM2015ER.sps:776`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 803 | | `pension:2015.wife_er62297` | RESOLVED | ER62297: P49D WTR AMT GE 200,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4193`; `PSID/family/2015/FAM2015ER.sps:776`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 803 | | `pension:2015.wife_er62298` | RESOLVED | ER62298: P49E WTR AMT GE 20,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4194`; `PSID/family/2015/FAM2015ER.sps:776`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 804 | -| `pension:2015.wife_er62306` | RESOLVED | ER62306: P52 STATUS PREV PNSN MEN1-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4202`; `PSID/family/2015/FAM2015ER.sps:779`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 808 | -| `pension:2015.wife_er62307` | RESOLVED | ER62307: P52 STATUS PREV PNSN MEN2-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4203`; `PSID/family/2015/FAM2015ER.sps:779`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 808; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 809 | -| `pension:2015.wife_er62308` | RESOLVED | ER62308: P52 STATUS PREV PNSN MEN3-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4204`; `PSID/family/2015/FAM2015ER.sps:780`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 809 | -| `pension:2015.wife_er62309` | RESOLVED | ER62309: P52 STATUS PREV PNSN MEN4-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4205`; `PSID/family/2015/FAM2015ER.sps:780`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 809; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 810 | -| `pension:2015.wife_er62310` | RESOLVED | ER62310: P52 STATUS PREV PNSN MEN5-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4206`; `PSID/family/2015/FAM2015ER.sps:780`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 810 | -| `pension:2015.wife_er62311` | RESOLVED | ER62311: P52 STATUS PREV PNSN MEN6-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4207`; `PSID/family/2015/FAM2015ER.sps:781`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 810; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 811 | +| `pension:2015.wife_er62306` | RESOLVED | ER62306: P52 STATUS PREV PNSN MEN1-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4202`; `PSID/family/2015/FAM2015ER.sps:779`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 808 | +| `pension:2015.wife_er62307` | RESOLVED | ER62307: P52 STATUS PREV PNSN MEN2-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4203`; `PSID/family/2015/FAM2015ER.sps:779`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 808; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 809 | +| `pension:2015.wife_er62308` | RESOLVED | ER62308: P52 STATUS PREV PNSN MEN3-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4204`; `PSID/family/2015/FAM2015ER.sps:780`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 809 | +| `pension:2015.wife_er62309` | RESOLVED | ER62309: P52 STATUS PREV PNSN MEN4-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4205`; `PSID/family/2015/FAM2015ER.sps:780`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 809; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 810 | +| `pension:2015.wife_er62310` | RESOLVED | ER62310: P52 STATUS PREV PNSN MEN5-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4206`; `PSID/family/2015/FAM2015ER.sps:780`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 810 | +| `pension:2015.wife_er62311` | RESOLVED | ER62311: P52 STATUS PREV PNSN MEN6-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4207`; `PSID/family/2015/FAM2015ER.sps:781`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 810; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 811 | | `pension:2015.wife_er62339` | RESOLVED | ER62339: P62 AMT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4235`; `PSID/family/2015/FAM2015ER.sps:790`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 823; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 824 | | `pension:2015.wife_er62340` | RESOLVED | ER62340: P62 PREV PNSN PER-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4236`; `PSID/family/2015/FAM2015ER.sps:790`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 824 | | `pension:2015.wife_er62341` | RESOLVED | ER62341: P62 PCT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4237`; `PSID/family/2015/FAM2015ER.sps:791`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 824; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 825 | -| `pension:2015.wife_prev2_dc_disposition` | RESOLVED | ER62353: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4249`; `PSID/family/2015/FAM2015ER.sps:795`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 830; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831 | -| `pension:2015.wife_prev2_dc_amount` | RESOLVED | ER62354: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/family/2015/FAM2015ER.sps:4250`; `PSID/family/2015/FAM2015ER.sps:795`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831 | +| `pension:2015.wife_prev2_dc_disposition` | RESOLVED | ER62353: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4249`; `PSID/family/2015/FAM2015ER.sps:795`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 830; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831 | +| `pension:2015.wife_prev2_dc_amount` | RESOLVED | ER62354: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4250`; `PSID/family/2015/FAM2015ER.sps:795`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831 | | `pension:2015.wife_er62355` | RESOLVED | ER62355: P65B WTR AMT GE 50,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4251`; `PSID/family/2015/FAM2015ER.sps:795`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 832 | | `pension:2015.wife_er62356` | RESOLVED | ER62356: P65C WTR AMT GE 100,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4252`; `PSID/family/2015/FAM2015ER.sps:796`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 832 | | `pension:2015.wife_er62357` | RESOLVED | ER62357: P65D WTR AMT GE 200,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4253`; `PSID/family/2015/FAM2015ER.sps:796`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 832; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 833 | | `pension:2015.wife_er62358` | RESOLVED | ER62358: P65E WTR AMT GE 20,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:4254`; `PSID/family/2015/FAM2015ER.sps:796`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 833 | | `pension:2015.route.current_job` | RESOLVED | P16 identifies formula=1, account=5, both=7, DK=8 and refused=9; P20 is the present account balance for account and combined plans. Use P16, not the later P22 checkpoint, to route P20. | `PSID/documentation/capture1/q2015.pdf` p. 152; `PSID/documentation/capture1/q2015.pdf` p. 154; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.previous_combined` | RESOLVED | Both previous-employer slots ask P48/P49 only for P46=7 (both). P48=2 is an IRA rollover; P48=3 is left to accumulate. The U2 inherited convention takes only disposition 3. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2015.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 165; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2015.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 165; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2015.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2015.route.formula_unknown_checkpoint` | TO VERIFY | Does the 2015 instrument route only formula plans expecting benefits with P62AMT DK/RF, or any P62AMT DK/RF (including unknown or combined plans)? Obtain a PSID documentary clarification of heading versus branch label and the codebook conflict; preregister the exact accepted plan types and expectation gate before executing. No observed category may resolve this. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 5, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 123). | `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 159; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692; `EV/phase2-20260927/out/u2-adjudicate.md:123` | +| `pension:2015.route.formula_unknown_checkpoint` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5b the 2015 route admits the documentary intersection: P46=5 (DC only) reaches P64/P65, and P46=7 (combined) reaches P48/P49 (pension:2015.route.previous_combined). The disputed P62ACKPT branch, whose heading reads 'P46=1 AND P52=1 AND P62AMT= DK/RF' while its branch label reads only 'P62AMT =DK/RF' (q2015 p. 164), is refused pending PSID clarification: an amount reached only through it is off-route under the named disposition off_route (section 15 employer_dc.excluded), never zero-filled. Formula (1), combined (7), DK (8) and NA or refused (9) plan types are off-route for P64/P65. refused_branch records amendment 5's section 15 fragment entry refused_branches for 2015. The adjudication's question and the route's refusal are kept as superseded_question and superseded_refusal. | `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 159; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692; `EV/phase2-20260927/out/u2-adjudicate.md:123` | | `pension:2015.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2015.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. Application also refuses pending pension:2015.route.formula_unknown_checkpoint. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2015.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2015.pdf` p. 154; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 647; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 648; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 665; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 701; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 730; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 731; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 748; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 765; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 766; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 802; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831 | | `pension:2015.route.previous_plan_universe` | RESOLVED | P45 asks about previous-employer pensions aside from IRA/Keogh plans, expected future benefits, and (under the displayed retirement-income condition) regular payments not yet begun. P69 repeats the plan loop at most twice; no third account balance is collected by that loop. | `PSID/documentation/capture1/q2015.pdf` p. 158; `PSID/documentation/capture1/q2015.pdf` p. 166; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.respondent_slots` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c) on blocker B1, with amendment 5c (ratified by d637): U7's 2015 spouse slot is the documented female spouse or partner slot that P70CKPT gates (family_list_gate: CYFUHU 1 and 4, CYAQRTH 202 and 222), and B1's exclusion (roles:2015.relationship.20, male code 20 in 2015 family unit) applies to U7 as to every other row, so no family unit that supplies an observation holds a male code-20 person. This replaces the adjudication's refusal, kept as superseded_refusal. Part B: Follows roles:2015.relationship.20 (blocker B1). Documented 2015 male legal spouses are Head, code 90 or code 98 and are not pension-loop spouse respondents; PSID answered the residual male code-20 question on 2026-10-07 (docs/design/u2_m1b_psid_research.md section 1), and Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/documentation/capture1/q2015.pdf` p. 158; `PSID/documentation/capture1/q2015.pdf` p. 166; `docs/design/boomers2004_1946_55_comparison.md:309`; `EV/phase2-20260927/out/u2-adjudicate.md:124` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 4213b6df4..a873abfb1 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -398,6 +398,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 3: `income.json` (the six crosswalk records and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the crosswalk blocker row, the d637 section and the six rows); `u2_source_registry.py` (amendment 3's scope) and `sources.py` (the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 4: `wealth.json` (the nine flags and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the wealth blocker row, the d637 section and the nine rows); `u2_source_registry.py` (amendment 4's scope) and `sources.py` (the wealth byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`, and `tests/data/test_track_u2_income_wealth.py`; and this section. - Amendment 5, builder: `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the declared employer-DC route by wave and `declared_dc_route`), `invented.py` (the invented DC frames use it), `__init__.py` (docstring); the specification's §19 and `u1_identity.json`; `tests/track_u2/test_u2_employer_dc.py`; and this section. +- Amendment 5b: `pension.json` (the 2015 checkpoint, its 34 dependents and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the P62A blocker row, the d637 section and 35 rows); `u2_source_registry.py` (5b's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 58594d539..84e014e0a 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -138,6 +138,29 @@ }, "absent": ("action", "blocking_dependencies", "question"), }, + "5b": { + "registry": "pension", + "entries": ("2015.route.formula_unknown_checkpoint",), + "ruling": { + **_D637, + "amendment": "5b", + "replaces_draft_3_lines": [1173, 1174, 1175, 1176], + }, + "fields": { + "status": "RESOLVED", + "accepted_plan_types": [5], + "rejected_plan_types": [1, 7, 8, 9], + "refused_branch": "P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF", + "off_route_disposition": "off_route", + }, + "absent": ( + "action", + "refusal", + "question", + "blocking_dependencies", + "accepted_conditional_types", + ), + }, } #: The d637 amendment each ruled entry belongs to, by (registry, id). _AMENDMENT_OF_ENTRY = { diff --git a/src/populace_dynamics/uniform_cut_track_u2/loader.py b/src/populace_dynamics/uniform_cut_track_u2/loader.py index f10b3cb43..3656048f0 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/loader.py +++ b/src/populace_dynamics/uniform_cut_track_u2/loader.py @@ -12,15 +12,16 @@ the support plan and the role of every amended relationship code -- must be RESOLVED with no blocking dependency, under the committed, pinned registries. It refuses at the committed registries: code 88 - (every wave), the 2015 P62A checkpoint and the revised 2017 - cross-section weight are TO VERIFY, and every 2017-2023 P64/P65 - record waits on the registry commit for amendment 5. The d1090 + (every wave) and the revised 2017 cross-section weight are TO VERIFY, + and every 2017-2023 P64/P65 record waits on the registry commit for + amendment 5a. The d1090 registry commit (section 16c) replaced the refused 2015 code-20 and 2019-2023 code-90/92 routes with the ratified family-unit exclusions, which released the income and pension entries that depended on them; the d637 registry commits (section 16b) resolve the ratified - amendments one by one, starting with amendment 2's spouse age/sex - slot metadata. + amendments one by one: amendment 2's spouse age/sex slot metadata, + amendment 3's crosswalks, amendment 4's wealth flags and 5b's 2015 + P62A checkpoint so far. 2. **Source identity**: each staged setup file and codebook the registries cite is rehashed against the registry's recorded SHA-256, and every registry layout -- the family files' and the individual diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 28b14d43d..1433d25cc 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -235,8 +235,9 @@ #: those commits moved against the registries d1090 left): amendments 2 #: and 3 moved the income bytes (the later spouse age and sex records #: became slot metadata; the spouse-retirement crosswalks became -#: non-executable metadata) and amendment 4 the wealth bytes (the nine -#: corrected accuracy flags record the ruling). +#: non-executable metadata), amendment 4 the wealth bytes (the nine +#: corrected accuracy flags record the ruling) and amendment 5b the +#: pension bytes (the 2015 P62ACKPT checkpoint and its dependents). REGISTRY_SHA256: dict[str, str] = { "income": ( "ac8310a166a66fa71a39d19cb79dec0dda5c41ff5a7033e535d70ebffe87204c" @@ -248,7 +249,7 @@ "4eab8e0abc69668c287310931baa049b8546d3f3e265a106f1b945b948c7649e" ), "pension": ( - "0e329d0a871b70800a93908f2927415a728ac020175a079b38664b532b551d9a" + "d6a26939ced0a960805edd2c4b3dad02edfde311f6ce8120d53173034661adf9" ), "roles": ( "df2b7afe76cda4f92380f88b478d9e6866e0f532b0d85f081d8226ee2e33dc87" @@ -1569,8 +1570,8 @@ def dc_route(wave: int, gate: SourceGate) -> DcRoute: Registry gate: built from the pension registry's route entries (current job; previous combined; previous DC-only; the checkpoint's formula/unknown route; IRA rollovers; duplicate and off-route rule). - Any route still TO VERIFY or blocked refuses -- today 2015's - checkpoint and 2017-2023's inherited-route amendment -- and so do + Any route still TO VERIFY or blocked refuses -- today 2017-2023's + inherited-route amendment -- and so do disposition codes on which the IRA-rollover, previous-combined and previous-DC-only entries disagree. Invented declared gate: :func:`declared_dc_route` (current account or combined plan; "both" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 77ccd6ebe..49183db3c 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,360 | -| `artifact` | 4,102 | +| `unit` | 7,362 | +| `artifact` | 4,109 | | `integration_psid` | 1,386 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,676** | +| **Total** | **13,685** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 70ab92ae6..d30395e86 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,8 +1,8 @@ { "schema_version": 1, "counts": { - "unit": 7360, - "artifact": 4102, + "unit": 7362, + "artifact": 4109, "integration_psid": 1386, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 115a2065d..37be9159b 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -64,7 +64,7 @@ def quoted_spans(markdown): "income": (764, 0, 0), "wealth": (234, 0, 0), "individual": (59, 0, 0), - "pension": (647, 0, 1), + "pension": (648, 0, 0), "roles": (30, 0, 6), "support": (16, 0, 0), "weights": (6, 0, 1), @@ -72,11 +72,11 @@ def quoted_spans(markdown): } SSI_AND_CENSUS_RESOLVED = 26 #: TO VERIFY records left: the adjudication's 24 A items less those the -#: d637 commits resolved (amendment 2: 10; amendment 3: 6). -OPEN_ITEMS = 8 +#: d637 commits resolved (amendment 2: 10; amendment 3: 6; 5b: 1). +OPEN_ITEMS = 7 #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. -D637_APPLIED = {"2": "A", "3": "A", "4": "D"} +D637_APPLIED = {"2": "A", "3": "A", "4": "D", "5b": "A"} D_ITEMS = { "wealth": { @@ -1097,10 +1097,12 @@ def test_section_16a_recommendations_and_options(): "dependents. The d637 commits, one per ratified amendment, have so " "far resolved amendment 2's ten later spouse age and sex records as " "slot metadata and amendment 3's six spouse-retirement crosswalks " - "as non-executable metadata, and recorded amendment 4's ruling on " - "the nine corrected wealth accuracy flags. Until the rest of them " - "(and, for code 88, d745) land, the 8 TO VERIFY records stay open " - "and the 2017\N{EN DASH}2023 P64/P65 records stay blocked.", + "as non-executable metadata, recorded amendment 4's ruling on the " + "nine corrected wealth accuracy flags, and resolved the 2015 " + "P62ACKPT checkpoint under 5b, releasing its dependents. Until the " + "rest of them (and, for code 88, d745) land, the 7 TO VERIFY " + "records stay open and the 2017\N{EN DASH}2023 P64/P65 records " + "stay blocked.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " @@ -1442,6 +1444,11 @@ def test_released_dependencies(documents): for wave in registry.U2_SOURCE_WAVES: entry = pension[f"{wave}.route.respondent_slots"] assert "blocking_dependencies" not in entry, wave + # d637's 5b commit released every dependent of the 2015 checkpoint. + checkpoint = "pension:2015.route.formula_unknown_checkpoint" + for document in documents.values(): + for entry in document["entries"]: + assert checkpoint not in entry.get("blocking_dependencies", []) for wave in (2017, 2019, 2021, 2023): for person in ("head", "wife"): for plan in (1, 2): diff --git a/tests/track_u2/test_pension_registry.py b/tests/track_u2/test_pension_registry.py index 27b7453a7..531a7b218 100644 --- a/tests/track_u2/test_pension_registry.py +++ b/tests/track_u2/test_pension_registry.py @@ -95,6 +95,35 @@ def test_amount_domains_and_evidence_are_explicit(pension_entries): assert entry["u2_value_handling"]["brackets"] == "not_used" +def test_2015_checkpoint_admits_the_documentary_intersection( + pension_entries, +): + """Amendment 5b (ratified by d637, section 16b): P46=5 reaches + P64/P65 and P46=7 reaches P48/P49; the disputed P62ACKPT branch is + refused, so an amount reached only through it is off-route.""" + by_id = {e["id"]: e for e in pension_entries} + checkpoint = by_id["2015.route.formula_unknown_checkpoint"] + assert checkpoint["status"] == "RESOLVED" + assert checkpoint["accepted_plan_types"] == [5] + assert set(checkpoint["rejected_plan_types"]) == {1, 7, 8, 9} + assert "accepted_conditional_types" not in checkpoint + assert checkpoint["refused_branch"] == ( + "P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF" + ) + assert checkpoint["off_route_disposition"] == "off_route" + assert checkpoint["adjudication"]["disposition"] == "A" + assert checkpoint["ruling"]["amendment"] == "5b" + assert registry.require_resolved("pension", checkpoint["id"]) + assert by_id["2015.route.previous_combined"]["accepted_plan_types"] == [7] + assert by_id["2015.route.previous_dc_only"]["accepted_plan_types"] == [5] + for person in ("head", "wife"): + for plan in (1, 2): + for item in ("dc_disposition", "dc_amount"): + key = f"2015.{person}_prev{plan}_{item}" + assert "blocking_dependencies" not in by_id[key], key + assert registry.require_resolved("pension", key) + + @pytest.mark.parametrize("wave", (2017, 2019, 2021, 2023)) def test_later_checkpoint_does_not_inherit_formula_unknown_route( pension_entries, wave diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 2f541263f..b979e7964 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -721,6 +721,50 @@ def _copy_ruling(document, source, target): "wealth", lambda d: _entry(d, "wealth.2019.wealth2_acc").pop("ruling"), ), + "a5b_conditional_types_readded": ( + "pension", + lambda d: _entry(d, "2015.route.formula_unknown_checkpoint").update( + accepted_conditional_types=[1, 8] + ), + ), + "a5b_formula_admitted": ( + "pension", + lambda d: _entry(d, "2015.route.formula_unknown_checkpoint").update( + accepted_plan_types=[5, 1] + ), + ), + "a5b_branch_rewritten": ( + "pension", + lambda d: _entry(d, "2015.route.formula_unknown_checkpoint").update( + refused_branch="P62ACKPT: P62AMT=DK/RF" + ), + ), + "a5b_disposition_renamed": ( + "pension", + lambda d: _entry(d, "2015.route.formula_unknown_checkpoint").update( + off_route_disposition="zero_filled" + ), + ), + "a5b_refusal_kept": ( + "pension", + lambda d: _entry(d, "2015.route.formula_unknown_checkpoint").update( + refusal="kept" + ), + ), + "a5b_on_2017_checkpoint": ( + "pension", + lambda d: _copy_ruling( + d, + "2015.route.formula_unknown_checkpoint", + "2017.route.formula_unknown_checkpoint", + ), + ), + "a5b_lines_narrowed": ( + "pension", + lambda d: _entry(d, "2015.route.formula_unknown_checkpoint")[ + "ruling" + ].update(replaces_draft_3_lines=[1175]), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_employer_dc.py b/tests/track_u2/test_u2_employer_dc.py index 9cbd1acce..57bd103eb 100644 --- a/tests/track_u2/test_u2_employer_dc.py +++ b/tests/track_u2/test_u2_employer_dc.py @@ -241,7 +241,14 @@ def test_2013_registry_route_equals_the_declared_route_and_u1_rule( assert int(mine[column].iloc[0]) == int(theirs[column].iloc[0]), column -@pytest.mark.parametrize("wave", [2015, 2017, 2019, 2021, 2023]) +#: The waves whose employer-DC route the registry gate applies: 2013 +#: (U1's route) and, since the d637 5b commit, 2015. +REGISTRY_DC_WAVES = (2013, 2015) + + +@pytest.mark.parametrize( + "wave", [w for w in sources.SUPPORT_WAVES if w not in REGISTRY_DC_WAVES] +) def test_later_wave_routes_refuse_under_the_registry_gate( wave, committed_registries ): @@ -252,6 +259,20 @@ def test_later_wave_routes_refuse_under_the_registry_gate( sources.pension_field_specs(wave, gate) +@pytest.mark.parametrize("wave", REGISTRY_DC_WAVES) +def test_registry_route_equals_the_declared_route(wave, committed_registries): + """Differential: wherever the registry gate applies a wave's route, + it is the declared route (amendment 5, ratified by d637), apart from + the label naming where it came from.""" + gate = sources.SourceGate(sources.REGISTRY, committed_registries) + registry_route = sources.dc_route(wave, gate).as_dict() + declared = sources.declared_dc_route(wave).as_dict() + assert registry_route.pop("source") == "pension_registry_routes" + assert declared.pop("source") == "section_15_declared_route_invented_only" + assert registry_route == declared + assert sources.pension_field_specs(wave, gate) + + def test_registry_documents_dc_only_checkpoints_from_2017( committed_registries, ): diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index de263f7c0..a602fb2a1 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -298,16 +298,40 @@ def test_d1090_moved_only_the_ruled_gate_fields(): # Amendment 4's nine D items already held their resolution: the # ruling and the ratified restriction wording move. "4": {"use_restriction", "ruling"}, + # The 2015 checkpoint's refusal is a gate field; its question is prose. + "5b": { + "status", + "accepted_plan_types", + "rejected_plan_types", + "condition", + "refused_branch", + "off_route_disposition", + "resolution", + "ruling", + "superseded_question", + "refusal", + "superseded_refusal", + }, } #: The dependencies each amendment released. -D637_RELEASED: dict[str, frozenset[str]] = {} +D637_RELEASED: dict[str, frozenset[str]] = { + "5b": frozenset({"pension:2015.route.formula_unknown_checkpoint"}), +} +#: How many entries, by registry, lost a released dependency (each then +#: has none left). +D637_RELEASED_COUNTS = {"pension": 34} #: The top-level fields each registry's d637 commits changed. D637_TOP = { "income": {"rulings_applied"}, "wealth": {"rulings_applied"}, + "pension": {"rulings_applied"}, } #: The amendments each registry's ``rulings_applied`` d637 record lists. -D637_RECORDED = {"income": ["2", "3"], "wealth": ["4"]} +D637_RECORDED = { + "income": ["2", "3"], + "wealth": ["4"], + "pension": ["5b"], +} def test_d637_moved_only_the_ruled_gate_fields(): @@ -369,7 +393,11 @@ def test_d637_moved_only_the_ruled_gate_fields(): ), (name, entry["id"]) # Every ruled entry moved, so no scope names an untouched entry. assert set(ruled) <= moved - assert sum(released.values()) == len(released) == 0 + by_registry = {} + for name, _ in released: + by_registry[name] = by_registry.get(name, 0) + 1 + assert by_registry == D637_RELEASED_COUNTS + assert all(released.values()) def test_the_gate_projection_sees_a_moved_gate_field(): @@ -599,35 +627,34 @@ def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): #: The first registry blocker each later wave's family-record specs meet #: (income concepts are read first, in :data:`sources.INCOME_CONCEPTS` #: order, then wealth, then the employer-DC items): the first P64 -#: disposition, which waits on the 2015 P62ACKPT checkpoint (amendment -#: 5b) or on 2017-2023's inherited-route amendment (5a). Until the d1090 -#: registry commit (section 16c), 2015's income entries depended on the -#: refused 2015 code-20 route and 2019-2023's on the refused code-90/92 -#: routes; until the d637 amendment-2 commit (section 16b), every later -#: wave first met its spouse age slot metadata, TO VERIFY. No income or -#: wealth entry blocks a later wave now. +#: disposition, which waits on 2017-2023's inherited-route amendment +#: (5a). Until the d1090 registry commit (section 16c), 2015's income +#: entries depended on the refused 2015 code-20 route and 2019-2023's on +#: the refused code-90/92 routes; until the d637 amendment-2 commit +#: (section 16b), every later wave first met its spouse age slot +#: metadata, TO VERIFY; until the 5b commit, 2015's P64 items waited on +#: its P62ACKPT checkpoint. No income or wealth entry blocks a later wave +#: now, and 2015 meets no blocker. +OPEN_WAVES_RESOLVED = (2013, 2015) FIRST_BLOCKER = { - 2015: ( - "pension:2015.head_prev1_dc_disposition: blocked by " - "pension:2015.route.formula_unknown_checkpoint" - ), - **{ - wave: ( - f"pension:{wave}.head_prev1_dc_disposition: blocked by " - f"pension:{wave}.route.inherited_route_amendment" - ) - for wave in (2017, 2019, 2021, 2023) - }, + wave: ( + f"pension:{wave}.head_prev1_dc_disposition: blocked by " + f"pension:{wave}.route.inherited_route_amendment" + ) + for wave in (2017, 2019, 2021, 2023) } def test_registry_gate_refuses_every_open_wave(registry_gate): # 2013: every mapping U2 reads is resolved (the 2013 meanings are - # U1's); 2015-2023: each wave meets a TO VERIFY or refused entry. - specs = loader.family_record_specs(2013, registry_gate) - concepts = {spec.concept for spec in specs} - assert set(sources.INCOME_CONCEPTS) <= concepts - assert {"wealth1", "vehicles", "head_current_amount"} <= concepts + # U1's); 2015 since the d637 commits for amendments 2 and 5b; each + # other wave meets a blocked entry. + for wave in OPEN_WAVES_RESOLVED: + specs = loader.family_record_specs(wave, registry_gate) + concepts = {spec.concept for spec in specs} + assert set(sources.INCOME_CONCEPTS) <= concepts + assert {"wealth1", "vehicles", "head_current_amount"} <= concepts + assert all(spec.gate == sources.REGISTRY for spec in specs) for wave, blocker in FIRST_BLOCKER.items(): with pytest.raises(sources.U2SourceRefusal) as error: loader.family_record_specs(wave, registry_gate) diff --git a/tests/track_u2/test_u2_scripts.py b/tests/track_u2/test_u2_scripts.py index 5228a8f45..2019a5404 100644 --- a/tests/track_u2/test_u2_scripts.py +++ b/tests/track_u2/test_u2_scripts.py @@ -98,8 +98,10 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): assert entry["income_equal_to_invented_frame"], wave assert entry["dc_equal_to_invented_frame"], wave assert entry["wealth1_identity"]["n_exact"] == entry["n_records"] - assert entry["registry_gate"]["refused"] == (wave != "2013") - assert entry["registry_gate_dc_route"]["refused"] == (wave != "2013") + # 2015 resolved with the d637 commits for amendments 2 and 5b. + resolved = wave in ("2013", "2015") + assert entry["registry_gate"]["refused"] == (not resolved) + assert entry["registry_gate_dc_route"]["refused"] == (not resolved) assert checks["mapping"]["negative_where_undocumented_refused"]["refused"] assert checks["mapping"]["crosswalk_as_input_refused"]["refused"] from populace_dynamics.uniform_cut_track_u2 import sources From db6054c3171bea9088957b74503735ef5ff4aba2 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:40:40 -0400 Subject: [PATCH 06/21] Apply d637 amendment 5a to the U2 source registries Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. This is the registry commit for 5a (section 16a, "Registry effect of the rulings": "Release the 2017-2023 P64/P65 records"). - pension.json: the four {2017,2019,2021,2023}.route.inherited_route_ amendment entries keep their documentary resolution (adjudication D) and now apply: each drops its refuse_pending_amendment_5_ruling action, keeps its refusal as superseded_refusal and gains a ruling_effect and a ruling (d637, amendment 5a, replacing draft-3 lines 1173-1176). The 40 P64/P65 entries that depended on them are released, none with a dependency left; the d637 rulings_applied record lists 5b and 5a. - u2_source_registry: 5a's scope pins the four entries and forbids an action, a refusal, a question or a dependency on them. - sources: the pension byte pin moves; the dc_route docstring. - The registry gate now applies every support wave's family records and employer-DC route; the registry/declared differential covers every wave (2017-2023 accept plan type 5 only, as the declared route does). The declared-gate audit is checked on an invented copy with one P64 item re-blocked, and the registry gate's refusal of a blocked route in memory. - The loader's source preflight now refuses on 14 blockers (58 before): code 88 12 (d745) and the 2017 weight 2 (amendment 6). - Master record: the P64/P65 blocker row, the d637 section and 44 rows. Specification section 17 item 2 and section 19 record it; the spec is re-pinned. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/pension.json | 277 +++++++----------- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 93 +++--- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 14 + .../uniform_cut_track_u2/loader.py | 9 +- .../uniform_cut_track_u2/sources.py | 12 +- tests/README-tiers.md | 6 +- tests/tier_counts.json | 4 +- tests/track_u2/test_adjudication_applied.py | 44 +-- tests/track_u2/test_pension_registry.py | 21 +- tests/track_u2/test_source_registries.py | 32 ++ tests/track_u2/test_u2_employer_dc.py | 70 ++--- tests/track_u2/test_u2_mappings.py | 88 +++--- tests/track_u2/test_u2_scripts.py | 8 +- 16 files changed, 341 insertions(+), 343 deletions(-) diff --git a/data/external/track_u2/pension.json b/data/external/track_u2/pension.json index 16069f345..921827585 100644 --- a/data/external/track_u2/pension.json +++ b/data/external/track_u2/pension.json @@ -10190,10 +10190,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--FIRST PENSION FROM FORMER\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER67986=2); no\npensions through a former employer (ER68042=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER68042=8 or 9); defined benefit retirement plan or\ncombination plan (ER68044=1 or 7); NA or RF type of\nretirement plan (ER68044=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2017.head_prev1_dc_amount", @@ -10247,10 +10244,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + } }, { "id": "2017.head_er68112", @@ -10890,10 +10884,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER67986=2); no\npensions through a former employer (ER68042=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER68042=8 or 9); no second pension through a former\nemployer (ER68123=5); defined benefit retirement plan or\ncombination plan (ER68124=1 or 7); NA or RF type of\nretirement plan (ER68124=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2017.head_prev2_dc_amount", @@ -10947,10 +10938,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + } }, { "id": "2017.head_er68192", @@ -12051,10 +12039,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--FIRST PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: no Spouse/Partner in FU (ER67399=5); has never\nworked for money (ER66688=5); no pensions through a\nformer employer (ER68259=5); DK, NA, or RF whether have\npensions through a former employer (ER68259=8 or 9);\ndefined benefit or combination plan (ER68261=1 or 7); NA\nor RF type of pension or retirement plan (ER68261=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2017.wife_prev1_dc_amount", @@ -12108,10 +12093,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + } }, { "id": "2017.wife_er68329", @@ -12751,10 +12733,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: actual amount reported; future benefits estimated\nas percent of pay; no Spouse/Partner in FU (ER67399=5);\nhas never worked for money (ER66688=5); no pensions\nthrough a former employer (ER68259=5); DK, NA, or RF\nwhether have pensions through a former employer\n(ER68259=8 or 9); no second pension through a former\nemployer (ER68340=5); retirement plan defined benefit or\ncombination plan (ER68341=1 or 7); NA, RF type of\nretirement plan (ER68341=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2017.wife_prev2_dc_amount", @@ -12808,10 +12787,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + } }, { "id": "2017.wife_er68409", @@ -13074,10 +13050,7 @@ 5 ], "counted_disposition": 3, - "excluded_ira_disposition": 2, - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + "excluded_ira_disposition": 2 }, { "id": "2017.route.checkpoint_documentation", @@ -13153,15 +13126,27 @@ ], "finding": "The draft parameter contract lists account/formula/dk previous account routes for U2, while this wave questionnaire permits only account P46=5.", "required_amendment": "2017: previous account-item accepted_plan_types=[5]; formula/unknown/combined excluded.", - "refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 125).", "resolution": "Independent adjudication D (amendment 5): q2017 p. 152 P62ACKPT gives '1. DB Only (P46=1)', '3. DC Only (P46=5)', '5. All Others (P46=7 or DK/RF)'; DB-only and all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded.", - "action": "refuse_pending_amendment_5_ruling", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 125, "disposition": "D", "amendment": 5 - } + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2017 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2017.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "5a", + "replaces_draft_3_lines": [ + 1173, + 1174, + 1175, + 1176 + ] + }, + "superseded_refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 125)." }, { "id": "2017.route.ira_rollovers", @@ -13215,10 +13200,7 @@ ], "finding": "Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts.", "duplicate_exclusion": "P65 when P46=7", - "requires_resolved_route": "2017.route.formula_unknown_checkpoint", - "blocking_dependencies": [ - "pension:2017.route.inherited_route_amendment" - ] + "requires_resolved_route": "2017.route.formula_unknown_checkpoint" }, { "id": "2017.route.amounts_brackets_top_codes", @@ -14323,10 +14305,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--FIRST PENSION FROM FORMER\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER74014=2); no\npensions through a former employer (ER74064=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER74064=8 or 9); defined benefit retirement plan or\ncombination plan (ER74066=1 or 7); NA or RF type of\nretirement plan (ER74066=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2019.head_prev1_dc_amount", @@ -14380,10 +14359,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + } }, { "id": "2019.head_er74132", @@ -15023,10 +14999,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: has never worked for money (ER74014=2); no\npensions through a former employer (ER74064=5); DK, NA,\nor RF whether had any pensions through a former employer\n(ER74064=8 or 9); no second pension through a former\nemployer (ER74143=5); defined benefit retirement plan or\ncombination plan (ER74144=1 or 7); NA or RF type of\nretirement plan (ER74144=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2019.head_prev2_dc_amount", @@ -15080,10 +15053,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + } }, { "id": "2019.head_er74210", @@ -16176,10 +16146,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--FIRST PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: no Spouse/Partner in FU (ER73422=5); has never\nworked for money (ER74221=2); no pensions through a\nformer employer (ER74271=5); DK, NA, or RF whether have\npensions through a former employer (ER74271=8 or 9);\ndefined benefit or combination plan (ER74273=1 or 7); NA\nor RF type of pension or retirement plan (ER74273=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2019.wife_prev1_dc_amount", @@ -16233,10 +16200,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + } }, { "id": "2019.wife_er74339", @@ -16868,10 +16832,7 @@ "codebook_documentation": "P64. When (you/he/she) left that employer, did (you/he/she) transfer (your/his/her)\naccount to a new employer, roll it over into an I.R.A., leave it to accumulate in\n(your/his/her) old plan, convert it to an annuity or what?--SECOND PENSION FROM PREVIOUS\nEMPLOYER\n1 Transferred to new employer\n2 Rolled over into IRA\n3 Left to accumulate\n4 Converted to annuity\n7 Other\n8 DK\n9 NA; refused\n0 Inap.: actual amount reported; future benefits estimated\nas percent of pay; no Spouse/Partner in FU (ER73422=5);\nhas never worked for money (ER74221=2); no pensions\nthrough a former employer (ER74271=5); DK, NA, or RF\nwhether have pensions through a former employer\n(ER74271=8 or 9); no second pension through a former\nemployer (ER74350=5); retirement plan defined benefit or\ncombination plan (ER74351=1 or 7); NA, RF type of\nretirement plan (ER74351=9)", "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, - "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited.", - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + "format_verification": "No separate family SPSS formats file staged for this wave; setup and codebook are cited." }, { "id": "2019.wife_prev2_dc_amount", @@ -16925,10 +16886,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + } }, { "id": "2019.wife_er74417", @@ -17191,10 +17149,7 @@ 5 ], "counted_disposition": 3, - "excluded_ira_disposition": 2, - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + "excluded_ira_disposition": 2 }, { "id": "2019.route.checkpoint_documentation", @@ -17270,15 +17225,27 @@ ], "finding": "The draft parameter contract lists account/formula/dk previous account routes for U2, while this wave questionnaire permits only account P46=5.", "required_amendment": "2019: previous account-item accepted_plan_types=[5]; formula/unknown/combined excluded.", - "refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 126).", "resolution": "Independent adjudication D (amendment 5): q2019 p. 163 P62ACKPT gives the same three P62ACKPT branches, followed by 'Plan with Former Employer - Defined Contribution (P46=DC Only)'. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded.", - "action": "refuse_pending_amendment_5_ruling", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 126, "disposition": "D", "amendment": 5 - } + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2019 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2019.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "5a", + "replaces_draft_3_lines": [ + 1173, + 1174, + 1175, + 1176 + ] + }, + "superseded_refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 126)." }, { "id": "2019.route.ira_rollovers", @@ -17332,10 +17299,7 @@ ], "finding": "Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts.", "duplicate_exclusion": "P65 when P46=7", - "requires_resolved_route": "2019.route.formula_unknown_checkpoint", - "blocking_dependencies": [ - "pension:2019.route.inherited_route_amendment" - ] + "requires_resolved_route": "2019.route.formula_unknown_checkpoint" }, { "id": "2019.route.amounts_brackets_top_codes", @@ -18540,10 +18504,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: has never worked for money (ER80137=2); no pensions through a former employer (ER80186=5); DK, NA, or RF...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2021.head_prev1_dc_amount", @@ -18597,10 +18558,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + } }, { "id": "2021.head_er80254", @@ -19305,10 +19263,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: has never worked for money (ER80137=2); no pensions through a former employer (ER80186=5); DK, NA, or RF...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2021.head_prev2_dc_amount", @@ -19362,10 +19317,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + } }, { "id": "2021.head_er80332", @@ -20586,10 +20538,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: no Spouse/Partner in FU (ER79524=5); has never worked for money (ER80343=2); no pensions through a former...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2021.wife_prev1_dc_amount", @@ -20643,10 +20592,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + } }, { "id": "2021.wife_er80460", @@ -21343,10 +21289,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: actual amount reported; future benefits estimated as percent of pay; no Spouse/Partner in FU (ER79524=5);...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2021.wife_prev2_dc_amount", @@ -21400,10 +21343,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + } }, { "id": "2021.wife_er80538", @@ -21686,10 +21626,7 @@ 5 ], "counted_disposition": 3, - "excluded_ira_disposition": 2, - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + "excluded_ira_disposition": 2 }, { "id": "2021.route.checkpoint_documentation", @@ -21765,15 +21702,27 @@ ], "finding": "The draft parameter contract lists account/formula/dk previous account routes for U2, while this wave questionnaire permits only account P46=5.", "required_amendment": "2021: previous account-item accepted_plan_types=[5]; formula/unknown/combined excluded.", - "refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 127).", "resolution": "Independent adjudication D (amendment 5): q2021 p. 304 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded.", - "action": "refuse_pending_amendment_5_ruling", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 127, "disposition": "D", "amendment": 5 - } + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2021 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2021.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "5a", + "replaces_draft_3_lines": [ + 1173, + 1174, + 1175, + 1176 + ] + }, + "superseded_refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 127)." }, { "id": "2021.route.ira_rollovers", @@ -21827,10 +21776,7 @@ ], "finding": "Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts.", "duplicate_exclusion": "P65 when P46=7", - "requires_resolved_route": "2021.route.formula_unknown_checkpoint", - "blocking_dependencies": [ - "pension:2021.route.inherited_route_amendment" - ] + "requires_resolved_route": "2021.route.formula_unknown_checkpoint" }, { "id": "2021.route.amounts_brackets_top_codes", @@ -23090,10 +23036,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: has never worked for money (ER84107=2); no pensions through a former employer (ER84156=5); DK, NA, or RF...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2023.head_prev1_dc_amount", @@ -23147,10 +23090,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + } }, { "id": "2023.head_er84224", @@ -23855,10 +23795,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: has never worked for money (ER84107=2); no pensions through a former employer (ER84156=5); DK, NA, or RF...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2023.head_prev2_dc_amount", @@ -23912,10 +23849,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + } }, { "id": "2023.head_er84302", @@ -25136,10 +25070,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 1, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: no Spouse/Partner in FU (ER83493=5); has never worked for money (ER84313=2); no pensions through a former...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2023.wife_prev1_dc_amount", @@ -25193,10 +25124,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + } }, { "id": "2023.wife_er84430", @@ -25893,10 +25821,7 @@ "adjudication_scope": "Variable identity, setup layout, documented code domain and universe; routing and role blockers remain separate entries.", "previous_plan": 2, "format_documentation": "1 'Transferred to new employer'\n 2 'Rolled over into IRA'\n 3 'Left to accumulate'\n 4 'Converted to annuity'\n 7 'Other'\n 8 'DK'\n 9 'NA; refused'\n 0 'Inap.: actual amount reported; future benefits estimated as percent of pay; no Spouse/Partner in FU (ER83493=5);...'", - "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes.", - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + "format_verification": "Matched variable block; format labels ending ... are truncated and do not replace full codebook universes." }, { "id": "2023.wife_prev2_dc_amount", @@ -25950,10 +25875,7 @@ "dk": "zero_counted_unreported", "na_refused": "zero_counted_unreported", "brackets": "not_used" - }, - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + } }, { "id": "2023.wife_er84508", @@ -26236,10 +26158,7 @@ 5 ], "counted_disposition": 3, - "excluded_ira_disposition": 2, - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + "excluded_ira_disposition": 2 }, { "id": "2023.route.checkpoint_documentation", @@ -26315,15 +26234,27 @@ ], "finding": "The draft parameter contract lists account/formula/dk previous account routes for U2, while this wave questionnaire permits only account P46=5.", "required_amendment": "2023: previous account-item accepted_plan_types=[5]; formula/unknown/combined excluded.", - "refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 128).", "resolution": "Independent adjudication D (amendment 5): q2023 p. 302 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded.", - "action": "refuse_pending_amendment_5_ruling", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 128, "disposition": "D", "amendment": 5 - } + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2023 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2023.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "5a", + "replaces_draft_3_lines": [ + 1173, + 1174, + 1175, + 1176 + ] + }, + "superseded_refusal": "The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 128)." }, { "id": "2023.route.ira_rollovers", @@ -26377,10 +26308,7 @@ ], "finding": "Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts.", "duplicate_exclusion": "P65 when P46=7", - "requires_resolved_route": "2023.route.formula_unknown_checkpoint", - "blocking_dependencies": [ - "pension:2023.route.inherited_route_amendment" - ] + "requires_resolved_route": "2023.route.formula_unknown_checkpoint" }, { "id": "2023.route.amounts_brackets_top_codes", @@ -26622,9 +26550,10 @@ "date": "2026-09-30", "specification_section": "16b", "amendments": [ - "5b" + "5b", + "5a" ], - "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 5b resolves the 2015 P62ACKPT checkpoint as the documentary intersection, refuses the disputed branch and releases its dependents. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 5b resolves the 2015 P62ACKPT checkpoint as the documentary intersection, refuses the disputed branch and releases its dependents; amendment 5a releases the 2017-2023 inherited-route amendments and the P64/P65 entries that depended on them. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index bf5a20696..411986f78 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "1a53b2961d6abe12ca5144debeffff8fb85e22cfa4d51e79edf425b433fd47a6", + "sha256": "6c2192aceb35aec3f6502944cd96dea5e1a22233b153b519c5267df93b14f607", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index ae5d9e823..04fe89956 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, and resolved the 2015 P62ACKPT checkpoint under 5b, releasing its dependents. Until the rest of them (and, for code 88, d745) land, the 7 TO VERIFY records stay open and the 2017–2023 P64/P65 records stay blocked. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b and released the 2017–2023 P64/P65 records under 5a. Until the rest of them (and, for code 88, d745) land, the 7 TO VERIFY records stay open. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1845,6 +1845,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 4: the nine corrected wealth accuracy flags (adjudication D) already held their resolution; each now records the ruling, and its use restriction (imputation diagnostics only; the correction never changes an amount or a sample-inclusion decision) names the ratification in place of "pending Max's ruling", in `data/external/track_u2/wealth.json` and the master record's rows. No U2 path reads these flags, and no count or loader blocker moves. - Amendment 5, before its registry commits: the declared employer-DC route, which only invented data use, follows the ratified replacement fragment printed in §16a. A previous plan's account items count for an account plan (P46 = 5) in every wave and, in 2013 only, for a formula or DK plan too; otherwise they are off-route. §15's own `previous_routes` keeps draft 3's text, because §§3–15 keep it until §20 step 5 materializes the final parameter block (§§16b and 16c; §17 item 9), and where a ratified amendment and §§3–15 differ the amendment governs. A test holds the declared route equal to the §16a fragment and §15's block to draft 3's generic route, so the materialization must change both together. The invented population's 2015–2023 formula and DK previous-plan items become off-route. - Amendment 5b: the 2015 P62ACKPT checkpoint (`pension:2015.route.formula_unknown_checkpoint`) is RESOLVED as the documentary intersection. P46 = 5 reaches P64/P65 and P46 = 7 reaches P48/P49; the disputed branch, 'P46=1 AND P52=1 AND P62AMT=DK/RF' as the §16a fragment's `refused_branches` names it, is refused, so an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. The 34 entries that waited on the checkpoint are released. The registry gate now applies 2015's employer-DC route, and a differential test holds it equal to the declared route. The loader's source preflight now refuses on 58 blockers. The master record counts 1785 RESOLVED, 0 REFUSED and 7 TO VERIFY records. +- Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65, formula, combined, DK, refused and NA plans are off-route under `off_route`, and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index df0d49564..4768a4e20 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -29,7 +29,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Code 92, 2017–2023 | 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | | Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is proposed in amendment 1. | | 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | -| 2017–2023 pension P64/P65 contract | RESOLVED (D); application waits for amendment 5 | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. The ratified §15 contract still lists account/formula/dk, so the dependent P64/P65 entries stay blocked until Max rules on amendment 5. | +| 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a replaces the §15 contract's generic account/formula/dk previous route with this wave-specific route, so the dependent P64/P65 entries are released. | | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D); amendment 4 ratified by Max's ruling d637 | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics, and the corrections never change an amount or a sample-inclusion decision. | @@ -78,8 +78,9 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 3: historical spouse-retirement crosswalk | 6 | `income:income.{2013,2015,2017,2019,2021,2023}.wife_retirement_annuities` are RESOLVED as non-executable metadata: their `kind` stays `historical_route_crosswalk`, which the field adapter refuses as an input under either gate, and a `use_restriction` states that no input or row reads them and no scalar equivalence is inferred. No row changes and no loader blocker moves: no U2 run applies these records | | 4: wealth accuracy-flag erratum | 9 | The nine corrected flags (`wealth:wealth.{2013,2015,2017}.{wealth2_acc,checking_saving_acc}` and `wealth:wealth.{2019,2021,2023}.wealth2_acc`) already held their documentary resolution (adjudication D); each now records the ruling, and its `use_restriction` (imputation diagnostics only; the correction never changes amounts or sample inclusion) names the ratification in place of "pending Max's ruling". No U2 path reads these nine flags, and no row or loader blocker moves | | 5b: the 2015 P62ACKPT checkpoint | 1 + 34 released | `pension:2015.route.formula_unknown_checkpoint` is RESOLVED: accepted plan type 5 (DC only), rejected 1, 7, 8 and 9, the refused branch `P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF` (amendment 5's section 15 fragment, `refused_branches`) and the named disposition `off_route`; its question and refusal are kept as `superseded_question` and `superseded_refusal`. The 34 entries that depended on it (for each person's two previous plans, the six P52 status mentions and the P64 and P65 items, and the `previous_dc_only` and `duplicate_and_off_route` routes) are released, none with a dependency left. The registry gate now applies 2015's employer-DC route, equal to the declared route | +| 5a: the 2017–2023 previous-employer route | 4 + 40 released | `pension:{2017,2019,2021,2023}.route.inherited_route_amendment` keep their documentary resolution (adjudication D) and now apply: each drops its `refuse_pending_amendment_5_ruling` action, keeps its refusal as `superseded_refusal` and gains a `ruling_effect`. The 40 entries that depended on them (each wave's P64 and P65 items of both persons' two previous plans, `previous_dc_only` and `duplicate_and_off_route`) are released, none with a dependency left. The registry gate now applies every wave's employer-DC route, each equal to the declared route | -The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment 1c, pending d745), the 2017–2023 P64/P65 records and their inherited-route amendments (44, amendment 5a) and the 2017 weight with the individual weight that depends on it (2, amendment 6). +The loader's source preflight now refuses on 14 blockers: code 88 (12, amendment 1c, pending d745) and the 2017 weight with the individual weight that depends on it (2, amendment 6). ## One row per source route or field @@ -1395,8 +1396,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2017.head_er68096` | RESOLVED | ER68096: P62 AMT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4020`; `PSID/family/2017/FAM2017ER.sps:709`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 705 | | `pension:2017.head_er68097` | RESOLVED | ER68097: P62 PREV PNSN PER-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4021`; `PSID/family/2017/FAM2017ER.sps:709`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 705 | | `pension:2017.head_er68098` | RESOLVED | ER68098: P62 PCT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4022`; `PSID/family/2017/FAM2017ER.sps:710`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 705; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 706 | -| `pension:2017.head_prev1_dc_disposition` | RESOLVED | ER68110: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4034`; `PSID/family/2017/FAM2017ER.sps:714`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 711; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712 | -| `pension:2017.head_prev1_dc_amount` | RESOLVED | ER68111: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4035`; `PSID/family/2017/FAM2017ER.sps:714`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712 | +| `pension:2017.head_prev1_dc_disposition` | RESOLVED | ER68110: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4034`; `PSID/family/2017/FAM2017ER.sps:714`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 711; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712 | +| `pension:2017.head_prev1_dc_amount` | RESOLVED | ER68111: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4035`; `PSID/family/2017/FAM2017ER.sps:714`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712 | | `pension:2017.head_er68112` | RESOLVED | ER68112: P65B WTR AMT GE 50,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4036`; `PSID/family/2017/FAM2017ER.sps:714`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712 | | `pension:2017.head_er68113` | RESOLVED | ER68113: P65C WTR AMT GE 100,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4037`; `PSID/family/2017/FAM2017ER.sps:715`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 713 | | `pension:2017.head_er68114` | RESOLVED | ER68114: P65D WTR AMT GE 200,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4038`; `PSID/family/2017/FAM2017ER.sps:715`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 713 | @@ -1412,8 +1413,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2017.head_er68176` | RESOLVED | ER68176: P62 AMT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4100`; `PSID/family/2017/FAM2017ER.sps:736`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 742 | | `pension:2017.head_er68177` | RESOLVED | ER68177: P62 PREV PNSN PER-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4101`; `PSID/family/2017/FAM2017ER.sps:736`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 742; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 743 | | `pension:2017.head_er68178` | RESOLVED | ER68178: P62 PCT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4102`; `PSID/family/2017/FAM2017ER.sps:736`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 743 | -| `pension:2017.head_prev2_dc_disposition` | RESOLVED | ER68190: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4114`; `PSID/family/2017/FAM2017ER.sps:740`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 749; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750 | -| `pension:2017.head_prev2_dc_amount` | RESOLVED | ER68191: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4115`; `PSID/family/2017/FAM2017ER.sps:741`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750 | +| `pension:2017.head_prev2_dc_disposition` | RESOLVED | ER68190: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4114`; `PSID/family/2017/FAM2017ER.sps:740`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 749; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750 | +| `pension:2017.head_prev2_dc_amount` | RESOLVED | ER68191: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4115`; `PSID/family/2017/FAM2017ER.sps:741`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750 | | `pension:2017.head_er68192` | RESOLVED | ER68192: P65B WTR AMT GE 50,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4116`; `PSID/family/2017/FAM2017ER.sps:741`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 751 | | `pension:2017.head_er68193` | RESOLVED | ER68193: P65C WTR AMT GE 100,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4117`; `PSID/family/2017/FAM2017ER.sps:741`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 751 | | `pension:2017.head_er68194` | RESOLVED | ER68194: P65D WTR AMT GE 200,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4118`; `PSID/family/2017/FAM2017ER.sps:742`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 751; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 752 | @@ -1441,8 +1442,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2017.wife_er68313` | RESOLVED | ER68313: P62 AMT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4237`; `PSID/family/2017/FAM2017ER.sps:781`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 806 | | `pension:2017.wife_er68314` | RESOLVED | ER68314: P62 PREV PNSN PER-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4238`; `PSID/family/2017/FAM2017ER.sps:782`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 806 | | `pension:2017.wife_er68315` | RESOLVED | ER68315: P62 PCT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4239`; `PSID/family/2017/FAM2017ER.sps:782`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 806; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 807 | -| `pension:2017.wife_prev1_dc_disposition` | RESOLVED | ER68327: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4251`; `PSID/family/2017/FAM2017ER.sps:786`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 812 | -| `pension:2017.wife_prev1_dc_amount` | RESOLVED | ER68328: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4252`; `PSID/family/2017/FAM2017ER.sps:786`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 812 | +| `pension:2017.wife_prev1_dc_disposition` | RESOLVED | ER68327: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4251`; `PSID/family/2017/FAM2017ER.sps:786`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 812 | +| `pension:2017.wife_prev1_dc_amount` | RESOLVED | ER68328: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4252`; `PSID/family/2017/FAM2017ER.sps:786`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 812 | | `pension:2017.wife_er68329` | RESOLVED | ER68329: P65B WTR AMT GE 50,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4253`; `PSID/family/2017/FAM2017ER.sps:787`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 813 | | `pension:2017.wife_er68330` | RESOLVED | ER68330: P65C WTR AMT GE 100,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4254`; `PSID/family/2017/FAM2017ER.sps:787`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 813 | | `pension:2017.wife_er68331` | RESOLVED | ER68331: P65D WTR AMT GE 200,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4255`; `PSID/family/2017/FAM2017ER.sps:787`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 813; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 814 | @@ -1458,20 +1459,20 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2017.wife_er68393` | RESOLVED | ER68393: P62 AMT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4317`; `PSID/family/2017/FAM2017ER.sps:808`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 843; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 844 | | `pension:2017.wife_er68394` | RESOLVED | ER68394: P62 PREV PNSN PER-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4318`; `PSID/family/2017/FAM2017ER.sps:808`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 844 | | `pension:2017.wife_er68395` | RESOLVED | ER68395: P62 PCT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4319`; `PSID/family/2017/FAM2017ER.sps:809`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 844; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 845 | -| `pension:2017.wife_prev2_dc_disposition` | RESOLVED | ER68407: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4331`; `PSID/family/2017/FAM2017ER.sps:813`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 850; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851 | -| `pension:2017.wife_prev2_dc_amount` | RESOLVED | ER68408: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/family/2017/FAM2017ER.sps:4332`; `PSID/family/2017/FAM2017ER.sps:813`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851 | +| `pension:2017.wife_prev2_dc_disposition` | RESOLVED | ER68407: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4331`; `PSID/family/2017/FAM2017ER.sps:813`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 850; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851 | +| `pension:2017.wife_prev2_dc_amount` | RESOLVED | ER68408: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4332`; `PSID/family/2017/FAM2017ER.sps:813`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851 | | `pension:2017.wife_er68409` | RESOLVED | ER68409: P65B WTR AMT GE 50,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4333`; `PSID/family/2017/FAM2017ER.sps:813`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 852 | | `pension:2017.wife_er68410` | RESOLVED | ER68410: P65C WTR AMT GE 100,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4334`; `PSID/family/2017/FAM2017ER.sps:814`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 852 | | `pension:2017.wife_er68411` | RESOLVED | ER68411: P65D WTR AMT GE 200,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4335`; `PSID/family/2017/FAM2017ER.sps:814`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 852; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 853 | | `pension:2017.wife_er68412` | RESOLVED | ER68412: P65E WTR AMT GE 20,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:4336`; `PSID/family/2017/FAM2017ER.sps:814`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 853 | | `pension:2017.route.current_job` | RESOLVED | P16 identifies formula=1, account=5, both=7, DK=8 and refused=9; P20 is the present account balance for account and combined plans. Use P16, not the later P22 checkpoint, to route P20. | `PSID/documentation/capture1/q2017.pdf` p. 140; `PSID/documentation/capture1/q2017.pdf` p. 141; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.previous_combined` | RESOLVED | Both previous-employer slots ask P48/P49 only for P46=7 (both). P48=2 is an IRA rollover; P48=3 is left to accumulate. The U2 inherited convention takes only disposition 3. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 147; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2017.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 153; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2017.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 153; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2017.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2017 p. 152 P62ACKPT gives '1. DB Only (P46=1)', '3. DC Only (P46=5)', '5. All Others (P46=7 or DK/RF)'; DB-only and all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 125). | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:125` | +| `pension:2017.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2017 p. 152 P62ACKPT gives '1. DB Only (P46=1)', '3. DC Only (P46=5)', '5. All Others (P46=7 or DK/RF)'; DB-only and all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2017 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2017.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:125` | | `pension:2017.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2017.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. Application also refuses pending pension:2017.route.inherited_route_amendment. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2017.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2017.pdf` p. 141; `PSID/documentation/capture1/q2017.pdf` p. 147; `PSID/documentation/capture1/q2017.pdf` p. 153; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 667; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 668; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 685; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 721; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 767; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 768; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 785; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 812; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 821; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 822; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851 | | `pension:2017.route.previous_plan_universe` | RESOLVED | P45 asks about previous-employer pensions aside from IRA/Keogh plans, expected future benefits, and (under the displayed retirement-income condition) regular payments not yet begun. P69 repeats the plan loop at most twice; no third account balance is collected by that loop. | `PSID/documentation/capture1/q2017.pdf` p. 145; `PSID/documentation/capture1/q2017.pdf` p. 154; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.respondent_slots` | RESOLVED | The pension loop covers the family-file head/reference-person slot and designated spouse/partner slot. Other family members are not separate pension-loop respondents. P70CKPT supplies the explicit spouse-loop family-list relationship gate. Individual-record role assignment remains the separate U2 role registry task. | `PSID/documentation/capture1/q2017.pdf` p. 145; `PSID/documentation/capture1/q2017.pdf` p. 154; `docs/design/boomers2004_1946_55_comparison.md:309` | @@ -1499,8 +1500,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2019.head_er74116` | RESOLVED | ER74116: P62 AMT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4044`; `PSID/family/2019/FAM2019ER.sps:716`; `PSID/family/2019/fam2019er_codebook.pdf` p. 711 | | `pension:2019.head_er74117` | RESOLVED | ER74117: P62 PREV PNSN PER-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4045`; `PSID/family/2019/FAM2019ER.sps:716`; `PSID/family/2019/fam2019er_codebook.pdf` p. 711 | | `pension:2019.head_er74118` | RESOLVED | ER74118: P62 PCT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4046`; `PSID/family/2019/FAM2019ER.sps:716`; `PSID/family/2019/fam2019er_codebook.pdf` p. 711; `PSID/family/2019/fam2019er_codebook.pdf` p. 712 | -| `pension:2019.head_prev1_dc_disposition` | RESOLVED | ER74130: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4058`; `PSID/family/2019/FAM2019ER.sps:720`; `PSID/family/2019/fam2019er_codebook.pdf` p. 717; `PSID/family/2019/fam2019er_codebook.pdf` p. 718 | -| `pension:2019.head_prev1_dc_amount` | RESOLVED | ER74131: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4059`; `PSID/family/2019/FAM2019ER.sps:721`; `PSID/family/2019/fam2019er_codebook.pdf` p. 718 | +| `pension:2019.head_prev1_dc_disposition` | RESOLVED | ER74130: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4058`; `PSID/family/2019/FAM2019ER.sps:720`; `PSID/family/2019/fam2019er_codebook.pdf` p. 717; `PSID/family/2019/fam2019er_codebook.pdf` p. 718 | +| `pension:2019.head_prev1_dc_amount` | RESOLVED | ER74131: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4059`; `PSID/family/2019/FAM2019ER.sps:721`; `PSID/family/2019/fam2019er_codebook.pdf` p. 718 | | `pension:2019.head_er74132` | RESOLVED | ER74132: P65B WTR AMT GE 50,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4060`; `PSID/family/2019/FAM2019ER.sps:721`; `PSID/family/2019/fam2019er_codebook.pdf` p. 718 | | `pension:2019.head_er74133` | RESOLVED | ER74133: P65C WTR AMT GE 100,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4061`; `PSID/family/2019/FAM2019ER.sps:721`; `PSID/family/2019/fam2019er_codebook.pdf` p. 718; `PSID/family/2019/fam2019er_codebook.pdf` p. 719 | | `pension:2019.head_er74134` | RESOLVED | ER74134: P65D WTR AMT GE 200,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4062`; `PSID/family/2019/FAM2019ER.sps:722`; `PSID/family/2019/fam2019er_codebook.pdf` p. 719 | @@ -1516,8 +1517,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2019.head_er74194` | RESOLVED | ER74194: P62 AMT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4122`; `PSID/family/2019/FAM2019ER.sps:742`; `PSID/family/2019/fam2019er_codebook.pdf` p. 747 | | `pension:2019.head_er74195` | RESOLVED | ER74195: P62 PREV PNSN PER-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4123`; `PSID/family/2019/FAM2019ER.sps:742`; `PSID/family/2019/fam2019er_codebook.pdf` p. 747; `PSID/family/2019/fam2019er_codebook.pdf` p. 748 | | `pension:2019.head_er74196` | RESOLVED | ER74196: P62 PCT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4124`; `PSID/family/2019/FAM2019ER.sps:742`; `PSID/family/2019/fam2019er_codebook.pdf` p. 748 | -| `pension:2019.head_prev2_dc_disposition` | RESOLVED | ER74208: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4136`; `PSID/family/2019/FAM2019ER.sps:746`; `PSID/family/2019/fam2019er_codebook.pdf` p. 754; `PSID/family/2019/fam2019er_codebook.pdf` p. 755 | -| `pension:2019.head_prev2_dc_amount` | RESOLVED | ER74209: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4137`; `PSID/family/2019/FAM2019ER.sps:747`; `PSID/family/2019/fam2019er_codebook.pdf` p. 755 | +| `pension:2019.head_prev2_dc_disposition` | RESOLVED | ER74208: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4136`; `PSID/family/2019/FAM2019ER.sps:746`; `PSID/family/2019/fam2019er_codebook.pdf` p. 754; `PSID/family/2019/fam2019er_codebook.pdf` p. 755 | +| `pension:2019.head_prev2_dc_amount` | RESOLVED | ER74209: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4137`; `PSID/family/2019/FAM2019ER.sps:747`; `PSID/family/2019/fam2019er_codebook.pdf` p. 755 | | `pension:2019.head_er74210` | RESOLVED | ER74210: P65B WTR AMT GE 50,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4138`; `PSID/family/2019/FAM2019ER.sps:747`; `PSID/family/2019/fam2019er_codebook.pdf` p. 755; `PSID/family/2019/fam2019er_codebook.pdf` p. 756 | | `pension:2019.head_er74211` | RESOLVED | ER74211: P65C WTR AMT GE 100,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4139`; `PSID/family/2019/FAM2019ER.sps:747`; `PSID/family/2019/fam2019er_codebook.pdf` p. 756 | | `pension:2019.head_er74212` | RESOLVED | ER74212: P65D WTR AMT GE 200,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4140`; `PSID/family/2019/FAM2019ER.sps:748`; `PSID/family/2019/fam2019er_codebook.pdf` p. 756; `PSID/family/2019/fam2019er_codebook.pdf` p. 757 | @@ -1545,8 +1546,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2019.wife_er74323` | RESOLVED | ER74323: P62 AMT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4251`; `PSID/family/2019/FAM2019ER.sps:785`; `PSID/family/2019/fam2019er_codebook.pdf` p. 805 | | `pension:2019.wife_er74324` | RESOLVED | ER74324: P62 PREV PNSN PER-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4252`; `PSID/family/2019/FAM2019ER.sps:785`; `PSID/family/2019/fam2019er_codebook.pdf` p. 806 | | `pension:2019.wife_er74325` | RESOLVED | ER74325: P62 PCT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4253`; `PSID/family/2019/FAM2019ER.sps:785`; `PSID/family/2019/fam2019er_codebook.pdf` p. 806 | -| `pension:2019.wife_prev1_dc_disposition` | RESOLVED | ER74337: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4265`; `PSID/family/2019/FAM2019ER.sps:789`; `PSID/family/2019/fam2019er_codebook.pdf` p. 811; `PSID/family/2019/fam2019er_codebook.pdf` p. 812 | -| `pension:2019.wife_prev1_dc_amount` | RESOLVED | ER74338: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4266`; `PSID/family/2019/FAM2019ER.sps:790`; `PSID/family/2019/fam2019er_codebook.pdf` p. 812 | +| `pension:2019.wife_prev1_dc_disposition` | RESOLVED | ER74337: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4265`; `PSID/family/2019/FAM2019ER.sps:789`; `PSID/family/2019/fam2019er_codebook.pdf` p. 811; `PSID/family/2019/fam2019er_codebook.pdf` p. 812 | +| `pension:2019.wife_prev1_dc_amount` | RESOLVED | ER74338: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4266`; `PSID/family/2019/FAM2019ER.sps:790`; `PSID/family/2019/fam2019er_codebook.pdf` p. 812 | | `pension:2019.wife_er74339` | RESOLVED | ER74339: P65B WTR AMT GE 50,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4267`; `PSID/family/2019/FAM2019ER.sps:790`; `PSID/family/2019/fam2019er_codebook.pdf` p. 812 | | `pension:2019.wife_er74340` | RESOLVED | ER74340: P65C WTR AMT GE 100,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4268`; `PSID/family/2019/FAM2019ER.sps:790`; `PSID/family/2019/fam2019er_codebook.pdf` p. 812; `PSID/family/2019/fam2019er_codebook.pdf` p. 813 | | `pension:2019.wife_er74341` | RESOLVED | ER74341: P65D WTR AMT GE 200,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4269`; `PSID/family/2019/FAM2019ER.sps:791`; `PSID/family/2019/fam2019er_codebook.pdf` p. 813 | @@ -1562,20 +1563,20 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2019.wife_er74401` | RESOLVED | ER74401: P62 AMT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4329`; `PSID/family/2019/FAM2019ER.sps:811`; `PSID/family/2019/fam2019er_codebook.pdf` p. 841; `PSID/family/2019/fam2019er_codebook.pdf` p. 842 | | `pension:2019.wife_er74402` | RESOLVED | ER74402: P62 PREV PNSN PER-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4330`; `PSID/family/2019/FAM2019ER.sps:811`; `PSID/family/2019/fam2019er_codebook.pdf` p. 842 | | `pension:2019.wife_er74403` | RESOLVED | ER74403: P62 PCT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4331`; `PSID/family/2019/FAM2019ER.sps:811`; `PSID/family/2019/fam2019er_codebook.pdf` p. 842; `PSID/family/2019/fam2019er_codebook.pdf` p. 843 | -| `pension:2019.wife_prev2_dc_disposition` | RESOLVED | ER74415: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4343`; `PSID/family/2019/FAM2019ER.sps:815`; `PSID/family/2019/fam2019er_codebook.pdf` p. 848; `PSID/family/2019/fam2019er_codebook.pdf` p. 849 | -| `pension:2019.wife_prev2_dc_amount` | RESOLVED | ER74416: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/family/2019/FAM2019ER.sps:4344`; `PSID/family/2019/FAM2019ER.sps:816`; `PSID/family/2019/fam2019er_codebook.pdf` p. 849 | +| `pension:2019.wife_prev2_dc_disposition` | RESOLVED | ER74415: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4343`; `PSID/family/2019/FAM2019ER.sps:815`; `PSID/family/2019/fam2019er_codebook.pdf` p. 848; `PSID/family/2019/fam2019er_codebook.pdf` p. 849 | +| `pension:2019.wife_prev2_dc_amount` | RESOLVED | ER74416: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4344`; `PSID/family/2019/FAM2019ER.sps:816`; `PSID/family/2019/fam2019er_codebook.pdf` p. 849 | | `pension:2019.wife_er74417` | RESOLVED | ER74417: P65B WTR AMT GE 50,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4345`; `PSID/family/2019/FAM2019ER.sps:816`; `PSID/family/2019/fam2019er_codebook.pdf` p. 849; `PSID/family/2019/fam2019er_codebook.pdf` p. 850 | | `pension:2019.wife_er74418` | RESOLVED | ER74418: P65C WTR AMT GE 100,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4346`; `PSID/family/2019/FAM2019ER.sps:816`; `PSID/family/2019/fam2019er_codebook.pdf` p. 850 | | `pension:2019.wife_er74419` | RESOLVED | ER74419: P65D WTR AMT GE 200,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4347`; `PSID/family/2019/FAM2019ER.sps:817`; `PSID/family/2019/fam2019er_codebook.pdf` p. 850; `PSID/family/2019/fam2019er_codebook.pdf` p. 851 | | `pension:2019.wife_er74420` | RESOLVED | ER74420: P65E WTR AMT GE 20,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:4348`; `PSID/family/2019/FAM2019ER.sps:817`; `PSID/family/2019/fam2019er_codebook.pdf` p. 851 | | `pension:2019.route.current_job` | RESOLVED | P16 identifies formula=1, account=5, both=7, DK=8 and refused=9; P20 is the present account balance for account and combined plans. Use P16, not the later P22 checkpoint, to route P20. | `PSID/documentation/capture1/q2019.pdf` p. 152; `PSID/documentation/capture1/q2019.pdf` p. 153; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.previous_combined` | RESOLVED | Both previous-employer slots ask P48/P49 only for P46=7 (both). P48=2 is an IRA rollover; P48=3 is left to accumulate. The U2 inherited convention takes only disposition 3. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 158; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2019.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2019.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2019.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2019 p. 163 P62ACKPT gives the same three P62ACKPT branches, followed by 'Plan with Former Employer - Defined Contribution (P46=DC Only)'. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 126). | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:126` | +| `pension:2019.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2019 p. 163 P62ACKPT gives the same three P62ACKPT branches, followed by 'Plan with Former Employer - Defined Contribution (P46=DC Only)'. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2019 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2019.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:126` | | `pension:2019.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2019.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. Application also refuses pending pension:2019.route.inherited_route_amendment. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2019.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2019.pdf` p. 153; `PSID/documentation/capture1/q2019.pdf` p. 158; `PSID/documentation/capture1/q2019.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2019/fam2019er_codebook.pdf` p. 677; `PSID/family/2019/fam2019er_codebook.pdf` p. 692; `PSID/family/2019/fam2019er_codebook.pdf` p. 718; `PSID/family/2019/fam2019er_codebook.pdf` p. 727; `PSID/family/2019/fam2019er_codebook.pdf` p. 755; `PSID/family/2019/fam2019er_codebook.pdf` p. 770; `PSID/family/2019/fam2019er_codebook.pdf` p. 786; `PSID/family/2019/fam2019er_codebook.pdf` p. 812; `PSID/family/2019/fam2019er_codebook.pdf` p. 821; `PSID/family/2019/fam2019er_codebook.pdf` p. 849 | | `pension:2019.route.previous_plan_universe` | RESOLVED | P45 asks about previous-employer pensions aside from IRA/Keogh plans, expected future benefits, and (under the displayed retirement-income condition) regular payments not yet begun. P69 repeats the plan loop at most twice; no third account balance is collected by that loop. | `PSID/documentation/capture1/q2019.pdf` p. 156; `PSID/documentation/capture1/q2019.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.respondent_slots` | RESOLVED | The pension loop covers the family-file head/reference-person slot and designated spouse/partner slot. Other family members are not separate pension-loop respondents. P70CKPT supplies the explicit spouse-loop family-list relationship gate. Individual-record role assignment remains the separate U2 role registry task. | `PSID/documentation/capture1/q2019.pdf` p. 156; `PSID/documentation/capture1/q2019.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | @@ -1603,8 +1604,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2021.head_er80238` | RESOLVED | ER80238: P62 AMT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3618`; `PSID/family/2021/FAM2021ER.sps:756`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 758 | | `pension:2021.head_er80239` | RESOLVED | ER80239: P62 PREV PNSN PER-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3619`; `PSID/family/2021/FAM2021ER.sps:757`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 758; `PSID/family/2021/FAM2021ER_formats.sps:19377` | | `pension:2021.head_er80240` | RESOLVED | ER80240: P62 PCT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3620`; `PSID/family/2021/FAM2021ER.sps:757`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 758; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 759 | -| `pension:2021.head_prev1_dc_disposition` | RESOLVED | ER80252: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3632`; `PSID/family/2021/FAM2021ER.sps:761`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 764; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765; `PSID/family/2021/FAM2021ER_formats.sps:19473` | -| `pension:2021.head_prev1_dc_amount` | RESOLVED | ER80253: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3633`; `PSID/family/2021/FAM2021ER.sps:761`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765 | +| `pension:2021.head_prev1_dc_disposition` | RESOLVED | ER80252: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3632`; `PSID/family/2021/FAM2021ER.sps:761`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 764; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765; `PSID/family/2021/FAM2021ER_formats.sps:19473` | +| `pension:2021.head_prev1_dc_amount` | RESOLVED | ER80253: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3633`; `PSID/family/2021/FAM2021ER.sps:761`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765 | | `pension:2021.head_er80254` | RESOLVED | ER80254: P65B WTR AMT GE 50,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3634`; `PSID/family/2021/FAM2021ER.sps:762`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765; `PSID/family/2021/FAM2021ER_formats.sps:19485` | | `pension:2021.head_er80255` | RESOLVED | ER80255: P65C WTR AMT GE 100,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3635`; `PSID/family/2021/FAM2021ER.sps:762`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 766; `PSID/family/2021/FAM2021ER_formats.sps:19494` | | `pension:2021.head_er80256` | RESOLVED | ER80256: P65D WTR AMT GE 200,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3636`; `PSID/family/2021/FAM2021ER.sps:762`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 766; `PSID/family/2021/FAM2021ER_formats.sps:19503` | @@ -1620,8 +1621,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2021.head_er80316` | RESOLVED | ER80316: P62 AMT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3696`; `PSID/family/2021/FAM2021ER.sps:782`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 794 | | `pension:2021.head_er80317` | RESOLVED | ER80317: P62 PREV PNSN PER-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3697`; `PSID/family/2021/FAM2021ER.sps:783`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 794; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 795; `PSID/family/2021/FAM2021ER_formats.sps:20268` | | `pension:2021.head_er80318` | RESOLVED | ER80318: P62 PCT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3698`; `PSID/family/2021/FAM2021ER.sps:783`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 795 | -| `pension:2021.head_prev2_dc_disposition` | RESOLVED | ER80330: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3710`; `PSID/family/2021/FAM2021ER.sps:787`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 801; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802; `PSID/family/2021/FAM2021ER_formats.sps:20364` | -| `pension:2021.head_prev2_dc_amount` | RESOLVED | ER80331: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3711`; `PSID/family/2021/FAM2021ER.sps:787`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802 | +| `pension:2021.head_prev2_dc_disposition` | RESOLVED | ER80330: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3710`; `PSID/family/2021/FAM2021ER.sps:787`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 801; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802; `PSID/family/2021/FAM2021ER_formats.sps:20364` | +| `pension:2021.head_prev2_dc_amount` | RESOLVED | ER80331: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3711`; `PSID/family/2021/FAM2021ER.sps:787`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802 | | `pension:2021.head_er80332` | RESOLVED | ER80332: P65B WTR AMT GE 50,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3712`; `PSID/family/2021/FAM2021ER.sps:788`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 803; `PSID/family/2021/FAM2021ER_formats.sps:20376` | | `pension:2021.head_er80333` | RESOLVED | ER80333: P65C WTR AMT GE 100,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3713`; `PSID/family/2021/FAM2021ER.sps:788`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 803; `PSID/family/2021/FAM2021ER_formats.sps:20385` | | `pension:2021.head_er80334` | RESOLVED | ER80334: P65D WTR AMT GE 200,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3714`; `PSID/family/2021/FAM2021ER.sps:788`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 803; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 804; `PSID/family/2021/FAM2021ER_formats.sps:20394` | @@ -1649,8 +1650,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2021.wife_er80444` | RESOLVED | ER80444: P62 AMT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3824`; `PSID/family/2021/FAM2021ER.sps:825`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 852 | | `pension:2021.wife_er80445` | RESOLVED | ER80445: P62 PREV PNSN PER-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3825`; `PSID/family/2021/FAM2021ER.sps:825`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 853; `PSID/family/2021/FAM2021ER_formats.sps:21747` | | `pension:2021.wife_er80446` | RESOLVED | ER80446: P62 PCT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3826`; `PSID/family/2021/FAM2021ER.sps:826`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 853 | -| `pension:2021.wife_prev1_dc_disposition` | RESOLVED | ER80458: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3838`; `PSID/family/2021/FAM2021ER.sps:830`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 858; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859; `PSID/family/2021/FAM2021ER_formats.sps:21843` | -| `pension:2021.wife_prev1_dc_amount` | RESOLVED | ER80459: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3839`; `PSID/family/2021/FAM2021ER.sps:830`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859 | +| `pension:2021.wife_prev1_dc_disposition` | RESOLVED | ER80458: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3838`; `PSID/family/2021/FAM2021ER.sps:830`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 858; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859; `PSID/family/2021/FAM2021ER_formats.sps:21843` | +| `pension:2021.wife_prev1_dc_amount` | RESOLVED | ER80459: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3839`; `PSID/family/2021/FAM2021ER.sps:830`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859 | | `pension:2021.wife_er80460` | RESOLVED | ER80460: P65B WTR AMT GE 50,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3840`; `PSID/family/2021/FAM2021ER.sps:830`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859; `PSID/family/2021/FAM2021ER_formats.sps:21855` | | `pension:2021.wife_er80461` | RESOLVED | ER80461: P65C WTR AMT GE 100,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3841`; `PSID/family/2021/FAM2021ER.sps:831`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 860; `PSID/family/2021/FAM2021ER_formats.sps:21864` | | `pension:2021.wife_er80462` | RESOLVED | ER80462: P65D WTR AMT GE 200,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3842`; `PSID/family/2021/FAM2021ER.sps:831`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 860; `PSID/family/2021/FAM2021ER_formats.sps:21873` | @@ -1666,20 +1667,20 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2021.wife_er80522` | RESOLVED | ER80522: P62 AMT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3902`; `PSID/family/2021/FAM2021ER.sps:851`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 888; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 889 | | `pension:2021.wife_er80523` | RESOLVED | ER80523: P62 PREV PNSN PER-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3903`; `PSID/family/2021/FAM2021ER.sps:851`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 889; `PSID/family/2021/FAM2021ER_formats.sps:22653` | | `pension:2021.wife_er80524` | RESOLVED | ER80524: P62 PCT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3904`; `PSID/family/2021/FAM2021ER.sps:852`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 889; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 890 | -| `pension:2021.wife_prev2_dc_disposition` | RESOLVED | ER80536: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3916`; `PSID/family/2021/FAM2021ER.sps:856`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 895; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896; `PSID/family/2021/FAM2021ER_formats.sps:22749` | -| `pension:2021.wife_prev2_dc_amount` | RESOLVED | ER80537: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/family/2021/FAM2021ER.sps:3917`; `PSID/family/2021/FAM2021ER.sps:856`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896 | +| `pension:2021.wife_prev2_dc_disposition` | RESOLVED | ER80536: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3916`; `PSID/family/2021/FAM2021ER.sps:856`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 895; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896; `PSID/family/2021/FAM2021ER_formats.sps:22749` | +| `pension:2021.wife_prev2_dc_amount` | RESOLVED | ER80537: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3917`; `PSID/family/2021/FAM2021ER.sps:856`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896 | | `pension:2021.wife_er80538` | RESOLVED | ER80538: P65B WTR AMT GE 50,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3918`; `PSID/family/2021/FAM2021ER.sps:856`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 897; `PSID/family/2021/FAM2021ER_formats.sps:22761` | | `pension:2021.wife_er80539` | RESOLVED | ER80539: P65C WTR AMT GE 100,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3919`; `PSID/family/2021/FAM2021ER.sps:857`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 897; `PSID/family/2021/FAM2021ER_formats.sps:22770` | | `pension:2021.wife_er80540` | RESOLVED | ER80540: P65D WTR AMT GE 200,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3920`; `PSID/family/2021/FAM2021ER.sps:857`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 897; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 898; `PSID/family/2021/FAM2021ER_formats.sps:22779` | | `pension:2021.wife_er80541` | RESOLVED | ER80541: P65E WTR AMT GE 20,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:3921`; `PSID/family/2021/FAM2021ER.sps:857`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 898; `PSID/family/2021/FAM2021ER_formats.sps:22788` | | `pension:2021.route.current_job` | RESOLVED | P16 identifies formula=1, account=5, both=7, DK=8 and refused=9; P20 is the present account balance for account and combined plans. Use P16, not the later P22 checkpoint, to route P20. | `PSID/documentation/capture1/q2021.pdf` p. 285; `PSID/documentation/capture1/q2021.pdf` p. 287; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.previous_combined` | RESOLVED | Both previous-employer slots ask P48/P49 only for P46=7 (both). P48=2 is an IRA rollover; P48=3 is left to accumulate. The U2 inherited convention takes only disposition 3. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 295; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2021.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 304; `PSID/documentation/capture1/q2021.pdf` p. 305; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2021.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 304; `PSID/documentation/capture1/q2021.pdf` p. 305; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2021.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2021 p. 304 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 127). | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:127` | +| `pension:2021.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2021 p. 304 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2021 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2021.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:127` | | `pension:2021.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2021.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. Application also refuses pending pension:2021.route.inherited_route_amendment. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 304; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2021.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 304; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2021.pdf` p. 287; `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 724; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 738; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 739; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 774; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 817; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 832; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 833; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 868; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896 | | `pension:2021.route.previous_plan_universe` | RESOLVED | P45 asks about previous-employer pensions aside from IRA/Keogh plans, expected future benefits, and (under the displayed retirement-income condition) regular payments not yet begun. P69 repeats the plan loop at most twice; no third account balance is collected by that loop. | `PSID/documentation/capture1/q2021.pdf` p. 293; `PSID/documentation/capture1/q2021.pdf` p. 307; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.respondent_slots` | RESOLVED | The pension loop covers the family-file head/reference-person slot and designated spouse/partner slot. Other family members are not separate pension-loop respondents. P70CKPT supplies the explicit spouse-loop family-list relationship gate. Individual-record role assignment remains the separate U2 role registry task. | `PSID/documentation/capture1/q2021.pdf` p. 293; `PSID/documentation/capture1/q2021.pdf` p. 307; `docs/design/boomers2004_1946_55_comparison.md:309` | @@ -1708,8 +1709,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2023.head_er84208` | RESOLVED | ER84208: P62 AMT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3539`; `PSID/family/2023/FAM2023ER.sps:746`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 745 | | `pension:2023.head_er84209` | RESOLVED | ER84209: P62 PREV PNSN PER-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3540`; `PSID/family/2023/FAM2023ER.sps:747`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 745; `PSID/family/2023/FAM2023ER_formats.sps:19118` | | `pension:2023.head_er84210` | RESOLVED | ER84210: P62 PCT EXPECT REC PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3541`; `PSID/family/2023/FAM2023ER.sps:747`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 745; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 746 | -| `pension:2023.head_prev1_dc_disposition` | RESOLVED | ER84222: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3553`; `PSID/family/2023/FAM2023ER.sps:751`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 751; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752; `PSID/family/2023/FAM2023ER_formats.sps:19214` | -| `pension:2023.head_prev1_dc_amount` | RESOLVED | ER84223: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3554`; `PSID/family/2023/FAM2023ER.sps:751`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752 | +| `pension:2023.head_prev1_dc_disposition` | RESOLVED | ER84222: P64 WHAT DID W/PREV PNSN-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3553`; `PSID/family/2023/FAM2023ER.sps:751`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 751; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752; `PSID/family/2023/FAM2023ER_formats.sps:19214` | +| `pension:2023.head_prev1_dc_amount` | RESOLVED | ER84223: P65 ACCT AMT PREV PNSN NOW-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3554`; `PSID/family/2023/FAM2023ER.sps:751`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752 | | `pension:2023.head_er84224` | RESOLVED | ER84224: P65B WTR AMT GE 50,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3555`; `PSID/family/2023/FAM2023ER.sps:752`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752; `PSID/family/2023/FAM2023ER_formats.sps:19226` | | `pension:2023.head_er84225` | RESOLVED | ER84225: P65C WTR AMT GE 100,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3556`; `PSID/family/2023/FAM2023ER.sps:752`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 753; `PSID/family/2023/FAM2023ER_formats.sps:19235` | | `pension:2023.head_er84226` | RESOLVED | ER84226: P65D WTR AMT GE 200,000-#1 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3557`; `PSID/family/2023/FAM2023ER.sps:752`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 753; `PSID/family/2023/FAM2023ER_formats.sps:19244` | @@ -1725,8 +1726,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2023.head_er84286` | RESOLVED | ER84286: P62 AMT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3617`; `PSID/family/2023/FAM2023ER.sps:772`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 781 | | `pension:2023.head_er84287` | RESOLVED | ER84287: P62 PREV PNSN PER-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3618`; `PSID/family/2023/FAM2023ER.sps:773`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 781; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 782; `PSID/family/2023/FAM2023ER_formats.sps:20009` | | `pension:2023.head_er84288` | RESOLVED | ER84288: P62 PCT EXPECT REC PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3619`; `PSID/family/2023/FAM2023ER.sps:773`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 782 | -| `pension:2023.head_prev2_dc_disposition` | RESOLVED | ER84300: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3631`; `PSID/family/2023/FAM2023ER.sps:777`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 788; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789; `PSID/family/2023/FAM2023ER_formats.sps:20105` | -| `pension:2023.head_prev2_dc_amount` | RESOLVED | ER84301: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3632`; `PSID/family/2023/FAM2023ER.sps:777`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789 | +| `pension:2023.head_prev2_dc_disposition` | RESOLVED | ER84300: P64 WHAT DID W/PREV PNSN-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3631`; `PSID/family/2023/FAM2023ER.sps:777`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 788; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789; `PSID/family/2023/FAM2023ER_formats.sps:20105` | +| `pension:2023.head_prev2_dc_amount` | RESOLVED | ER84301: P65 ACCT AMT PREV PNSN NOW-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3632`; `PSID/family/2023/FAM2023ER.sps:777`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789 | | `pension:2023.head_er84302` | RESOLVED | ER84302: P65B WTR AMT GE 50,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3633`; `PSID/family/2023/FAM2023ER.sps:778`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 790; `PSID/family/2023/FAM2023ER_formats.sps:20117` | | `pension:2023.head_er84303` | RESOLVED | ER84303: P65C WTR AMT GE 100,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3634`; `PSID/family/2023/FAM2023ER.sps:778`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 790; `PSID/family/2023/FAM2023ER_formats.sps:20126` | | `pension:2023.head_er84304` | RESOLVED | ER84304: P65D WTR AMT GE 200,000-#2 - RP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3635`; `PSID/family/2023/FAM2023ER.sps:778`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 790; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 791; `PSID/family/2023/FAM2023ER_formats.sps:20135` | @@ -1754,8 +1755,8 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2023.wife_er84414` | RESOLVED | ER84414: P62 AMT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3745`; `PSID/family/2023/FAM2023ER.sps:815`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 839 | | `pension:2023.wife_er84415` | RESOLVED | ER84415: P62 PREV PNSN PER-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3746`; `PSID/family/2023/FAM2023ER.sps:815`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 840; `PSID/family/2023/FAM2023ER_formats.sps:21490` | | `pension:2023.wife_er84416` | RESOLVED | ER84416: P62 PCT EXPECT REC PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3747`; `PSID/family/2023/FAM2023ER.sps:816`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 840 | -| `pension:2023.wife_prev1_dc_disposition` | RESOLVED | ER84428: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3759`; `PSID/family/2023/FAM2023ER.sps:820`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 845; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846; `PSID/family/2023/FAM2023ER_formats.sps:21586` | -| `pension:2023.wife_prev1_dc_amount` | RESOLVED | ER84429: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3760`; `PSID/family/2023/FAM2023ER.sps:820`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846 | +| `pension:2023.wife_prev1_dc_disposition` | RESOLVED | ER84428: P64 WHAT DID W/PREV PNSN-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3759`; `PSID/family/2023/FAM2023ER.sps:820`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 845; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846; `PSID/family/2023/FAM2023ER_formats.sps:21586` | +| `pension:2023.wife_prev1_dc_amount` | RESOLVED | ER84429: P65 ACCT AMT PREV PNSN NOW-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3760`; `PSID/family/2023/FAM2023ER.sps:820`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846 | | `pension:2023.wife_er84430` | RESOLVED | ER84430: P65B WTR AMT GE 50,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3761`; `PSID/family/2023/FAM2023ER.sps:820`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846; `PSID/family/2023/FAM2023ER_formats.sps:21598` | | `pension:2023.wife_er84431` | RESOLVED | ER84431: P65C WTR AMT GE 100,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3762`; `PSID/family/2023/FAM2023ER.sps:821`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 847; `PSID/family/2023/FAM2023ER_formats.sps:21607` | | `pension:2023.wife_er84432` | RESOLVED | ER84432: P65D WTR AMT GE 200,000-#1 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3763`; `PSID/family/2023/FAM2023ER.sps:821`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 847; `PSID/family/2023/FAM2023ER_formats.sps:21616` | @@ -1771,20 +1772,20 @@ The loader's source preflight now refuses on 58 blockers: code 88 (12, amendment | `pension:2023.wife_er84492` | RESOLVED | ER84492: P62 AMT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3823`; `PSID/family/2023/FAM2023ER.sps:841`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 875; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 876 | | `pension:2023.wife_er84493` | RESOLVED | ER84493: P62 PREV PNSN PER-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3824`; `PSID/family/2023/FAM2023ER.sps:841`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 876; `PSID/family/2023/FAM2023ER_formats.sps:22396` | | `pension:2023.wife_er84494` | RESOLVED | ER84494: P62 PCT EXPECT REC PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3825`; `PSID/family/2023/FAM2023ER.sps:842`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 876; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 877 | -| `pension:2023.wife_prev2_dc_disposition` | RESOLVED | ER84506: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3837`; `PSID/family/2023/FAM2023ER.sps:846`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 882; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883; `PSID/family/2023/FAM2023ER_formats.sps:22492` | -| `pension:2023.wife_prev2_dc_amount` | RESOLVED | ER84507: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/family/2023/FAM2023ER.sps:3838`; `PSID/family/2023/FAM2023ER.sps:846`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883 | +| `pension:2023.wife_prev2_dc_disposition` | RESOLVED | ER84506: P64 WHAT DID W/PREV PNSN-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3837`; `PSID/family/2023/FAM2023ER.sps:846`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 882; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883; `PSID/family/2023/FAM2023ER_formats.sps:22492` | +| `pension:2023.wife_prev2_dc_amount` | RESOLVED | ER84507: P65 ACCT AMT PREV PNSN NOW-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3838`; `PSID/family/2023/FAM2023ER.sps:846`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883 | | `pension:2023.wife_er84508` | RESOLVED | ER84508: P65B WTR AMT GE 50,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3839`; `PSID/family/2023/FAM2023ER.sps:846`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 884; `PSID/family/2023/FAM2023ER_formats.sps:22504` | | `pension:2023.wife_er84509` | RESOLVED | ER84509: P65C WTR AMT GE 100,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3840`; `PSID/family/2023/FAM2023ER.sps:847`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 884; `PSID/family/2023/FAM2023ER_formats.sps:22513` | | `pension:2023.wife_er84510` | RESOLVED | ER84510: P65D WTR AMT GE 200,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3841`; `PSID/family/2023/FAM2023ER.sps:847`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 884; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 885; `PSID/family/2023/FAM2023ER_formats.sps:22522` | | `pension:2023.wife_er84511` | RESOLVED | ER84511: P65E WTR AMT GE 20,000-#2 - SP. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:3842`; `PSID/family/2023/FAM2023ER.sps:847`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 885; `PSID/family/2023/FAM2023ER_formats.sps:22531` | | `pension:2023.route.current_job` | RESOLVED | P16 identifies formula=1, account=5, both=7, DK=8 and refused=9; P20 is the present account balance for account and combined plans. Use P16, not the later P22 checkpoint, to route P20. | `PSID/documentation/capture1/q2023.pdf` p. 283; `PSID/documentation/capture1/q2023.pdf` p. 285; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.previous_combined` | RESOLVED | Both previous-employer slots ask P48/P49 only for P46=7 (both). P48=2 is an IRA rollover; P48=3 is left to accumulate. The U2 inherited convention takes only disposition 3. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 293; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2023.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 302; `PSID/documentation/capture1/q2023.pdf` p. 303; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2023.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 302; `PSID/documentation/capture1/q2023.pdf` p. 303; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2023.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2023 p. 302 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. The documentary route is resolved, but the ratified section 15 contract still lists the generic account/formula/dk previous route. Application stays refused, and dependent P64/P65 entries stay blocked, until Max rules on u2-draft-4 amendment 5 (independent adjudication D; EV/phase2-20260927/out/u2-adjudicate.md line 128). | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:128` | +| `pension:2023.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2023 p. 302 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2023 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2023.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:128` | | `pension:2023.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2023.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. Application also refuses pending pension:2023.route.inherited_route_amendment. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 302; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | +| `pension:2023.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 302; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2023.pdf` p. 285; `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 711; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 725; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 726; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 761; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 804; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 819; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 820; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 855; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883 | | `pension:2023.route.previous_plan_universe` | RESOLVED | P45 asks about previous-employer pensions aside from IRA/Keogh plans, expected future benefits, and (under the displayed retirement-income condition) regular payments not yet begun. P69 repeats the plan loop at most twice; no third account balance is collected by that loop. | `PSID/documentation/capture1/q2023.pdf` p. 291; `PSID/documentation/capture1/q2023.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.respondent_slots` | RESOLVED | The pension loop covers the family-file head/reference-person slot and designated spouse/partner slot. Other family members are not separate pension-loop respondents. P70CKPT supplies the explicit spouse-loop family-list relationship gate. Individual-record role assignment remains the separate U2 role registry task. | `PSID/documentation/capture1/q2023.pdf` p. 291; `PSID/documentation/capture1/q2023.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index a873abfb1..7511488bd 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -399,6 +399,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 4: `wealth.json` (the nine flags and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the wealth blocker row, the d637 section and the nine rows); `u2_source_registry.py` (amendment 4's scope) and `sources.py` (the wealth byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`, and `tests/data/test_track_u2_income_wealth.py`; and this section. - Amendment 5, builder: `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the declared employer-DC route by wave and `declared_dc_route`), `invented.py` (the invented DC frames use it), `__init__.py` (docstring); the specification's §19 and `u1_identity.json`; `tests/track_u2/test_u2_employer_dc.py`; and this section. - Amendment 5b: `pension.json` (the 2015 checkpoint, its 34 dependents and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the P62A blocker row, the d637 section and 35 rows); `u2_source_registry.py` (5b's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. +- Amendment 5a: `pension.json` (the four inherited-route amendments, their 40 dependents and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row, the d637 section and 44 rows); `u2_source_registry.py` (5a's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 84e014e0a..97c2eb7f4 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -161,6 +161,20 @@ "accepted_conditional_types", ), }, + "5a": { + "registry": "pension", + "entries": tuple( + f"{wave}.route.inherited_route_amendment" + for wave in _LATER_WAVES[1:] + ), + "ruling": { + **_D637, + "amendment": "5a", + "replaces_draft_3_lines": [1173, 1174, 1175, 1176], + }, + "fields": {"status": "RESOLVED"}, + "absent": ("action", "refusal", "question", "blocking_dependencies"), + }, } #: The d637 amendment each ruled entry belongs to, by (registry, id). _AMENDMENT_OF_ENTRY = { diff --git a/src/populace_dynamics/uniform_cut_track_u2/loader.py b/src/populace_dynamics/uniform_cut_track_u2/loader.py index 3656048f0..496448d7b 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/loader.py +++ b/src/populace_dynamics/uniform_cut_track_u2/loader.py @@ -12,16 +12,15 @@ the support plan and the role of every amended relationship code -- must be RESOLVED with no blocking dependency, under the committed, pinned registries. It refuses at the committed registries: code 88 - (every wave) and the revised 2017 cross-section weight are TO VERIFY, - and every 2017-2023 P64/P65 record waits on the registry commit for - amendment 5a. The d1090 + (every wave) and the revised 2017 cross-section weight are TO + VERIFY. The d1090 registry commit (section 16c) replaced the refused 2015 code-20 and 2019-2023 code-90/92 routes with the ratified family-unit exclusions, which released the income and pension entries that depended on them; the d637 registry commits (section 16b) resolve the ratified amendments one by one: amendment 2's spouse age/sex slot metadata, - amendment 3's crosswalks, amendment 4's wealth flags and 5b's 2015 - P62A checkpoint so far. + amendment 3's crosswalks, amendment 4's wealth flags, 5b's 2015 P62A + checkpoint and 5a's 2017-2023 P64/P65 route so far. 2. **Source identity**: each staged setup file and codebook the registries cite is rehashed against the registry's recorded SHA-256, and every registry layout -- the family files' and the individual diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 1433d25cc..c965881d5 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -236,8 +236,9 @@ #: and 3 moved the income bytes (the later spouse age and sex records #: became slot metadata; the spouse-retirement crosswalks became #: non-executable metadata), amendment 4 the wealth bytes (the nine -#: corrected accuracy flags record the ruling) and amendment 5b the -#: pension bytes (the 2015 P62ACKPT checkpoint and its dependents). +#: corrected accuracy flags record the ruling) and amendments 5b and 5a +#: the pension bytes (the 2015 P62ACKPT checkpoint, the 2017-2023 +#: inherited-route amendments and the P64/P65 entries they released). REGISTRY_SHA256: dict[str, str] = { "income": ( "ac8310a166a66fa71a39d19cb79dec0dda5c41ff5a7033e535d70ebffe87204c" @@ -249,7 +250,7 @@ "4eab8e0abc69668c287310931baa049b8546d3f3e265a106f1b945b948c7649e" ), "pension": ( - "d6a26939ced0a960805edd2c4b3dad02edfde311f6ce8120d53173034661adf9" + "1801b1414503e441de65196a834dae4286a6fecc958e7f5e4a3ad2f8c5489a8b" ), "roles": ( "df2b7afe76cda4f92380f88b478d9e6866e0f532b0d85f081d8226ee2e33dc87" @@ -1570,8 +1571,9 @@ def dc_route(wave: int, gate: SourceGate) -> DcRoute: Registry gate: built from the pension registry's route entries (current job; previous combined; previous DC-only; the checkpoint's formula/unknown route; IRA rollovers; duplicate and off-route rule). - Any route still TO VERIFY or blocked refuses -- today 2017-2023's - inherited-route amendment -- and so do + Any route still TO VERIFY or blocked refuses (since the d637 + registry commits for 5b and 5a, none in the committed registries), + and so do disposition codes on which the IRA-rollover, previous-combined and previous-DC-only entries disagree. Invented declared gate: :func:`declared_dc_route` (current account or combined plan; "both" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 49183db3c..c1d48b0d5 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,362 | -| `artifact` | 4,109 | +| `unit` | 7,367 | +| `artifact` | 4,114 | | `integration_psid` | 1,386 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,685** | +| **Total** | **13,695** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index d30395e86..3b5b7a31f 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,8 +1,8 @@ { "schema_version": 1, "counts": { - "unit": 7362, - "artifact": 4109, + "unit": 7367, + "artifact": 4114, "integration_psid": 1386, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 37be9159b..a189bf7b6 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -72,11 +72,12 @@ def quoted_spans(markdown): } SSI_AND_CENSUS_RESOLVED = 26 #: TO VERIFY records left: the adjudication's 24 A items less those the -#: d637 commits resolved (amendment 2: 10; amendment 3: 6; 5b: 1). +#: d637 commits resolved (amendment 2: 10; amendment 3: 6; 5b: 1; 5a's +#: four entries were D items, RESOLVED already). OPEN_ITEMS = 7 #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. -D637_APPLIED = {"2": "A", "3": "A", "4": "D", "5b": "A"} +D637_APPLIED = {"2": "A", "3": "A", "4": "D", "5b": "A", "5a": "D"} D_ITEMS = { "wealth": { @@ -447,8 +448,14 @@ def test_documentary_resolutions_carry_evidence(documents): assert entry["income_role"] == "ofum" assert not entry["spouse_slot"] if name == "pension": - assert entry["action"] == "refuse_pending_amendment_5_ruling" - assert "amendment 5" in entry["refusal"] + # Applied by Max's d637 ruling under 5a (section 16b). + assert "action" not in entry and "refusal" not in entry + assert "amendment 5" in entry["superseded_refusal"] + assert entry["ruling"]["amendment"] == "5a" + assert entry["ruling_effect"].startswith( + "Max's ruling d637 (2026-09-30; specification section " + "16b) ratified u2-draft-4 amendment 5 as written." + ) def test_open_items_name_their_amendment(documents, monkeypatch): @@ -1098,11 +1105,10 @@ def test_section_16a_recommendations_and_options(): "far resolved amendment 2's ten later spouse age and sex records as " "slot metadata and amendment 3's six spouse-retirement crosswalks " "as non-executable metadata, recorded amendment 4's ruling on the " - "nine corrected wealth accuracy flags, and resolved the 2015 " - "P62ACKPT checkpoint under 5b, releasing its dependents. Until the " - "rest of them (and, for code 88, d745) land, the 7 TO VERIFY " - "records stay open and the 2017\N{EN DASH}2023 P64/P65 records " - "stay blocked.", + "nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT " + "checkpoint under 5b and released the 2017\N{EN DASH}2023 P64/P65 " + "records under 5a. Until the rest of them (and, for code 88, d745) " + "land, the 7 TO VERIFY records stay open.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " @@ -1444,19 +1450,21 @@ def test_released_dependencies(documents): for wave in registry.U2_SOURCE_WAVES: entry = pension[f"{wave}.route.respondent_slots"] assert "blocking_dependencies" not in entry, wave - # d637's 5b commit released every dependent of the 2015 checkpoint. - checkpoint = "pension:2015.route.formula_unknown_checkpoint" + # d637's 5b and 5a commits released every dependent of the 2015 + # checkpoint and of the 2017-2023 inherited-route amendments. + routes = {"pension:2015.route.formula_unknown_checkpoint"} | { + f"pension:{wave}.route.inherited_route_amendment" + for wave in (2017, 2019, 2021, 2023) + } for document in documents.values(): for entry in document["entries"]: - assert checkpoint not in entry.get("blocking_dependencies", []) - for wave in (2017, 2019, 2021, 2023): + assert not routes & set(entry.get("blocking_dependencies", [])) + for wave in registry.U2_SOURCE_WAVES: for person in ("head", "wife"): for plan in (1, 2): - entry = pension[f"{wave}.{person}_prev{plan}_dc_amount"] - assert ( - f"pension:{wave}.route.inherited_route_amendment" - in entry["blocking_dependencies"] - ) + for item in ("dc_disposition", "dc_amount"): + entry = pension[f"{wave}.{person}_prev{plan}_{item}"] + assert "blocking_dependencies" not in entry def test_master_record_matches_registries(documents): diff --git a/tests/track_u2/test_pension_registry.py b/tests/track_u2/test_pension_registry.py index 531a7b218..7fdc9cd2f 100644 --- a/tests/track_u2/test_pension_registry.py +++ b/tests/track_u2/test_pension_registry.py @@ -133,18 +133,19 @@ def test_later_checkpoint_does_not_inherit_formula_unknown_route( assert documented["accepted_plan_types"] == [5] assert set(documented["rejected_plan_types"]) == {1, 7, 8, 9} amendment = by_id[f"{wave}.route.inherited_route_amendment"] - # Independent adjudication D: the documents resolve the DC-only route, - # but application waits for Max's ruling on u2-draft-4 amendment 5. + # Independent adjudication D resolved the DC-only route from the + # documents; Max's d637 ruling ratified amendment 5 (section 16b), so + # under 5a the route applies and its P64/P65 entries are released. assert amendment["status"] == "RESOLVED" assert amendment["adjudication"]["disposition"] == "D" - assert amendment["action"] == "refuse_pending_amendment_5_ruling" - with pytest.raises(registry.SourceAdjudicationError, match="amendment_5"): - registry.require_resolved("pension", amendment["id"]) + assert "action" not in amendment + assert amendment["superseded_refusal"].startswith( + "The documentary route is resolved" + ) + assert amendment["ruling"]["amendment"] == "5a" + assert registry.require_resolved("pension", amendment["id"]) for person in ("head", "wife"): for plan in (1, 2): entry = by_id[f"{wave}.{person}_prev{plan}_dc_amount"] - assert ( - f"pension:{amendment['id']}" in entry["blocking_dependencies"] - ) - with pytest.raises(registry.SourceAdjudicationError): - registry.require_resolved("pension", entry["id"]) + assert "blocking_dependencies" not in entry + assert registry.require_resolved("pension", entry["id"]) diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index b979e7964..b3962a2c3 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -765,6 +765,38 @@ def _copy_ruling(document, source, target): "ruling" ].update(replaces_draft_3_lines=[1175]), ), + "a5a_refusal_restored": ( + "pension", + lambda d: _entry(d, "2019.route.inherited_route_amendment").update( + action="refuse_pending_amendment_5_ruling" + ), + ), + "a5a_on_a_dependent_route": ( + "pension", + lambda d: _copy_ruling( + d, + "2017.route.inherited_route_amendment", + "2017.route.previous_dc_only", + ), + ), + "a5a_ruling_dropped": ( + "pension", + lambda d: _entry(d, "2023.route.inherited_route_amendment").pop( + "ruling" + ), + ), + "a5a_as_5b": ( + "pension", + lambda d: _entry(d, "2021.route.inherited_route_amendment")[ + "ruling" + ].update(amendment="5b"), + ), + "a5a_blocked_again": ( + "pension", + lambda d: _entry(d, "2021.route.inherited_route_amendment").update( + blocking_dependencies=["weights:2017.cross_section_weight"] + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_employer_dc.py b/tests/track_u2/test_u2_employer_dc.py index 57bd103eb..e6f97cc30 100644 --- a/tests/track_u2/test_u2_employer_dc.py +++ b/tests/track_u2/test_u2_employer_dc.py @@ -242,21 +242,30 @@ def test_2013_registry_route_equals_the_declared_route_and_u1_rule( #: The waves whose employer-DC route the registry gate applies: 2013 -#: (U1's route) and, since the d637 5b commit, 2015. -REGISTRY_DC_WAVES = (2013, 2015) +#: (U1's route) and, since the d637 5b and 5a commits, every later wave. +REGISTRY_DC_WAVES = sources.SUPPORT_WAVES -@pytest.mark.parametrize( - "wave", [w for w in sources.SUPPORT_WAVES if w not in REGISTRY_DC_WAVES] -) -def test_later_wave_routes_refuse_under_the_registry_gate( - wave, committed_registries +@pytest.mark.parametrize("wave", [2015, 2017, 2019, 2021, 2023]) +def test_a_blocked_later_route_refuses_under_the_registry_gate( + wave, committed_registries, monkeypatch ): + """The registry gate still refuses a route entry that is blocked, as + each later wave's was before the d637 5b and 5a commits; the entry is + re-blocked in memory only.""" + original = sources.RegistrySet.entry + key = f"{wave}.route.previous_dc_only" + + def entry(self, name, entry_id): + found = original(self, name, entry_id) + if (name, entry_id) == ("pension", key): + return {**found, "blocking_dependencies": ["pension:example"]} + return found + + monkeypatch.setattr(sources.RegistrySet, "entry", entry) gate = sources.SourceGate(sources.REGISTRY, committed_registries) - with pytest.raises(sources.U2SourceRefusal): + with pytest.raises(sources.U2SourceRefusal, match="blocked by"): sources.dc_route(wave, gate) - with pytest.raises(sources.U2SourceRefusal): - sources.pension_field_specs(wave, gate) @pytest.mark.parametrize("wave", REGISTRY_DC_WAVES) @@ -276,45 +285,36 @@ def test_registry_route_equals_the_declared_route(wave, committed_registries): def test_registry_documents_dc_only_checkpoints_from_2017( committed_registries, ): - # The documentary finding the pending amendment 5 would adopt: the - # 2017-2023 checkpoint routes DC-only plans (P46 = 5). The - # adjudication resolved it (disposition D) but its application waits - # on Max's ruling, so the loader refuses the amendment entry and every - # P64/P65 record that depends on it. U2 records the finding and - # refuses; it does not silently narrow or widen the route. + # The documentary finding amendment 5 adopts: the 2017-2023 checkpoint + # routes DC-only plans (P46 = 5). The adjudication resolved it + # (disposition D); Max's d637 ruling ratified amendment 5 as written + # (section 16b), so under 5a the amendment entry and every P64/P65 + # record that depended on it apply, and the registry gate's route is + # the DC-only route, neither narrowed nor widened. from populace_dynamics.data import u2_source_registry as loader_api + registry_gate = sources.SourceGate(sources.REGISTRY, committed_registries) for wave in (2017, 2019, 2021, 2023): entry = committed_registries.entry( "pension", f"{wave}.route.formula_unknown_checkpoint" ) assert list(entry["accepted_plan_types"]) == [5] - amendment = committed_registries.entry( + amendment = loader_api.require_resolved( "pension", f"{wave}.route.inherited_route_amendment" ) - assert amendment["status"] == "RESOLVED" - assert amendment["action"] == "refuse_pending_amendment_5_ruling" - with pytest.raises( - loader_api.SourceAdjudicationError, - match="refuse_pending_amendment_5_ruling", - ): - loader_api.require_resolved( - "pension", f"{wave}.route.inherited_route_amendment" - ) - with pytest.raises( - loader_api.SourceAdjudicationError, match="blocked" - ): - loader_api.require_resolved( - "pension", f"{wave}.route.previous_dc_only" - ) + assert amendment["ruling"]["amendment"] == "5a" + assert "action" not in amendment + loader_api.require_resolved( + "pension", f"{wave}.route.previous_dc_only" + ) + route = sources.dc_route(wave, registry_gate) + assert route.previous_account_items == (5,) declared = sources.SourceGate( sources.INVENTED_DECLARED, committed_registries ) route = sources.dc_route(2019, declared) assert route.source == "section_15_declared_route_invented_only" - assert "pension:2019.route.inherited_route_amendment" in ( - declared.would_refuse - ) + assert declared.would_refuse == {} def test_invented_dc_frames_exercise_every_path(u2_inputs): diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index a602fb2a1..28f89742e 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -312,14 +312,27 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "refusal", "superseded_refusal", }, + # The 2017-2023 D items keep their documentary resolution; the + # refusal pending the ruling moves out. + "5a": { + "action", + "refusal", + "ruling_effect", + "ruling", + "superseded_refusal", + }, } #: The dependencies each amendment released. D637_RELEASED: dict[str, frozenset[str]] = { "5b": frozenset({"pension:2015.route.formula_unknown_checkpoint"}), + "5a": frozenset( + f"pension:{wave}.route.inherited_route_amendment" + for wave in (2017, 2019, 2021, 2023) + ), } #: How many entries, by registry, lost a released dependency (each then #: has none left). -D637_RELEASED_COUNTS = {"pension": 34} +D637_RELEASED_COUNTS = {"pension": 34 + 40} #: The top-level fields each registry's d637 commits changed. D637_TOP = { "income": {"rulings_applied"}, @@ -330,7 +343,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): D637_RECORDED = { "income": ["2", "3"], "wealth": ["4"], - "pension": ["5b"], + "pension": ["5b", "5a"], } @@ -624,31 +637,22 @@ def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): ) -#: The first registry blocker each later wave's family-record specs meet -#: (income concepts are read first, in :data:`sources.INCOME_CONCEPTS` -#: order, then wealth, then the employer-DC items): the first P64 -#: disposition, which waits on 2017-2023's inherited-route amendment -#: (5a). Until the d1090 registry commit (section 16c), 2015's income -#: entries depended on the refused 2015 code-20 route and 2019-2023's on -#: the refused code-90/92 routes; until the d637 amendment-2 commit -#: (section 16b), every later wave first met its spouse age slot -#: metadata, TO VERIFY; until the 5b commit, 2015's P64 items waited on -#: its P62ACKPT checkpoint. No income or wealth entry blocks a later wave -#: now, and 2015 meets no blocker. -OPEN_WAVES_RESOLVED = (2013, 2015) -FIRST_BLOCKER = { - wave: ( - f"pension:{wave}.head_prev1_dc_disposition: blocked by " - f"pension:{wave}.route.inherited_route_amendment" - ) - for wave in (2017, 2019, 2021, 2023) -} +#: The waves whose family-record specs (income, wealth and employer-DC +#: items) the registry gate applies: every support wave. Until the d1090 +#: registry commit (section 16c), 2015's income entries depended on the +#: refused 2015 code-20 route and 2019-2023's on the refused code-90/92 +#: routes; until the d637 amendment-2 commit (section 16b), every later +#: wave first met its spouse age slot metadata, TO VERIFY; until the 5b +#: and 5a commits, the P64/P65 items waited on 2015's P62ACKPT checkpoint +#: and on 2017-2023's inherited-route amendments. +OPEN_WAVES_RESOLVED = sources.SUPPORT_WAVES +FIRST_BLOCKER: dict[int, str] = {} def test_registry_gate_refuses_every_open_wave(registry_gate): # 2013: every mapping U2 reads is resolved (the 2013 meanings are - # U1's); 2015 since the d637 commits for amendments 2 and 5b; each - # other wave meets a blocked entry. + # U1's); 2015-2023 since the d637 commits for amendments 2, 5b and + # 5a. A wave still open would meet its first blocked entry. for wave in OPEN_WAVES_RESOLVED: specs = loader.family_record_specs(wave, registry_gate) concepts = {spec.concept for spec in specs} @@ -704,23 +708,29 @@ def test_declared_gate_records_what_the_registry_gate_refuses( loader.family_record_specs(2019, gate) audit = gate.audit() assert audit["gate"] == sources.INVENTED_DECLARED - refused = audit["would_refuse_under_registry_gate"] - # Since the d637 amendment-2 commit (section 16b) no income or wealth - # entry is refused: the spouse age and sex are resolved slot metadata. - assert refused - assert not any(key.startswith(("income:", "wealth:")) for key in refused) - # Since d1090 no reason is a ruled role entry (section 16c). - assert not any( - dependency in reason - for reasons in refused.values() - for reason in reasons - for dependency in D1090_DEPENDENCIES - ) - assert any( - "pension:2019.route.inherited_route_amendment" in reason - for reasons in refused.values() - for reason in reasons + # Since the d637 commits for amendments 2 and 5a (section 16b) the + # registry gate refuses nothing in 2019's family records. + assert audit["would_refuse_under_registry_gate"] == {} + assert audit["n_entries_applied"] > 0 + # The record is kept when an entry is blocked: the same specs on an + # invented copy of the registries with one P64 item re-blocked, as + # before 5a. + documents = json.loads(json.dumps(dict(committed_registries.documents))) + blocked = "pension:2019.route.inherited_route_amendment" + for entry in documents["pension"]["entries"]: + if entry["id"] == "2019.head_prev1_dc_amount": + entry["blocking_dependencies"] = [blocked] + copied = sources.RegistrySet( + documents, dict(committed_registries.sha256), "invented" ) + gate = sources.SourceGate(sources.INVENTED_DECLARED, copied) + loader.family_record_specs(2019, gate) + refused = gate.audit()["would_refuse_under_registry_gate"] + assert refused == { + "pension:2019.head_prev1_dc_amount": [ + f"pension:2019.head_prev1_dc_amount: blocked by {blocked}" + ] + } def test_source_preflight_refuses_before_opening_any_psid_file( diff --git a/tests/track_u2/test_u2_scripts.py b/tests/track_u2/test_u2_scripts.py index 2019a5404..1c9d99d53 100644 --- a/tests/track_u2/test_u2_scripts.py +++ b/tests/track_u2/test_u2_scripts.py @@ -98,10 +98,10 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): assert entry["income_equal_to_invented_frame"], wave assert entry["dc_equal_to_invented_frame"], wave assert entry["wealth1_identity"]["n_exact"] == entry["n_records"] - # 2015 resolved with the d637 commits for amendments 2 and 5b. - resolved = wave in ("2013", "2015") - assert entry["registry_gate"]["refused"] == (not resolved) - assert entry["registry_gate_dc_route"]["refused"] == (not resolved) + # 2015-2023 resolved with the d637 commits for amendments 2, 5b + # and 5a: the registry gate applies every wave's family records. + assert not entry["registry_gate"]["refused"], wave + assert not entry["registry_gate_dc_route"]["refused"], wave assert checks["mapping"]["negative_where_undocumented_refused"]["refused"] assert checks["mapping"]["crosswalk_as_input_refused"]["refused"] from populace_dynamics.uniform_cut_track_u2 import sources From 3d156aee81728b17a3f3c8b6f84732242f2cea85 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:44:44 -0400 Subject: [PATCH 07/21] Apply d637 amendment 6 to the U2 source registries Max's ruling d637 (2026-09-30; specification section 16b) took amendment 6's option 1 for blocker B3, ratified as an explicit amendment to draft-3 line 134. This is its registry commit (section 16a, "Registry effect of the rulings": "Resolve the 2017 weight record under the chosen option"). - weights.json: 2017.cross_section_weight (ER34651) is RESOLVED: used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its registration_disclosure lists what registration states, as option 1 specifies: what PSID has documented about the revised weight (its identity, the ACS one-year PUMS control and its two named exclusions, the calibration-variable selection approach, some calibration controls, the 2017 Immigrant sample's base weights as formed for the 2019 weights), what it has not published (that sample's starting weights and joint-family integration in the revised 2017 weight, the complete 2017 raking specification), and the coverage bound the control's exclusions set. It keeps its adjudication record (A) and its question as superseded_question, and gains a resolution and a ruling (d637, amendment 6, option 1, amending draft-3 line 134). - individual.json: 2017.weight no longer depends on it. - u2_source_registry: amendment 6's scope; sources: the weights and individual byte pins. - The loader's source preflight now refuses on 12 blockers (14 before), all code 88's (amendment 1c, pending d745). - Master record: 1786 RESOLVED, 0 REFUSED, 6 TO VERIFY; the weight blocker row, the d637 section and two rows. Specification section 17 item 2 and section 19 record it; the spec is re-pinned. Tests: a new test checks the option-1 record against draft-3 line 134 read from Git and the disclosure's documented and unpublished points; five scope mutations each fail; the projection-sensitivity test reads the weights registry at the d1090 commit, which still held the record TO VERIFY. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/individual.json | 5 +- data/external/track_u2/u1_identity.json | 2 +- data/external/track_u2/weights.json | 45 +++++++++++-- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 13 ++-- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 12 ++++ .../uniform_cut_track_u2/loader.py | 7 +- .../uniform_cut_track_u2/sources.py | 8 ++- tests/README-tiers.md | 6 +- tests/tier_counts.json | 4 +- tests/track_u2/test_adjudication_applied.py | 65 ++++++++++++++++--- tests/track_u2/test_source_registries.py | 30 +++++++++ tests/track_u2/test_u2_mappings.py | 19 ++++-- 14 files changed, 180 insertions(+), 40 deletions(-) diff --git a/data/external/track_u2/individual.json b/data/external/track_u2/individual.json index 35d579cf7..33af1aabd 100644 --- a/data/external/track_u2/individual.json +++ b/data/external/track_u2/individual.json @@ -1681,10 +1681,7 @@ "page": 1224 } ], - "selection": "positive recorded cross-sectional weight; do not infer a domain from empirical ranges printed in codebook", - "blocking_dependencies": [ - "weights:2017.cross_section_weight" - ] + "selection": "positive recorded cross-sectional weight; do not infer a domain from empirical ranges printed in codebook" }, { "id": "2019.interview", diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 411986f78..d85c338d9 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "6c2192aceb35aec3f6502944cd96dea5e1a22233b153b519c5267df93b14f607", + "sha256": "3491907c4e328c110175a98ee244ac431fa739504a3ad20074a70756b9693ed9", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/data/external/track_u2/weights.json b/data/external/track_u2/weights.json index 08b901b0a..babc7ca96 100644 --- a/data/external/track_u2/weights.json +++ b/data/external/track_u2/weights.json @@ -153,7 +153,6 @@ "wave": 2017, "concept": "weight", "variable": "ER34651", - "status": "TO VERIFY", "label": "CORE/IMM INDIVIDUAL CROSS-SECTION WT 17", "layout": { "start": 5245, @@ -336,14 +335,41 @@ "selection": "positive recorded cross-sectional weight; do not infer a domain from empirical ranges printed in codebook", "purpose": "Use reporting-wave individual cross-sectional weight times documentary plan multiplier. No cohort reweighting or immigrant exclusion.", "construction": "UserGuide2017 p. 62 says ER34651 and ER71571 were updated to include the NIS-2017 sample 'with Release 2 of PSID-2017 data'; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2). The May 2019 notes for the cross-wave individual file's Release 2 say the 2017 Immigrant individuals' weights will be updated in Release 3, and no individual-file document dates ER34651's update to a Release 2. The difference concerns timing only. The staged February 2019 construction report excludes post-1997 immigrants from its ACS calibration population; the currently served official report is byte-identical. The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, donut-hole families and group-quarters post-1997 immigrants excluded from the 2017 ACS estimate) and a calibration-variable selection approach used since 2017. Of the two, only the April 2021 report documents gender, race, region and age as calibration controls in comparisons that include 2017; both say cross-sectional weights for every wave since 1997 are built with a version of the fair-shares method. The 2019 longitudinal-weights report (April 2021) and the 2021 longitudinal-weights report (September 2023) document how the 2017 Immigrant sample's base weights were formed and integrated with the core sample, for the 2019 weights. PSID's later reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' (Chang, Nishimura, Heeringa, Johnson and Sastry) for content the served February 2019 report lacks. Which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for that sample, whose 2017 longitudinal family weight is zero, and the complete 2017 raking specification remain unresolved.", - "question": "Which PSID document describes the construction of the revised 2017 individual cross-sectional weight ER34651, which now includes the 2017 Immigrant (NIS-2017) sample? In particular, did it start that sample, whose 2017 longitudinal family weight is zero, from the nonresponse-adjusted base weights described in the 2019 longitudinal-weights report, and integrate joint families with the core sample in the same way? And which calibration (raking) variables, interactions, collapsed categories and trimming settings were used for 2017? Your April 2021 reports cite a 2019 version of \"PSID Cross-Sectional Individual Weights, 1997-2017\" (Chang, Nishimura, Heeringa, Johnson and Sastry); is that version available, and does it document the revised ER34651? The April 2021 and June 2026 cross-sectional reports document the 2017 population control and the calibration-variable selection approach, the April 2021 report also some calibration controls, and the 2019 longitudinal-weights report the sample's base weights as formed for the 2019 weights; none documents these points for the revised 2017 weight. The February 2019 construction report predates the revision. Do not remove immigrants to match the older report. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 6, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 129).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 129, "disposition": "A", "amendment": 6 }, - "part_b_finding": "PARTIAL (u2-m1b Part B, revised 2026-09-29 after the independent review; docs/design/u2_m1b_psid_research.md section 3). The June 2026 report on the 2023 cross-sectional weights (tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf) names ER34651 as the 2017 individual weight (Table A3, PDF p. 14); prints the 2017 sum of the individual weights beside the ACS one-year PUMS population total, with the CPS column 'Not Used' (Table A2, PDF p. 13); says that donut-hole families and post-1997 immigrants living in group quarters 'were excluded from the ACS estimate in 2017' (Table A2 note); and says PSID 'started to use a different approach to select the calibration variables since 2017' (PDF p. 6). The April 2021 report on the 2019 cross-sectional weights (tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf) already documented the same 2017 control (Table A2a, PDF p. 17; Table A3, PDF p. 18; calibration approach 'since 2017', PDF p. 7). The May 2019 release notes say the weights for the 2017 Immigrant individuals 'remain zero and will be updated in Release 3', while UserGuide2017 PDF p. 62 dates the update to Release 2; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2), and no individual-file document dates ER34651's update to a Release 2. Of the two cross-sectional reports, only the April 2021 report names gender, race, region and age as calibration controls in comparisons that include 2017 (PDF pp. 12-13). Both say cross-sectional weights for every wave since 1997 were built 'Using a version of' the fair-shares methodology (PDF p. 6 of each). The September 2023 report on the 2021 cross-sectional weights says the post-1997 immigrant families 'did not have a longitudinal weight in 2017 due to the anticipation of adding more post-1997 immigrant families in 2019' (PDF p. 2). The 2019 longitudinal-weights report (tests/data/track_u2/psid_docs/long_weight_19.pdf) documents how the 2017 Immigrant sample's base weights were formed, 'the product of the base probability of selection weight and the final combined nonresponse adjustment factor' (PDF p. 13), and integrated with the core sample (PDF pp. 13-15), for the 2019 weights (PDF p. 8); the 2021 longitudinal-weights report summarizes the same steps (PSID/documentation/capture1/long_weight_21.pdf, PDF p. 4). PSID's April 2021 and September 2023 reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' by Chang, Nishimura, Heeringa, Johnson and Sastry (cross_sec_weights_19 PDF pp. 9, 10 and 14; long_weight_19 PDF pp. 10 and 23; cross_sec_weights_21 PDF p. 11); the served February 2019 report of that title lists McGonagle, not Sastry, among its authors (cross_sec_weights_17 PDF p. 1) and lacks the cited content. Not documented online, as far as the staged and archived documents show: which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for the 2017 Immigrant sample, and the complete 2017 raking specification." + "part_b_finding": "PARTIAL (u2-m1b Part B, revised 2026-09-29 after the independent review; docs/design/u2_m1b_psid_research.md section 3). The June 2026 report on the 2023 cross-sectional weights (tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf) names ER34651 as the 2017 individual weight (Table A3, PDF p. 14); prints the 2017 sum of the individual weights beside the ACS one-year PUMS population total, with the CPS column 'Not Used' (Table A2, PDF p. 13); says that donut-hole families and post-1997 immigrants living in group quarters 'were excluded from the ACS estimate in 2017' (Table A2 note); and says PSID 'started to use a different approach to select the calibration variables since 2017' (PDF p. 6). The April 2021 report on the 2019 cross-sectional weights (tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf) already documented the same 2017 control (Table A2a, PDF p. 17; Table A3, PDF p. 18; calibration approach 'since 2017', PDF p. 7). The May 2019 release notes say the weights for the 2017 Immigrant individuals 'remain zero and will be updated in Release 3', while UserGuide2017 PDF p. 62 dates the update to Release 2; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2), and no individual-file document dates ER34651's update to a Release 2. Of the two cross-sectional reports, only the April 2021 report names gender, race, region and age as calibration controls in comparisons that include 2017 (PDF pp. 12-13). Both say cross-sectional weights for every wave since 1997 were built 'Using a version of' the fair-shares methodology (PDF p. 6 of each). The September 2023 report on the 2021 cross-sectional weights says the post-1997 immigrant families 'did not have a longitudinal weight in 2017 due to the anticipation of adding more post-1997 immigrant families in 2019' (PDF p. 2). The 2019 longitudinal-weights report (tests/data/track_u2/psid_docs/long_weight_19.pdf) documents how the 2017 Immigrant sample's base weights were formed, 'the product of the base probability of selection weight and the final combined nonresponse adjustment factor' (PDF p. 13), and integrated with the core sample (PDF pp. 13-15), for the 2019 weights (PDF p. 8); the 2021 longitudinal-weights report summarizes the same steps (PSID/documentation/capture1/long_weight_21.pdf, PDF p. 4). PSID's April 2021 and September 2023 reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' by Chang, Nishimura, Heeringa, Johnson and Sastry (cross_sec_weights_19 PDF pp. 9, 10 and 14; long_weight_19 PDF pp. 10 and 23; cross_sec_weights_21 PDF p. 11); the served February 2019 report of that title lists McGonagle, not Sastry, among its authors (cross_sec_weights_17 PDF p. 1) and lacks the cited content. Not documented online, as far as the staged and archived documents show: which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for the 2017 Immigrant sample, and the complete 2017 raking specification.", + "status": "RESOLVED", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) took amendment 6's option 1 for blocker B3, ratified as an explicit amendment to draft-3 line 134: ER34651 is used as released in the 1968-2023 individual file, selected by positive weight, with no immigrant exclusion, no reconstructed weight and no recalibration. Line 134's requirement, 'The weight construction and immigrant-refreshment coverage must be documented before registration.', is read as met for 2017 by the documented coverage and population control plus the disclosure in registration_disclosure, which registration states. PSID's 2026-10-07 answer (docs/design/u2_m1b_psid_research.md section 3) pointed to the weight documentation on its website and added nothing on the undocumented points, so the disclosure stands as option 1 specifies. The adjudication's question is kept as superseded_question.", + "registration_disclosure": { + "documented": [ + "the weight's identity: ER34651, CORE/IMM INDIVIDUAL CROSS-SECTION WT 17, the 2017 individual weight in Table A3 of PSID's April 2021 and June 2026 cross-sectional weight reports", + "the 2017 population control: the ACS one-year PUMS population total, with the CPS not used, and its two named exclusions, individuals in donut-hole families and post-1997 immigrants living in group quarters", + "the calibration-variable selection approach PSID has used since 2017", + "some calibration controls: gender, race, region and age in 10-year groups, in comparisons that include 2017 (April 2021 report)", + "how the 2017 Immigrant sample's base weights were formed and integrated with the core sample, for the 2019 weights (the 2019 longitudinal-weights report)" + ], + "not_published": [ + "which starting weights the revised 2017 weight used for the 2017 Immigrant sample, whose 2017 longitudinal family weight is zero", + "how that sample's joint families were integrated with the core sample in the revised 2017 weight", + "the complete 2017 raking specification: every calibration variable and interaction, the collapsed categories and the trimming" + ], + "coverage_bound": "The control's documented exclusions bound coverage: the donut-hole group includes post-1997 immigrants who co-reside with individuals born in 1960-1971, with no limit on their own birth year, and post-1997 immigrants in group quarters are excluded too (June 2026 report, Table A2 note)." + }, + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "6", + "option": "1", + "amends_draft_3_lines": [ + 134 + ] + }, + "superseded_question": "Which PSID document describes the construction of the revised 2017 individual cross-sectional weight ER34651, which now includes the 2017 Immigrant (NIS-2017) sample? In particular, did it start that sample, whose 2017 longitudinal family weight is zero, from the nonresponse-adjusted base weights described in the 2019 longitudinal-weights report, and integrate joint families with the core sample in the same way? And which calibration (raking) variables, interactions, collapsed categories and trimming settings were used for 2017? Your April 2021 reports cite a 2019 version of \"PSID Cross-Sectional Individual Weights, 1997-2017\" (Chang, Nishimura, Heeringa, Johnson and Sastry); is that version available, and does it document the revised ER34651? The April 2021 and June 2026 cross-sectional reports document the 2017 population control and the calibration-variable selection approach, the April 2021 report also some calibration controls, and the 2019 longitudinal-weights report the sample's base weights as formed for the 2019 weights; none documents these points for the revised 2017 weight. The February 2019 construction report predates the revision. Do not remove immigrants to match the older report. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 6, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 129)." }, { "id": "2019.cross_section_weight", @@ -528,5 +554,16 @@ "bytes": 26564, "applied_by": "u2-m1b", "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." - } + }, + "rulings_applied": [ + { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendments": [ + "6" + ], + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1, an explicit amendment to draft-3 line 134. Applied here, each in its own reviewed commit: amendment 6 resolves the 2017 cross-sectional weight ER34651 under option 1, an explicit amendment to draft-3 line 134, and releases the 2017 individual weight. Each ruled entry keeps its adjudication record and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." + } + ] } diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 04fe89956..ba5b9cd12 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b and released the 2017–2023 P64/P65 records under 5a. Until the rest of them (and, for code 88, d745) land, the 7 TO VERIFY records stay open. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1. The 6 TO VERIFY records left are code 88's, open until Max confirms the 1a–1c reading (d745). 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1846,6 +1846,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 5, before its registry commits: the declared employer-DC route, which only invented data use, follows the ratified replacement fragment printed in §16a. A previous plan's account items count for an account plan (P46 = 5) in every wave and, in 2013 only, for a formula or DK plan too; otherwise they are off-route. §15's own `previous_routes` keeps draft 3's text, because §§3–15 keep it until §20 step 5 materializes the final parameter block (§§16b and 16c; §17 item 9), and where a ratified amendment and §§3–15 differ the amendment governs. A test holds the declared route equal to the §16a fragment and §15's block to draft 3's generic route, so the materialization must change both together. The invented population's 2015–2023 formula and DK previous-plan items become off-route. - Amendment 5b: the 2015 P62ACKPT checkpoint (`pension:2015.route.formula_unknown_checkpoint`) is RESOLVED as the documentary intersection. P46 = 5 reaches P64/P65 and P46 = 7 reaches P48/P49; the disputed branch, 'P46=1 AND P52=1 AND P62AMT=DK/RF' as the §16a fragment's `refused_branches` names it, is refused, so an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. The 34 entries that waited on the checkpoint are released. The registry gate now applies 2015's employer-DC route, and a differential test holds it equal to the declared route. The loader's source preflight now refuses on 58 blockers. The master record counts 1785 RESOLVED, 0 REFUSED and 7 TO VERIFY records. - Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65, formula, combined, DK, refused and NA plans are off-route under `off_route`, and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). +- Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index 4768a4e20..8c93d308f 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,7 +2,7 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2026-09-28) to apply the independent adjudication `EV/phase2-20260927/out/u2-adjudicate.md` (SHA-256 `a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662`, verdict ERRORS FOUND). This record does not ratify, register or execute U2. No PSID records were read and no estimator was run. Milestone 1b writes the proposed revision `u2-draft-4` into the specification's new §16a; every line the registries cite keeps its draft-3 text and line number. -**1785 RESOLVED; 0 REFUSED; 7 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 7 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). +**1786 RESOLVED; 0 REFUSED; 6 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 6 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). | Registry | RESOLVED | REFUSED | TO VERIFY | |---|---:|---:|---:| @@ -12,7 +12,7 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2 | pension | 648 | 0 | 0 | | roles | 30 | 0 | 6 | | support | 16 | 0 | 0 | -| weights | 6 | 0 | 1 | +| weights | 7 | 0 | 0 | | design | 2 | 0 | 0 | | SSI and Census | 26 | 0 | 0 | @@ -33,7 +33,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D); amendment 4 ratified by Max's ruling d637 | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics, and the corrections never change an amount or a sample-inclusion decision. | -| Revised 2017 individual-weight construction | TO VERIFY (adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Options in amendment 6. | +| Revised 2017 individual-weight construction | RESOLVED by Max's ruling d637 (amendment 6, option 1; adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Max took amendment 6's option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 is used as released, and registration discloses what PSID has and has not documented (`registration_disclosure`). | | 2023 weight documentation | RESOLVED | Original June 2026 report captured and SHA-256 pinned in `tests/data/track_u2/psid_sources/weights23_manifest.json`. | | SSI capture and notices | RESOLVED | Eleven annual notices, 88 historical CFR snapshots, seven revision-pinned YAML snapshots, constant checks and exact 2012 equality. `docs/design/u2_m1_ssi_adjudication.md` supplies all source hashes. The adjudication reconfirmed all 106 source hashes. | | Census reuse | RESOLVED | Track M capture hash and full 2003–2022 coverage, including full 2012 equality and 2022 precision, verified without recapture. | @@ -79,8 +79,9 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 4: wealth accuracy-flag erratum | 9 | The nine corrected flags (`wealth:wealth.{2013,2015,2017}.{wealth2_acc,checking_saving_acc}` and `wealth:wealth.{2019,2021,2023}.wealth2_acc`) already held their documentary resolution (adjudication D); each now records the ruling, and its `use_restriction` (imputation diagnostics only; the correction never changes amounts or sample inclusion) names the ratification in place of "pending Max's ruling". No U2 path reads these nine flags, and no row or loader blocker moves | | 5b: the 2015 P62ACKPT checkpoint | 1 + 34 released | `pension:2015.route.formula_unknown_checkpoint` is RESOLVED: accepted plan type 5 (DC only), rejected 1, 7, 8 and 9, the refused branch `P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF` (amendment 5's section 15 fragment, `refused_branches`) and the named disposition `off_route`; its question and refusal are kept as `superseded_question` and `superseded_refusal`. The 34 entries that depended on it (for each person's two previous plans, the six P52 status mentions and the P64 and P65 items, and the `previous_dc_only` and `duplicate_and_off_route` routes) are released, none with a dependency left. The registry gate now applies 2015's employer-DC route, equal to the declared route | | 5a: the 2017–2023 previous-employer route | 4 + 40 released | `pension:{2017,2019,2021,2023}.route.inherited_route_amendment` keep their documentary resolution (adjudication D) and now apply: each drops its `refuse_pending_amendment_5_ruling` action, keeps its refusal as `superseded_refusal` and gains a `ruling_effect`. The 40 entries that depended on them (each wave's P64 and P65 items of both persons' two previous plans, `previous_dc_only` and `duplicate_and_off_route`) are released, none with a dependency left. The registry gate now applies every wave's employer-DC route, each equal to the declared route | +| 6: the 2017 cross-sectional weight | 1 + 1 released | `weights:2017.cross_section_weight` is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration, and a `registration_disclosure` listing what PSID has documented (its identity, the ACS one-year PUMS control and its two named exclusions, the calibration-variable selection approach, some calibration controls, the 2017 Immigrant sample's base weights as formed for the 2019 weights) and has not published (that sample's starting weights and joint-family integration in the revised 2017 weight, and the complete 2017 raking specification), with the coverage bound the control's exclusions set. `individual:2017.weight` is released | -The loader's source preflight now refuses on 14 blockers: code 88 (12, amendment 1c, pending d745) and the 2017 weight with the individual weight that depends on it (2, amendment 6). +The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745): each wave's code-88 role entry is TO VERIFY and refuses its substantive routing. ## One row per source route or field @@ -1101,7 +1102,7 @@ The loader's source preflight now refuses on 14 blockers: code 88 (12, amendment | `individual:2017.relationship` | RESOLVED | ER34503: RELATION TO REFERENCE PERSON 17. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:721`; `PSID/ind2023er/IND2023ER.sps:3090`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `PSID/ind2023er/IND2023ER_formats.sps:24449` | | `individual:2017.age` | RESOLVED | ER34504: AGE OF INDIVIDUAL 17. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:721`; `PSID/ind2023er/IND2023ER.sps:3091`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097 | | `individual:2017.reported_birth_year` | RESOLVED | ER34506: YEAR INDIVIDUAL BORN 17. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:722`; `PSID/ind2023er/IND2023ER.sps:3093`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1098 | -| `individual:2017.weight` | RESOLVED | ER34651: CORE/IMM INDIVIDUAL CROSS-SECTION WT 17. Exact layout, codebook domain and universe are retained in the JSON registry. Application also refuses pending weights:2017.cross_section_weight. | `PSID/ind2023er/IND2023ER.sps:770`; `PSID/ind2023er/IND2023ER.sps:3238`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1224 | +| `individual:2017.weight` | RESOLVED | ER34651: CORE/IMM INDIVIDUAL CROSS-SECTION WT 17. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:770`; `PSID/ind2023er/IND2023ER.sps:3238`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1224 | | `individual:2019.interview` | RESOLVED | ER34701: 2019 INTERVIEW NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:771`; `PSID/ind2023er/IND2023ER.sps:3239`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225 | | `individual:2019.sequence` | RESOLVED | ER34702: SEQUENCE NUMBER 19. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:771`; `PSID/ind2023er/IND2023ER.sps:3240`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_formats.sps:25923` | | `individual:2019.relationship` | RESOLVED | ER34703: RELATION TO REFERENCE PERSON 19. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/ind2023er/IND2023ER.sps:771`; `PSID/ind2023er/IND2023ER.sps:3241`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `PSID/ind2023er/IND2023ER_formats.sps:25976` | @@ -1845,7 +1846,7 @@ The loader's source preflight now refuses on 14 blockers: code 88 (12, amendment | `support:common.birth_derivation` | RESOLVED | Reuse existing history precedence; common support changes seeds only. No cohort implementation in milestone 1. | `src/populace_dynamics/estimates/career.py:652`; `docs/design/boomers2004_1946_55_comparison.md:108` | | `weights:2013.cross_section_weight` | RESOLVED | Fair shares base weights; trimming/cell adjustment then CPS post-stratification. Post-1997 immigrants incompletely covered. | `PSID/ind2023er/IND2023ER.sps:672`; `PSID/ind2023er/IND2023ER.sps:2943`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 976; `PSID/documentation/capture1/cross_sec_weights_13.pdf` p. 4; `PSID/documentation/capture1/cross_sec_weights_13.pdf` p. 5; `PSID/documentation/capture1/cross_sec_weights_13.pdf` p. 6; `PSID/documentation/capture1/cross_sec_weights_13.pdf` p. 8 | | `weights:2015.cross_section_weight` | RESOLVED | Fair shares base weights; trimming/cell adjustment then ACS 2015 post-stratification. Post-1997 immigrants incompletely covered. | `PSID/ind2023er/IND2023ER.sps:720`; `PSID/ind2023er/IND2023ER.sps:3087`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1094; `PSID/documentation/capture1/cross_sec_weights_15.pdf` p. 4; `PSID/documentation/capture1/cross_sec_weights_15.pdf` p. 5; `PSID/documentation/capture1/cross_sec_weights_15.pdf` p. 6; `PSID/documentation/capture1/cross_sec_weights_15.pdf` p. 8 | -| `weights:2017.cross_section_weight` | TO VERIFY | Which PSID document describes the construction of the revised 2017 individual cross-sectional weight ER34651, which now includes the 2017 Immigrant (NIS-2017) sample? In particular, did it start that sample, whose 2017 longitudinal family weight is zero, from the nonresponse-adjusted base weights described in the 2019 longitudinal-weights report, and integrate joint families with the core sample in the same way? And which calibration (raking) variables, interactions, collapsed categories and trimming settings were used for 2017? Your April 2021 reports cite a 2019 version of "PSID Cross-Sectional Individual Weights, 1997-2017" (Chang, Nishimura, Heeringa, Johnson and Sastry); is that version available, and does it document the revised ER34651? The April 2021 and June 2026 cross-sectional reports document the 2017 population control and the calibration-variable selection approach, the April 2021 report also some calibration controls, and the 2019 longitudinal-weights report the sample's base weights as formed for the 2019 weights; none documents these points for the revised 2017 weight. The February 2019 construction report predates the revision. Do not remove immigrants to match the older report. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 6, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 129). Part B: PARTIAL (u2-m1b Part B, revised 2026-09-29 after the independent review; docs/design/u2_m1b_psid_research.md section 3). The June 2026 report on the 2023 cross-sectional weights (tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf) names ER34651 as the 2017 individual weight (Table A3, PDF p. 14); prints the 2017 sum of the individual weights beside the ACS one-year PUMS population total, with the CPS column 'Not Used' (Table A2, PDF p. 13); says that donut-hole families and post-1997 immigrants living in group quarters 'were excluded from the ACS estimate in 2017' (Table A2 note); and says PSID 'started to use a different approach to select the calibration variables since 2017' (PDF p. 6). The April 2021 report on the 2019 cross-sectional weights (tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf) already documented the same 2017 control (Table A2a, PDF p. 17; Table A3, PDF p. 18; calibration approach 'since 2017', PDF p. 7). The May 2019 release notes say the weights for the 2017 Immigrant individuals 'remain zero and will be updated in Release 3', while UserGuide2017 PDF p. 62 dates the update to Release 2; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2), and no individual-file document dates ER34651's update to a Release 2. Of the two cross-sectional reports, only the April 2021 report names gender, race, region and age as calibration controls in comparisons that include 2017 (PDF pp. 12-13). Both say cross-sectional weights for every wave since 1997 were built 'Using a version of' the fair-shares methodology (PDF p. 6 of each). The September 2023 report on the 2021 cross-sectional weights says the post-1997 immigrant families 'did not have a longitudinal weight in 2017 due to the anticipation of adding more post-1997 immigrant families in 2019' (PDF p. 2). The 2019 longitudinal-weights report (tests/data/track_u2/psid_docs/long_weight_19.pdf) documents how the 2017 Immigrant sample's base weights were formed, 'the product of the base probability of selection weight and the final combined nonresponse adjustment factor' (PDF p. 13), and integrated with the core sample (PDF pp. 13-15), for the 2019 weights (PDF p. 8); the 2021 longitudinal-weights report summarizes the same steps (PSID/documentation/capture1/long_weight_21.pdf, PDF p. 4). PSID's April 2021 and September 2023 reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' by Chang, Nishimura, Heeringa, Johnson and Sastry (cross_sec_weights_19 PDF pp. 9, 10 and 14; long_weight_19 PDF pp. 10 and 23; cross_sec_weights_21 PDF p. 11); the served February 2019 report of that title lists McGonagle, not Sastry, among its authors (cross_sec_weights_17 PDF p. 1) and lacks the cited content. Not documented online, as far as the staged and archived documents show: which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for the 2017 Immigrant sample, and the complete 2017 raking specification. | `PSID/ind2023er/IND2023ER.sps:770`; `PSID/ind2023er/IND2023ER.sps:3238`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1224; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 5; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 6; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 8; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 9; `PSID/documentation/capture1/UserGuide2017.pdf` p. 62; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:129`; `tests/data/track_u2/psid_docs/DataRelease-May2019.pdf` p. 1; `tests/data/track_u2/psid_docs/UserGuide2017.pdf` p. 62; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 2; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 3; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 9; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 2; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 3; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 6; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 13; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 14; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 4; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 7; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 17; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 18; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 6; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 12; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 13; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 2; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 8; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 13; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 14; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 15; `PSID/documentation/capture1/long_weight_21.pdf` p. 4; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 9; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 10; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 14; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 10; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 23; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 11; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 1; `PSID/documentation/capture1/long_weight_17.pdf` p. 9; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2 | +| `weights:2017.cross_section_weight` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) took amendment 6's option 1 for blocker B3, ratified as an explicit amendment to draft-3 line 134: ER34651 is used as released in the 1968-2023 individual file, selected by positive weight, with no immigrant exclusion, no reconstructed weight and no recalibration. Line 134's requirement, 'The weight construction and immigrant-refreshment coverage must be documented before registration.', is read as met for 2017 by the documented coverage and population control plus the disclosure in registration_disclosure, which registration states. PSID's 2026-10-07 answer (docs/design/u2_m1b_psid_research.md section 3) pointed to the weight documentation on its website and added nothing on the undocumented points, so the disclosure stands as option 1 specifies. The adjudication's question is kept as superseded_question. Part B: PARTIAL (u2-m1b Part B, revised 2026-09-29 after the independent review; docs/design/u2_m1b_psid_research.md section 3). The June 2026 report on the 2023 cross-sectional weights (tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf) names ER34651 as the 2017 individual weight (Table A3, PDF p. 14); prints the 2017 sum of the individual weights beside the ACS one-year PUMS population total, with the CPS column 'Not Used' (Table A2, PDF p. 13); says that donut-hole families and post-1997 immigrants living in group quarters 'were excluded from the ACS estimate in 2017' (Table A2 note); and says PSID 'started to use a different approach to select the calibration variables since 2017' (PDF p. 6). The April 2021 report on the 2019 cross-sectional weights (tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf) already documented the same 2017 control (Table A2a, PDF p. 17; Table A3, PDF p. 18; calibration approach 'since 2017', PDF p. 7). The May 2019 release notes say the weights for the 2017 Immigrant individuals 'remain zero and will be updated in Release 3', while UserGuide2017 PDF p. 62 dates the update to Release 2; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2), and no individual-file document dates ER34651's update to a Release 2. Of the two cross-sectional reports, only the April 2021 report names gender, race, region and age as calibration controls in comparisons that include 2017 (PDF pp. 12-13). Both say cross-sectional weights for every wave since 1997 were built 'Using a version of' the fair-shares methodology (PDF p. 6 of each). The September 2023 report on the 2021 cross-sectional weights says the post-1997 immigrant families 'did not have a longitudinal weight in 2017 due to the anticipation of adding more post-1997 immigrant families in 2019' (PDF p. 2). The 2019 longitudinal-weights report (tests/data/track_u2/psid_docs/long_weight_19.pdf) documents how the 2017 Immigrant sample's base weights were formed, 'the product of the base probability of selection weight and the final combined nonresponse adjustment factor' (PDF p. 13), and integrated with the core sample (PDF pp. 13-15), for the 2019 weights (PDF p. 8); the 2021 longitudinal-weights report summarizes the same steps (PSID/documentation/capture1/long_weight_21.pdf, PDF p. 4). PSID's April 2021 and September 2023 reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' by Chang, Nishimura, Heeringa, Johnson and Sastry (cross_sec_weights_19 PDF pp. 9, 10 and 14; long_weight_19 PDF pp. 10 and 23; cross_sec_weights_21 PDF p. 11); the served February 2019 report of that title lists McGonagle, not Sastry, among its authors (cross_sec_weights_17 PDF p. 1) and lacks the cited content. Not documented online, as far as the staged and archived documents show: which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for the 2017 Immigrant sample, and the complete 2017 raking specification. | `PSID/ind2023er/IND2023ER.sps:770`; `PSID/ind2023er/IND2023ER.sps:3238`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1224; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 5; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 6; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 8; `PSID/documentation/capture1/cross_sec_weights_17.pdf` p. 9; `PSID/documentation/capture1/UserGuide2017.pdf` p. 62; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:129`; `tests/data/track_u2/psid_docs/DataRelease-May2019.pdf` p. 1; `tests/data/track_u2/psid_docs/UserGuide2017.pdf` p. 62; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 2; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 3; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 9; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 2; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 3; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 6; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 13; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 14; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 4; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 7; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 17; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 18; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 6; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 12; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 13; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 2; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 8; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 13; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 14; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 15; `PSID/documentation/capture1/long_weight_21.pdf` p. 4; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 9; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 10; `tests/data/track_u2/psid_docs/cross_sec_weights_19.pdf` p. 14; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 10; `tests/data/track_u2/psid_docs/long_weight_19.pdf` p. 23; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 11; `tests/data/track_u2/psid_sources/cross_sec_weights_17.pdf` p. 1; `PSID/documentation/capture1/long_weight_17.pdf` p. 9; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2 | | `weights:2019.cross_section_weight` | RESOLVED | Initial longitudinal family weights allocated by fair shares, calibrated to ACS 2019 PUMS with raking and trimming; includes 2017/2019 refresher. | `PSID/ind2023er/IND2023ER.sps:825`; `PSID/ind2023er/IND2023ER.sps:3402`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1345; `PSID/documentation/capture1/cross_sec_weights_19.pdf` p. 4; `PSID/documentation/capture1/cross_sec_weights_19.pdf` p. 7; `PSID/documentation/capture1/cross_sec_weights_19.pdf` p. 8; `PSID/documentation/capture1/cross_sec_weights_19.pdf` p. 9; `PSID/documentation/capture1/cross_sec_weights_19.pdf` p. 10 | | `weights:2021.cross_section_weight` | RESOLVED | Initial longitudinal family weights allocated by fair shares, calibrated to ACS 2021 PUMS with raking and trimming; includes 2017/2019 refresher. | `PSID/ind2023er/IND2023ER.sps:880`; `PSID/ind2023er/IND2023ER.sps:3567`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1466; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 4; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 6; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 7; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 8; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 9; `PSID/documentation/capture1/cross_sec_weights_21.pdf` p. 10 | | `weights:2023.cross_section_weight` | RESOLVED | Fair shares initial weights calibrated to ACS 2023 one-year PUMS. June 2026 report documents 2017/2019 refresher coverage. | `PSID/ind2023er/IND2023ER.sps:935`; `PSID/ind2023er/IND2023ER.sps:3732`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1587; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 2; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 3; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 5; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 6; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 7; `tests/data/track_u2/psid_sources/cross_sec_weights_23.pdf` p. 8 | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 7511488bd..fa8619080 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -400,6 +400,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 5, builder: `src/populace_dynamics/uniform_cut_track_u2/sources.py` (the declared employer-DC route by wave and `declared_dc_route`), `invented.py` (the invented DC frames use it), `__init__.py` (docstring); the specification's §19 and `u1_identity.json`; `tests/track_u2/test_u2_employer_dc.py`; and this section. - Amendment 5b: `pension.json` (the 2015 checkpoint, its 34 dependents and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the P62A blocker row, the d637 section and 35 rows); `u2_source_registry.py` (5b's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. - Amendment 5a: `pension.json` (the four inherited-route amendments, their 40 dependents and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row, the d637 section and 44 rows); `u2_source_registry.py` (5a's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. +- Amendment 6: `weights.json` (the 2017 weight and a d637 record), `individual.json` (the released 2017 weight) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the weight blocker row, the d637 section and two rows); `u2_source_registry.py` (amendment 6's scope), `sources.py` (two byte pins) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 97c2eb7f4..e1ae04512 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -175,6 +175,18 @@ "fields": {"status": "RESOLVED"}, "absent": ("action", "refusal", "question", "blocking_dependencies"), }, + "6": { + "registry": "weights", + "entries": ("2017.cross_section_weight",), + "ruling": { + **_D637, + "amendment": "6", + "option": "1", + "amends_draft_3_lines": [134], + }, + "fields": {"status": "RESOLVED", "variable": "ER34651"}, + "absent": ("action", "refusal", "question", "blocking_dependencies"), + }, } #: The d637 amendment each ruled entry belongs to, by (registry, id). _AMENDMENT_OF_ENTRY = { diff --git a/src/populace_dynamics/uniform_cut_track_u2/loader.py b/src/populace_dynamics/uniform_cut_track_u2/loader.py index 496448d7b..029c04560 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/loader.py +++ b/src/populace_dynamics/uniform_cut_track_u2/loader.py @@ -12,15 +12,16 @@ the support plan and the role of every amended relationship code -- must be RESOLVED with no blocking dependency, under the committed, pinned registries. It refuses at the committed registries: code 88 - (every wave) and the revised 2017 cross-section weight are TO - VERIFY. The d1090 + is TO VERIFY in every wave (amendment 1c, pending Max's confirmation, + d745). The d1090 registry commit (section 16c) replaced the refused 2015 code-20 and 2019-2023 code-90/92 routes with the ratified family-unit exclusions, which released the income and pension entries that depended on them; the d637 registry commits (section 16b) resolve the ratified amendments one by one: amendment 2's spouse age/sex slot metadata, amendment 3's crosswalks, amendment 4's wealth flags, 5b's 2015 P62A - checkpoint and 5a's 2017-2023 P64/P65 route so far. + checkpoint, 5a's 2017-2023 P64/P65 route and amendment 6's 2017 + weight. 2. **Source identity**: each staged setup file and codebook the registries cite is rehashed against the registry's recorded SHA-256, and every registry layout -- the family files' and the individual diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index c965881d5..c20794083 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -238,7 +238,9 @@ #: non-executable metadata), amendment 4 the wealth bytes (the nine #: corrected accuracy flags record the ruling) and amendments 5b and 5a #: the pension bytes (the 2015 P62ACKPT checkpoint, the 2017-2023 -#: inherited-route amendments and the P64/P65 entries they released). +#: inherited-route amendments and the P64/P65 entries they released), and +#: amendment 6 the weights and individual bytes (the 2017 weight ER34651 +#: under option 1, and the individual weight it released). REGISTRY_SHA256: dict[str, str] = { "income": ( "ac8310a166a66fa71a39d19cb79dec0dda5c41ff5a7033e535d70ebffe87204c" @@ -247,7 +249,7 @@ "e2d24c2665db23c24f9313dd44876c35a22b7e67b4d0acfad1c65b48c64e6602" ), "individual": ( - "4eab8e0abc69668c287310931baa049b8546d3f3e265a106f1b945b948c7649e" + "1cb368486d689e20e116b775ee2198504b0492fb3c7efa4d6fe7e985a2b1307c" ), "pension": ( "1801b1414503e441de65196a834dae4286a6fecc958e7f5e4a3ad2f8c5489a8b" @@ -259,7 +261,7 @@ "5102825d8555e1e8648dfafbd4e6c72eef78d09e453a6d25279ec3b9298d5109" ), "weights": ( - "356fcbd8dd19f4baf3e34406d75b4a6ae608dc25330d975ecdb9c2d9bb5cf84f" + "519901c7647a0e9300ed9e3d4e9f83d47a3224e63888e1919960ca56ff3f239e" ), "design": ( "56181372d412811af6d06f5c0d8d1e071049f7fb73c34bd394aaff8f5e0c2fd6" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index c1d48b0d5..9d87dcc16 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -40,8 +40,8 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| | `unit` | 7,367 | -| `artifact` | 4,114 | -| `integration_psid` | 1,386 | +| `artifact` | 4,119 | +| `integration_psid` | 1,387 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,695** | +| **Total** | **13,701** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 3b5b7a31f..4bbc869fb 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -2,8 +2,8 @@ "schema_version": 1, "counts": { "unit": 7367, - "artifact": 4114, - "integration_psid": 1386, + "artifact": 4119, + "integration_psid": 1387, "reproduction_legacy": 520, "oracle_policyengine": 308 } diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index a189bf7b6..a35d15787 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -67,17 +67,25 @@ def quoted_spans(markdown): "pension": (648, 0, 0), "roles": (30, 0, 6), "support": (16, 0, 0), - "weights": (6, 0, 1), + "weights": (7, 0, 0), "design": (2, 0, 0), } SSI_AND_CENSUS_RESOLVED = 26 #: TO VERIFY records left: the adjudication's 24 A items less those the #: d637 commits resolved (amendment 2: 10; amendment 3: 6; 5b: 1; 5a's -#: four entries were D items, RESOLVED already). -OPEN_ITEMS = 7 +#: four entries were D items, RESOLVED already; amendment 6: 1). The six +#: left are code 88's (amendment 1c), pending Max's confirmation (d745). +OPEN_ITEMS = 6 #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. -D637_APPLIED = {"2": "A", "3": "A", "4": "D", "5b": "A", "5a": "D"} +D637_APPLIED = { + "2": "A", + "3": "A", + "4": "D", + "5b": "A", + "5a": "D", + "6": "A", +} D_ITEMS = { "wealth": { @@ -565,6 +573,43 @@ def test_d637_amendment_2_spouse_metadata_never_decides_the_slot(documents): assert entry is None or "usage" not in entry +def test_d637_amendment_6_uses_er34651_as_released(documents): + """Amendment 6, option 1 (ratified by d637 as an explicit amendment to + draft-3 line 134): ER34651 as released, selected by positive weight, + with the disclosure option 1 specifies; the 2017 individual weight is + released with it.""" + record = json.loads( + (registry.REGISTRY_DIRECTORY / "u1_identity.json").read_text() + )["u2_specification"] + line_134 = draft_3_bytes(record).decode("utf-8").split("\n")[133] + entry = next( + e + for e in documents["weights"]["entries"] + if e["id"] == "2017.cross_section_weight" + ) + assert entry["ruling"]["amends_draft_3_lines"] == [134] + assert entry["ruling"]["option"] == "1" + assert f"'{line_134}'" in entry["resolution"] + assert "no immigrant exclusion" in entry["resolution"] + assert entry["selection"].startswith("positive recorded cross-sectional") + assert "No cohort reweighting or immigrant exclusion" in entry["purpose"] + disclosure = entry["registration_disclosure"] + assert set(disclosure) == {"documented", "not_published", "coverage_bound"} + assert len(disclosure["documented"]) == 5 + assert len(disclosure["not_published"]) == 3 + for phrase in ("starting weights", "joint families", "raking"): + assert any(phrase in item for item in disclosure["not_published"]) + for phrase in ("ER34651", "ACS one-year PUMS", "donut-hole"): + assert any(phrase in item for item in disclosure["documented"]) + weight = next( + e + for e in documents["individual"]["entries"] + if e["id"] == "2017.weight" + ) + assert weight["variable"] == "ER34651" + assert "blocking_dependencies" not in weight + + def test_corrected_specification_citations(documents): spec = (ROOT / SPEC).read_text(encoding="utf-8").split("\n") corrected = 0 @@ -1106,9 +1151,10 @@ def test_section_16a_recommendations_and_options(): "slot metadata and amendment 3's six spouse-retirement crosswalks " "as non-executable metadata, recorded amendment 4's ruling on the " "nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT " - "checkpoint under 5b and released the 2017\N{EN DASH}2023 P64/P65 " - "records under 5a. Until the rest of them (and, for code 88, d745) " - "land, the 7 TO VERIFY records stay open.", + "checkpoint under 5b, released the 2017\N{EN DASH}2023 P64/P65 " + "records under 5a and resolved the 2017 weight ER34651 under " + "amendment 6's option 1. The 6 TO VERIFY records left are code " + "88's, open until Max confirms the 1a\N{EN DASH}1c reading (d745).", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " @@ -1393,8 +1439,9 @@ def test_part_b_verdicts_agree(): assert finding.startswith("PARTIAL") master = MASTER.read_text(encoding="utf-8") assert ( - "| Revised 2017 individual-weight construction | TO VERIFY " - "(adjudication A); Part B PARTIAL |" in master + "| Revised 2017 individual-weight construction | RESOLVED by Max's " + "ruling d637 (amendment 6, option 1; adjudication A); Part B " + "PARTIAL |" in master ) assert "NOT DOCUMENTED ONLINE" not in master for blocker in ( diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index b3962a2c3..eea9110a3 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -797,6 +797,36 @@ def _copy_ruling(document, source, target): blocking_dependencies=["weights:2017.cross_section_weight"] ), ), + "a6_option_changed": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight")["ruling"].update( + option="3" + ), + ), + "a6_line_dropped": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight")["ruling"].update( + amends_draft_3_lines=[] + ), + ), + "a6_longitudinal_weight": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight").update( + variable="ER34650" + ), + ), + "a6_on_2019_weight": ( + "weights", + lambda d: _copy_ruling( + d, "2017.cross_section_weight", "2019.cross_section_weight" + ), + ), + "a6_reopened": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight").update( + status="TO VERIFY", question="reopened" + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 28f89742e..bc292ee7a 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -321,6 +321,13 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "ruling", "superseded_refusal", }, + "6": { + "status", + "resolution", + "registration_disclosure", + "ruling", + "superseded_question", + }, } #: The dependencies each amendment released. D637_RELEASED: dict[str, frozenset[str]] = { @@ -329,21 +336,24 @@ def test_d1090_moved_only_the_ruled_gate_fields(): f"pension:{wave}.route.inherited_route_amendment" for wave in (2017, 2019, 2021, 2023) ), + "6": frozenset({"weights:2017.cross_section_weight"}), } #: How many entries, by registry, lost a released dependency (each then #: has none left). -D637_RELEASED_COUNTS = {"pension": 34 + 40} +D637_RELEASED_COUNTS = {"pension": 34 + 40, "individual": 1} #: The top-level fields each registry's d637 commits changed. D637_TOP = { "income": {"rulings_applied"}, "wealth": {"rulings_applied"}, "pension": {"rulings_applied"}, + "weights": {"rulings_applied"}, } #: The amendments each registry's ``rulings_applied`` d637 record lists. D637_RECORDED = { "income": ["2", "3"], "wealth": ["4"], "pension": ["5b", "5a"], + "weights": ["6"], } @@ -414,9 +424,10 @@ def test_d637_moved_only_the_ruled_gate_fields(): def test_the_gate_projection_sees_a_moved_gate_field(): - """A changed status is not prose: the projection hash moves.""" - raw = (registry.REGISTRY_DIRECTORY / "weights.json").read_bytes() - document = json.loads(raw) + """A changed status is not prose: the projection hash moves. Read at + the d1090 commit, whose weights registry still held the 2017 weight + TO VERIFY (the d637 amendment-6 commit resolved it).""" + document = json.loads(_at(D1090_COMMIT, "weights")) target = next( entry for entry in document["entries"] From 6bb6606f6b74b2c3f4d0c2d500c05942bdc1e2d1 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:48:11 -0400 Subject: [PATCH 08/21] Record the d637 rulings in the U2 registries' adjudication notes The last wording step of the d637 registry work (the brief's item 6). No gate field, count or loader blocker moves. - income, wealth, pension, roles, support and weights: each independent_adjudication note no longer says the A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; it records the d637 and d1090 rulings, points to rulings_applied, and says the six code-88 records (amendment 1c) wait for d745. - wealth: status_scope no longer says role assignment "is separately refused by the U2 role registry"; d1090 fixed the same sentence in the income registry only. - Master record: the milestone-1b D row now says the 2017-2023 route's application stayed refused until d637 released it under 5a. - sources: six byte pins; the d637 differential allows exactly these top-level moves and checks the notes' new ending and unchanged start. - Specification section 19 records it; the spec is re-pinned. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 2 +- data/external/track_u2/pension.json | 2 +- data/external/track_u2/roles.json | 2 +- data/external/track_u2/support.json | 2 +- data/external/track_u2/u1_identity.json | 2 +- data/external/track_u2/wealth.json | 4 +-- data/external/track_u2/weights.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1_source_adjudication.md | 2 +- docs/design/u2_m1b_exposure.md | 1 + .../uniform_cut_track_u2/sources.py | 18 ++++++---- tests/track_u2/test_u2_mappings.py | 34 ++++++++++++++++--- 12 files changed, 51 insertions(+), 21 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index 24f8e85d5..371a9afe6 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -28015,7 +28015,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/pension.json b/data/external/track_u2/pension.json index 921827585..d0bc46206 100644 --- a/data/external/track_u2/pension.json +++ b/data/external/track_u2/pension.json @@ -26536,7 +26536,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/roles.json b/data/external/track_u2/roles.json index 240dec2e8..55ca89398 100644 --- a/data/external/track_u2/roles.json +++ b/data/external/track_u2/roles.json @@ -1882,7 +1882,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/support.json b/data/external/track_u2/support.json index ed8ad156a..04dd0bedf 100644 --- a/data/external/track_u2/support.json +++ b/data/external/track_u2/support.json @@ -966,6 +966,6 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." } } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index d85c338d9..e41d48e75 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "3491907c4e328c110175a98ee244ac431fa739504a3ad20074a70756b9693ed9", + "sha256": "2fda85fb80b0cb272ea0ebd26fdbd9c30c7c75a0b0b32ac768db2e5eb5a0aebf", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/data/external/track_u2/wealth.json b/data/external/track_u2/wealth.json index 37d33776d..66209a5c0 100644 --- a/data/external/track_u2/wealth.json +++ b/data/external/track_u2/wealth.json @@ -9056,14 +9056,14 @@ ] } }, - "status_scope": "RESOLVED means the family-file documentary variable mapping and its printed domain. Individual relationship-to-income-slot assignment is separately refused by the U2 role registry; this registry does not resolve that assignment.", + "status_scope": "RESOLVED means the family-file documentary variable mapping and its printed domain. Individual relationship-to-income-slot assignment is decided separately by the U2 role registry; this registry does not resolve that assignment.", "raw_data_read": false, "independent_adjudication": { "file": "EV/phase2-20260927/out/u2-adjudicate.md", "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/weights.json b/data/external/track_u2/weights.json index babc7ca96..841fc4035 100644 --- a/data/external/track_u2/weights.json +++ b/data/external/track_u2/weights.json @@ -553,7 +553,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index ba5b9cd12..53a1346bf 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1847,6 +1847,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 5b: the 2015 P62ACKPT checkpoint (`pension:2015.route.formula_unknown_checkpoint`) is RESOLVED as the documentary intersection. P46 = 5 reaches P64/P65 and P46 = 7 reaches P48/P49; the disputed branch, 'P46=1 AND P52=1 AND P62AMT=DK/RF' as the §16a fragment's `refused_branches` names it, is refused, so an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. The 34 entries that waited on the checkpoint are released. The registry gate now applies 2015's employer-DC route, and a differential test holds it equal to the declared route. The loader's source preflight now refuses on 58 blockers. The master record counts 1785 RESOLVED, 0 REFUSED and 7 TO VERIFY records. - Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65, formula, combined, DK, refused and NA plans are off-route under `off_route`, and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). - Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. +- Wording: the six registries that record the independent adjudication no longer say its A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; each note records the rulings and that the six code-88 records wait for d745. The wealth registry's scope no longer says role assignment is "refused" by the role registry, which d1090 left stale in that registry. The master record's account of milestone 1b's D resolutions now says the 2017–2023 route's application was released under 5a. No gate field, count or blocker moves. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index 8c93d308f..8486a00ba 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -48,7 +48,7 @@ The independent adjudication (`EV/phase2-20260927/out/u2-adjudicate.md`) confirm |---|---:|---| | Amendment 7: WRONG citations corrected | 18 | 15 support entries cite their own calendar row (S:74–78, S:88–92) instead of blank S:67; `roles:2013.relationship.90` cites S:147, 150, 156 and 175 instead of S:154; `pension:2021.route.other_current_plan` adds q2021 p. 292 and `pension:2023.route.other_current_plan` adds q2023 p. 290 | | Builder-found citation fix | 6 | The six code-88 role entries cited S:198, a staged-materials table row; they now cite S:190. The adjudication checked only RESOLVED records, so it did not review these | -| D: resolved from documents | 16 | Status RESOLVED with a `resolution` quoting the evidence: 9 wealth accuracy flags (amendment 4), roles 2015.90, 2017.90 and 2017.92 (amendment 1), and the four 2017–2023 pension `inherited_route_amendment` entries (amendment 5), whose application stays refused until Max rules on amendment 5 | +| D: resolved from documents | 16 | Status RESOLVED with a `resolution` quoting the evidence: 9 wealth accuracy flags (amendment 4), roles 2015.90, 2017.90 and 2017.92 (amendment 1), and the four 2017–2023 pension `inherited_route_amendment` entries (amendment 5), whose application stayed refused until Max's ruling d637 (applied under 5a; section below) | | F: explicit refusals | 8 | Status RESOLVED, `action` beginning `refuse_` and ending `per_u2_adjudicate_F`, a `refusal` naming the adjudication line, a `part_b_finding` and an `open_question` for PSID staff: roles 2015.20, 2019.90, 2019.92, 2021.90, 2021.92, 2023.90, 2023.92, and `pension:2015.route.respondent_slots` | | A: open | 24 | Still TO VERIFY; each question now names its disposition, amendment and adjudication line | | Dependencies released | 248 entries | `roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92` no longer block dependents. 2015 slot maps stay blocked by the refused code-20 route and 2019–2023 slot maps by the refused 90/92 routes; the 2017 slot maps no longer carry a role blocker. `pension:2015.route.respondent_slots` now depends on `roles:2015.relationship.20` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index fa8619080..324b2f409 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -401,6 +401,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 5b: `pension.json` (the 2015 checkpoint, its 34 dependents and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the P62A blocker row, the d637 section and 35 rows); `u2_source_registry.py` (5b's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. - Amendment 5a: `pension.json` (the four inherited-route amendments, their 40 dependents and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row, the d637 section and 44 rows); `u2_source_registry.py` (5a's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. - Amendment 6: `weights.json` (the 2017 weight and a d637 record), `individual.json` (the released 2017 weight) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the weight blocker row, the d637 section and two rows); `u2_source_registry.py` (amendment 6's scope), `sources.py` (two byte pins) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. +- Wording: `income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json` and `weights.json` (each `independent_adjudication` note; the wealth `status_scope`) and `u1_identity.json`; the specification's §19; the master record (the milestone-1b D row); `sources.py` (six byte pins); `tests/track_u2/test_u2_mappings.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index c20794083..edbc3383b 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -240,28 +240,32 @@ #: the pension bytes (the 2015 P62ACKPT checkpoint, the 2017-2023 #: inherited-route amendments and the P64/P65 entries they released), and #: amendment 6 the weights and individual bytes (the 2017 weight ER34651 -#: under option 1, and the individual weight it released). +#: under option 1, and the individual weight it released). A last +#: wording commit moved six registries' bytes once more: each +#: ``independent_adjudication`` note now records the rulings, and the +#: wealth registry's ``status_scope`` no longer says role assignment is +#: refused. REGISTRY_SHA256: dict[str, str] = { "income": ( - "ac8310a166a66fa71a39d19cb79dec0dda5c41ff5a7033e535d70ebffe87204c" + "034d3f2e3b1d121e544584c650fc317454112500fd900043dc4e7ec0be611562" ), "wealth": ( - "e2d24c2665db23c24f9313dd44876c35a22b7e67b4d0acfad1c65b48c64e6602" + "cebf9bfad6a07d2a0e7ec8c627f30ed10641512c479bc482b91998b9e21294e8" ), "individual": ( "1cb368486d689e20e116b775ee2198504b0492fb3c7efa4d6fe7e985a2b1307c" ), "pension": ( - "1801b1414503e441de65196a834dae4286a6fecc958e7f5e4a3ad2f8c5489a8b" + "241aeaddb7e8b134ac5807f944053c4be59458d17bcf68c52177da5cab5328a6" ), "roles": ( - "df2b7afe76cda4f92380f88b478d9e6866e0f532b0d85f081d8226ee2e33dc87" + "79feadac7b4847c545e37cee33784873bb0324b9999cfc2693292af6aaacf43d" ), "support": ( - "5102825d8555e1e8648dfafbd4e6c72eef78d09e453a6d25279ec3b9298d5109" + "419341b90e0adae2c8a8ee1c2324d405bbd5e0a49faa54a00c19025fc6fd57c9" ), "weights": ( - "519901c7647a0e9300ed9e3d4e9f83d47a3224e63888e1919960ca56ff3f239e" + "6042524ae68d2163bd5b119ecfc0837e6c9e59713b0d50cffe9fc5b788a20e2c" ), "design": ( "56181372d412811af6d06f5c0d8d1e071049f7fb73c34bd394aaff8f5e0c2fd6" diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index bc292ee7a..b2dfecdc1 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -341,13 +341,25 @@ def test_d1090_moved_only_the_ruled_gate_fields(): #: How many entries, by registry, lost a released dependency (each then #: has none left). D637_RELEASED_COUNTS = {"pension": 34 + 40, "individual": 1} -#: The top-level fields each registry's d637 commits changed. +#: The top-level fields each registry's d637 commits changed: the d637 +#: ``rulings_applied`` record, and in the last (wording) commit every +#: ``independent_adjudication`` note and the wealth ``status_scope``. D637_TOP = { - "income": {"rulings_applied"}, - "wealth": {"rulings_applied"}, - "pension": {"rulings_applied"}, - "weights": {"rulings_applied"}, + "income": {"rulings_applied", "independent_adjudication"}, + "wealth": {"rulings_applied", "independent_adjudication", "status_scope"}, + "pension": {"rulings_applied", "independent_adjudication"}, + "roles": {"independent_adjudication"}, + "support": {"independent_adjudication"}, + "weights": {"rulings_applied", "independent_adjudication"}, } +#: The wording commit's note, after the adjudication's own sentences. +D637_ADJUDICATION_NOTE_END = ( + "A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's " + "ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on " + "2026-10-10 (d1090); rulings_applied records what each ruling changed " + "here. The six code-88 role records (amendment 1c) stay TO VERIFY " + "pending Max's confirmation of the 1a-1c reading (d745)." +) #: The amendments each registry's ``rulings_applied`` d637 record lists. D637_RECORDED = { "income": ["2", "3"], @@ -389,6 +401,18 @@ def test_d637_moved_only_the_ruled_gate_fields(): assert after["rulings_applied"][:-1] == before.get( "rulings_applied", [] ), name + if "independent_adjudication" in top: + old_meta = before["independent_adjudication"] + new_meta = after["independent_adjudication"] + assert _changed_keys(old_meta, new_meta) == {"note"}, name + assert new_meta["note"].endswith(D637_ADJUDICATION_NOTE_END) + assert new_meta["note"].startswith( + old_meta["note"].split("A items stay TO VERIFY")[0] + ), name + if "status_scope" in top: + assert after["status_scope"] == before["status_scope"].replace( + "is separately refused by", "is decided separately by" + ) assert [e["id"] for e in before["entries"]] == [ e["id"] for e in after["entries"] ], name From 595f3357c6b2e58d4a97dc6ad831b1bc826e2b6e Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:55:07 -0400 Subject: [PATCH 09/21] Answer the first-round reviews of the d637 registry commits Reviews of the amendment 2, 3 and 4 commits (GPT-6.1 Sol; Subfleet jobs 20261011-221957-d637-review-a2, REQUEST CHANGES; -222335-d637-review-a3 and -222713-d637-review-a4, APPROVE with findings). - Amendment 2's rule no longer names the slot's occupants ("the in-family code-20 or code-22 person"), a closed list that would have decided code 88's slot status, part of amendment 1c, pending d745. It defers to the roles registry's spouse_slot rule with that caveat, and says code 88's slot status stays TO VERIFY (a2 finding 1). - sources.field_specs refuses a slot-metadata concept other than the spouse age under either gate; a static test keeps wife_age and wife_sex out of every other U2 module and U1's income concept (a2 finding 3). - The validator now holds each applied amendment to its adjudication disposition and amendment number and to nonempty required text fields; holds amendment 4's nine corrected targets; refuses a ruling whose decision is not d637 or d1090, and an amendment's distinctive value (usage, restriction, refused branch, disclosure) outside its scope; forbids a scalar variable on a crosswalk and any dependency on a ruled entry; and pins amendment 3's restriction text (a2 finding 3, a3 findings 1 and 3, a4 findings 2 and 3). Seventeen new scope mutations each fail. - Tests pin each applied amendment's ruled wording by hash and by its operative clauses, with seven text mutations that each fail; the registry differential compares every superseded question and refusal with the d1090 registries' text in full (a2 finding 2, a3 finding 1). - Amendment 3's resolution adds that U2 reads none of the four components as an input of its own (a3 finding 4). - Records: section 19 softens the variant claim (its reference person is male, so it tests the check's direction) and records this round; dates read 2026-10-11, the commits' date (a2 findings 4 and 5). The master record's 16 rows and the spouse age/sex blocker row follow the new text. Not changed: the package docstring (a4 finding 1) was already fixed in 8de64f51; tests/README-tiers.md is the repository's required tier bookkeeping (a2 finding 6); the wealth note's generic clause (a4 finding 5) is harmless; tier counts move with the rebase onto master. The loader's source preflight still refuses on 12 blockers, all code 88's. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 32 +-- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 5 +- docs/design/u2_m1_source_adjudication.md | 38 ++-- docs/design/u2_m1b_exposure.md | 5 +- .../data/u2_source_registry.py | 121 ++++++++++- .../uniform_cut_track_u2/sources.py | 23 +- tests/README-tiers.md | 8 +- tests/tier_counts.json | 6 +- tests/track_u2/test_adjudication_applied.py | 198 +++++++++++++++++- tests/track_u2/test_source_registries.py | 103 +++++++++ tests/track_u2/test_u2_mappings.py | 60 +++++- 12 files changed, 541 insertions(+), 60 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index 371a9afe6..b2bd559b8 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -4426,7 +4426,7 @@ }, "status": "RESOLVED", "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -4615,7 +4615,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -7546,7 +7546,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -9022,7 +9022,7 @@ }, "status": "RESOLVED", "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -9211,7 +9211,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -12158,7 +12158,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -13626,7 +13626,7 @@ }, "status": "RESOLVED", "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -13815,7 +13815,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -16754,7 +16754,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -18222,7 +18222,7 @@ }, "status": "RESOLVED", "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -18421,7 +18421,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -21435,7 +21435,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -23043,7 +23043,7 @@ }, "status": "RESOLVED", "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -23242,7 +23242,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -26256,7 +26256,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -27864,7 +27864,7 @@ }, "status": "RESOLVED", "use_restriction": "Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it.", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question.", "ruling": { "decision": "d637", "date": "2026-09-30", diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index e41d48e75..aee585259 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "2fda85fb80b0cb272ea0ebd26fdbd9c30c7c75a0b0b32ac768db2e5eb5a0aebf", + "sha256": "ea4753b966ebc14e8dc2225e081238c212cf91d10c5e17636c69af6e56647632", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 53a1346bf..2345788dc 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1837,10 +1837,10 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - After the second round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261010-070559-d1090-review-a2` and `20261010-070559-d1090-review-bc`, both REQUEST CHANGES; same folder): option 1e-1 does not specify B2's counting unit, so §16c records the person count only as a proposal pending Max's confirmation (d1242), states that the record ratifies no further counting requirement, and §17 item 8 cites the counts the options specify. The builder computes B1's person count only. The record test now checks the complete operative paragraphs and lines. The registry validator holds each exclusion to its exact ratified scope. New invented variants test that earlier dispositions keep precedence and that a code-88 refusal inside an excluded unit still refuses. The master record's per-route rows now state each entry's remaining dependencies (537 sentences removed, 16 corrected). - After the third-round review (GPT-6.1 Sol, Subfleet job `20261010-075932-d1090-review-r3`, REQUEST CHANGES, two should-fix findings; same folder): the record test pins every line of §16c and §20's status line, so no wording in them can change without the test changing, and 33 mutations each fail it. The registry validator requires every role entry's id to name its own wave and code, and an exclusion to sit on a present entry, so a rule cannot be moved or dropped under an unchanged id. -**`u2-draft-4`, Max's d637 rulings applied to the registries (from 2026-10-12):** +**`u2-draft-4`, Max's d637 rulings applied to the registries (from 2026-10-11):** - Each ratified amendment's registry effect (§16a, "Registry effect of the rulings") lands in its own reviewed commit. `src/populace_dynamics/data/u2_source_registry.py` (`RATIFIED_AMENDMENTS`) pins each applied amendment's entries, ruling and gate fields, and `tests/track_u2/test_u2_mappings.py` checks every gate field these commits move against the registries the d1090 commit left, read from Git. Amendment 1's parts 1a–1c, and with them the six code-88 records, wait for Max's confirmation (d745). No line before §16a and no registry-cited line changes. -- Amendment 2: the ten 2015–2023 family-file spouse age and sex records (`income..wife_age` and `wife_sex`) are RESOLVED as slot metadata only. Under their no-slot-inference rule, the variables and their 'Head is female or single male' or 'Reference Person is female or single male' wording never decide whether a person occupies the spouse slot; the in-family code-20 or code-22 person does, and annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age. The builder reads the spouse age only for the family file's slot flag, which the income rows must find equal to the roster's occupant or they refuse; a new invented variant covers the direction this amendment concerns, a family-file 'no spouse' code beside a roster occupant. The loader's source preflight now refuses on 69 blockers, down from 74. The master record counts 1778 RESOLVED, 0 REFUSED and 14 TO VERIFY records. +- Amendment 2: the ten 2015–2023 family-file spouse age and sex records (`income..wife_age` and `wife_sex`) are RESOLVED as slot metadata only. Under their no-slot-inference rule, the variables and their 'Head is female or single male' or 'Reference Person is female or single male' wording never decide whether a person occupies the spouse slot; the relationship rules in the roles registry do, and code 88's slot status stays TO VERIFY (amendment 1c, pending d745). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age. The builder reads the spouse age only for the family file's slot flag, which the income rows must find equal to the roster's occupant or they refuse. A new invented variant puts a family-file 'no spouse' code beside a roster occupant, and the income rows refuse; its reference person is male, so it tests the check's direction rather than the female-reference-person wording itself, and the check reads no sex. The loader's source preflight now refuses on 69 blockers, down from 74. The master record counts 1778 RESOLVED, 0 REFUSED and 14 TO VERIFY records. - Amendment 3: the six `income..wife_retirement_annuities` crosswalk records (2013–2023) are RESOLVED as non-executable metadata. They keep their crosswalk kind, which the field adapter refuses as an input under either gate; no scalar equivalence is asserted between U1's combined item and the later separately documented spouse pension, annuity, IRA and other-retirement items, which stay inside total family money income under §4 and are never added a second time. No row changes and no loader blocker moves (69). The master record counts 1784 RESOLVED, 0 REFUSED and 8 TO VERIFY records. - Amendment 4: the nine corrected wealth accuracy flags (adjudication D) already held their resolution; each now records the ruling, and its use restriction (imputation diagnostics only; the correction never changes an amount or a sample-inclusion decision) names the ratification in place of "pending Max's ruling", in `data/external/track_u2/wealth.json` and the master record's rows. No U2 path reads these flags, and no count or loader blocker moves. - Amendment 5, before its registry commits: the declared employer-DC route, which only invented data use, follows the ratified replacement fragment printed in §16a. A previous plan's account items count for an account plan (P46 = 5) in every wave and, in 2013 only, for a formula or DK plan too; otherwise they are off-route. §15's own `previous_routes` keeps draft 3's text, because §§3–15 keep it until §20 step 5 materializes the final parameter block (§§16b and 16c; §17 item 9), and where a ratified amendment and §§3–15 differ the amendment governs. A test holds the declared route equal to the §16a fragment and §15's block to draft 3's generic route, so the materialization must change both together. The invented population's 2015–2023 formula and DK previous-plan items become off-route. @@ -1848,6 +1848,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65, formula, combined, DK, refused and NA plans are off-route under `off_route`, and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). - Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. - Wording: the six registries that record the independent adjudication no longer say its A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; each note records the rulings and that the six code-88 records wait for d745. The wealth registry's scope no longer says role assignment is "refused" by the role registry, which d1090 left stale in that registry. The master record's account of milestone 1b's D resolutions now says the 2017–2023 route's application was released under 5a. No gate field, count or blocker moves. +- After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index 8486a00ba..dd20a01d3 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -30,7 +30,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is proposed in amendment 1. | | 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a replaces the §15 contract's generic account/formula/dk previous route with this wave-specific route, so the dependent P64/P65 entries are released. | -| Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot (the in-family code-20 or code-22 person does). | +| Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot: the relationship rules in the roles registry do, and code 88's slot status stays TO VERIFY (amendment 1c, pending d745). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D); amendment 4 ratified by Max's ruling d637 | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics, and the corrections never change an amount or a sample-inclusion decision. | | Revised 2017 individual-weight construction | RESOLVED by Max's ruling d637 (amendment 6, option 1; adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Max took amendment 6's option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 is used as released, and registration discloses what PSID has and has not documented (`registration_disclosure`). | @@ -68,13 +68,13 @@ Max ruled on blockers B1 and B2 on 2026-10-10 (d1090), after PSID answered (`doc The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cut_track_u2/sources.py`, `family_unit_exclusion`; `cohort.py`, `build_u2_cohort`) and counts each excluded observation under its named disposition. No PSID record was read and no count was made. After that commit the loader's source preflight still refused, on 74 blockers: code 88 (amendment 1c, pending d745), the later waves' spouse age/sex metadata (amendment 2), the 2015 P62A checkpoint (amendment 5b) and the 2017 weight (amendment 6) stayed TO VERIFY, and the 2017–2023 P64/P65 records waited on amendment 5's registry commit. -## Max's ruling d637 applied (registry commits from 2026-10-12) +## Max's ruling d637 applied (registry commits from 2026-10-11) Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): amendments 2–5 ratified as written and amendment 6's option 1 ratified as an explicit amendment to draft-3 line 134. Each amendment's registry effect is its own reviewed commit, and `RATIFIED_AMENDMENTS` in `src/populace_dynamics/data/u2_source_registry.py` pins each applied amendment's entries, ruling and gate fields. Each ruled entry keeps its adjudication record and gains a `ruling`; an open question or refusal the ruling answers is kept as `superseded_question` or `superseded_refusal`. Amendment 1's parts 1a–1c, and the six code-88 records with them, wait for Max's confirmation (d745). | Amendment | Records | Registry effect | |---|---:|---| -| 2: annuitant input precedence | 10 | `income:income.{2015,2017,2019,2021,2023}.{wife_age,wife_sex}` are RESOLVED as slot metadata (`usage` `slot_metadata_only`), with a no-slot-inference rule in `use_restriction`: the variables and their inapplicability wording never decide who occupies the spouse slot, and annuity lives use each person's recorded sex and derived income-year age. The builder reads the spouse age only for the family file's slot flag, and the income rows refuse any observation where it disagrees with the roster's code-20 or code-22 occupant | +| 2: annuitant input precedence | 10 | `income:income.{2015,2017,2019,2021,2023}.{wife_age,wife_sex}` are RESOLVED as slot metadata (`usage` `slot_metadata_only`), with a no-slot-inference rule in `use_restriction`: the variables and their inapplicability wording never decide who occupies the spouse slot, and annuity lives use each person's recorded sex and derived income-year age. The builder reads the spouse age only for the family file's slot flag, and the income rows refuse any observation where it disagrees with the roster's spouse-slot occupant | | 3: historical spouse-retirement crosswalk | 6 | `income:income.{2013,2015,2017,2019,2021,2023}.wife_retirement_annuities` are RESOLVED as non-executable metadata: their `kind` stays `historical_route_crosswalk`, which the field adapter refuses as an input under either gate, and a `use_restriction` states that no input or row reads them and no scalar equivalence is inferred. No row changes and no loader blocker moves: no U2 run applies these records | | 4: wealth accuracy-flag erratum | 9 | The nine corrected flags (`wealth:wealth.{2013,2015,2017}.{wealth2_acc,checking_saving_acc}` and `wealth:wealth.{2019,2021,2023}.wealth2_acc`) already held their documentary resolution (adjudication D); each now records the ruling, and its `use_restriction` (imputation diagnostics only; the correction never changes amounts or sample inclusion) names the ratification in place of "pending Max's ruling". No U2 path reads these nine flags, and no row or loader blocker moves | | 5b: the 2015 P62ACKPT checkpoint | 1 + 34 released | `pension:2015.route.formula_unknown_checkpoint` is RESOLVED: accepted plan type 5 (DC only), rejected 1, 7, 8 and 9, the refused branch `P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF` (amendment 5's section 15 fragment, `refused_branches`) and the named disposition `off_route`; its question and refusal are kept as `superseded_question` and `superseded_refusal`. The 34 entries that depended on it (for each person's two previous plans, the six P52 status mentions and the P64 and P65 items, and the `previous_dc_only` and `duplicate_and_off_route` routes) are released, none with a dependency left. The registry gate now applies 2015's employer-DC route, equal to the declared route | @@ -210,12 +210,12 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2013.wife_annuities_acc` | RESOLVED | ER58100: ACCURACY OF WIFE ANNUITIES-2012. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1710`; `PSID/family/2013/FAM2013ER.sps:6923`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826 | | `income:income.2013.wife_iras_acc` | RESOLVED | ER58102: ACCURACY OF WIFE IRAS-2012. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6925`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826 | | `income:income.2013.wife_other_retirement_acc` | RESOLVED | ER58104: ACCURACY OF WIFE OTHER RETIREMENT-2012. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1712`; `PSID/family/2013/FAM2013ER.sps:6927`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1827 | -| `income:income.2013.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2013/FAM2013ER.sps:1709`; `PSID/family/2013/FAM2013ER.sps:6920`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1825; `PSID/family/2013/FAM2013ER.sps:1710`; `PSID/family/2013/FAM2013ER.sps:6922`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6924`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6926`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1827; `EV/phase2-20260927/out/u2-adjudicate.md:67` | +| `income:income.2013.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2013/FAM2013ER.sps:1709`; `PSID/family/2013/FAM2013ER.sps:6920`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1825; `PSID/family/2013/FAM2013ER.sps:1710`; `PSID/family/2013/FAM2013ER.sps:6922`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6924`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1826; `PSID/family/2013/FAM2013ER.sps:1711`; `PSID/family/2013/FAM2013ER.sps:6926`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1827; `EV/phase2-20260927/out/u2-adjudicate.md:67` | | `income:income.2015.interview` | RESOLVED | ER60002: 2015 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:11`; `PSID/family/2015/FAM2015ER.sps:1898`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 2 | | `income:income.2015.fu_size` | RESOLVED | ER60016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1912`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 5 | | `income:income.2015.head_age` | RESOLVED | ER60017: AGE OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1913`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `income:income.2015.head_sex` | RESOLVED | ER60018: SEX OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1914`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | -| `income:income.2015.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1915`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:68` | +| `income:income.2015.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1915`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:68` | | `income:income.2015.n_children` | RESOLVED | ER60021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1917`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `income:income.2015.head_labor` | RESOLVED | ER65216: LABOR INCOME OF HEAD-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1749`; `PSID/family/2015/FAM2015ER.sps:7112`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1943 | | `income:income.2015.head_farm` | RESOLVED | ER65195: FARM INCOME OF HEAD AND SPOUSE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1742`; `PSID/family/2015/FAM2015ER.sps:7091`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1938 | @@ -298,7 +298,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2015.ofum_asset_acc` | RESOLVED | ER65320: ACCURACY OF OTR FU MEMBR ASSET Y-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1784`; `PSID/family/2015/FAM2015ER.sps:7216`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1969 | | `income:income.2015.wife_va_pension` | RESOLVED | ER65290: SPOUSE VA PENSION-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7186`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962 | | `income:income.2015.wife_alimony` | RESOLVED | ER65306: SPOUSE ALIMONY-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1779`; `PSID/family/2015/FAM2015ER.sps:7202`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1966 | -| `income:income.2015.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1916`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:69` | +| `income:income.2015.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1916`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:69` | | `income:income.2015.head_farm_acc` | RESOLVED | ER65196: ACC FARM INCOME OF HEAD AND SPOUSE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1742`; `PSID/family/2015/FAM2015ER.sps:7092`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1938 | | `income:income.2015.head_tanf_acc` | RESOLVED | ER65255: ACCURACY OF HEAD INCOME FROM TANF-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1762`; `PSID/family/2015/FAM2015ER.sps:7151`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1954 | | `income:income.2015.head_other_welfare_acc` | RESOLVED | ER65259: ACCURACY OF HEAD OTHER WELFARE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1763`; `PSID/family/2015/FAM2015ER.sps:7155`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1954; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1955 | @@ -338,12 +338,12 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2015.wife_other_retirement_acc` | RESOLVED | ER65299: ACCURACY OF SPOUSE OTHER RETIREMENT-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1777`; `PSID/family/2015/FAM2015ER.sps:7195`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1964 | | `income:income.2015.wife_va_pension_acc` | RESOLVED | ER65291: ACCURACY OF SPOUSE VA PENSION-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7187`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962 | | `income:income.2015.wife_alimony_acc` | RESOLVED | ER65307: ACCURACY OF SPOUSE ALIMONY-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1779`; `PSID/family/2015/FAM2015ER.sps:7203`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1966 | -| `income:income.2015.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7188`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962; `PSID/family/2015/FAM2015ER.sps:1775`; `PSID/family/2015/FAM2015ER.sps:7190`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7192`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7194`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1964; `EV/phase2-20260927/out/u2-adjudicate.md:70` | +| `income:income.2015.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7188`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962; `PSID/family/2015/FAM2015ER.sps:1775`; `PSID/family/2015/FAM2015ER.sps:7190`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7192`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1963; `PSID/family/2015/FAM2015ER.sps:1776`; `PSID/family/2015/FAM2015ER.sps:7194`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1964; `EV/phase2-20260927/out/u2-adjudicate.md:70` | | `income:income.2017.interview` | RESOLVED | ER66002: 2017 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:11`; `PSID/family/2017/FAM2017ER.sps:1926`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2 | | `income:income.2017.fu_size` | RESOLVED | ER66016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1940`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_age` | RESOLVED | ER66017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1941`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_sex` | RESOLVED | ER66018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1942`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | -| `income:income.2017.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1943`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:71` | +| `income:income.2017.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1943`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:71` | | `income:income.2017.n_children` | RESOLVED | ER66021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1945`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | | `income:income.2017.head_labor` | RESOLVED | ER71293: LABOR INCOME OF REF PERSON-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1775`; `PSID/family/2017/FAM2017ER.sps:7217`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1996 | | `income:income.2017.head_farm` | RESOLVED | ER71272: FARM INCOME OF REF PERSN AND SPOUSE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1768`; `PSID/family/2017/FAM2017ER.sps:7196`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1990 | @@ -426,7 +426,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2017.ofum_asset_acc` | RESOLVED | ER71397: ACCURACY OF OTR FU MEMBR ASSET Y-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1809`; `PSID/family/2017/FAM2017ER.sps:7321`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2022 | | `income:income.2017.wife_va_pension` | RESOLVED | ER71367: SPOUSE VA PENSION-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1799`; `PSID/family/2017/FAM2017ER.sps:7291`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2014 | | `income:income.2017.wife_alimony` | RESOLVED | ER71383: SPOUSE ALIMONY-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1805`; `PSID/family/2017/FAM2017ER.sps:7307`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2018 | -| `income:income.2017.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1944`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:72` | +| `income:income.2017.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1944`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:72` | | `income:income.2017.head_farm_acc` | RESOLVED | ER71273: ACC FARM INCOME OF RP AND SPOUSE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1768`; `PSID/family/2017/FAM2017ER.sps:7197`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1991 | | `income:income.2017.head_tanf_acc` | RESOLVED | ER71332: ACCURACY OF RP INCOME FROM TANF-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1788`; `PSID/family/2017/FAM2017ER.sps:7256`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2006 | | `income:income.2017.head_other_welfare_acc` | RESOLVED | ER71336: ACCURACY OF RP OTHER WELFARE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1789`; `PSID/family/2017/FAM2017ER.sps:7260`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2007 | @@ -466,12 +466,12 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2017.wife_other_retirement_acc` | RESOLVED | ER71376: ACCURACY OF SPOUSE OTHER RETIREMENT-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1802`; `PSID/family/2017/FAM2017ER.sps:7300`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016 | | `income:income.2017.wife_va_pension_acc` | RESOLVED | ER71368: ACCURACY OF SPOUSE VA PENSION-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1800`; `PSID/family/2017/FAM2017ER.sps:7292`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2014 | | `income:income.2017.wife_alimony_acc` | RESOLVED | ER71384: ACCURACY OF SPOUSE ALIMONY-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1805`; `PSID/family/2017/FAM2017ER.sps:7308`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2018 | -| `income:income.2017.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2017/FAM2017ER.sps:1800`; `PSID/family/2017/FAM2017ER.sps:7293`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7295`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7297`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `PSID/family/2017/FAM2017ER.sps:1802`; `PSID/family/2017/FAM2017ER.sps:7299`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `EV/phase2-20260927/out/u2-adjudicate.md:73` | +| `income:income.2017.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2017/FAM2017ER.sps:1800`; `PSID/family/2017/FAM2017ER.sps:7293`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7295`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2015; `PSID/family/2017/FAM2017ER.sps:1801`; `PSID/family/2017/FAM2017ER.sps:7297`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `PSID/family/2017/FAM2017ER.sps:1802`; `PSID/family/2017/FAM2017ER.sps:7299`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2016; `EV/phase2-20260927/out/u2-adjudicate.md:73` | | `income:income.2019.interview` | RESOLVED | ER72002: 2019 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:11`; `PSID/family/2019/FAM2019ER.sps:1930`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2 | | `income:income.2019.fu_size` | RESOLVED | ER72016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1944`; `PSID/family/2019/fam2019er_codebook.pdf` p. 5 | | `income:income.2019.head_age` | RESOLVED | ER72017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1945`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | | `income:income.2019.head_sex` | RESOLVED | ER72018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1946`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | -| `income:income.2019.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1947`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:74` | +| `income:income.2019.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1947`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:74` | | `income:income.2019.n_children` | RESOLVED | ER72021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1949`; `PSID/family/2019/fam2019er_codebook.pdf` p. 7 | | `income:income.2019.head_labor` | RESOLVED | ER77315: LABOR INCOME OF REF PERSON-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1782`; `PSID/family/2019/FAM2019ER.sps:7243`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1980 | | `income:income.2019.head_farm` | RESOLVED | ER77294: FARM INCOME OF REF PERSN AND SPOUSE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1775`; `PSID/family/2019/FAM2019ER.sps:7222`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1975 | @@ -554,7 +554,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2019.ofum_asset_acc` | RESOLVED | ER77419: ACCURACY OF OTR FU MEMBR ASSET Y-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1817`; `PSID/family/2019/FAM2019ER.sps:7347`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2007 | | `income:income.2019.wife_va_pension` | RESOLVED | ER77389: SPOUSE VA PENSION-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7317`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1999 | | `income:income.2019.wife_alimony` | RESOLVED | ER77405: SPOUSE ALIMONY-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1812`; `PSID/family/2019/FAM2019ER.sps:7333`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2003 | -| `income:income.2019.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1948`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:75` | +| `income:income.2019.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1948`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:75` | | `income:income.2019.head_farm_acc` | RESOLVED | ER77295: ACC FARM INCOME OF RP AND SPOUSE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1775`; `PSID/family/2019/FAM2019ER.sps:7223`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1975 | | `income:income.2019.head_tanf_acc` | RESOLVED | ER77354: ACCURACY OF RP INCOME FROM TANF-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1795`; `PSID/family/2019/FAM2019ER.sps:7282`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1991 | | `income:income.2019.head_other_welfare_acc` | RESOLVED | ER77358: ACCURACY OF RP OTHER WELFARE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1796`; `PSID/family/2019/FAM2019ER.sps:7286`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1991; `PSID/family/2019/fam2019er_codebook.pdf` p. 1992 | @@ -594,12 +594,12 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2019.wife_other_retirement_acc` | RESOLVED | ER77398: ACCURACY OF SPOUSE OTHER RETIREMENT-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1810`; `PSID/family/2019/FAM2019ER.sps:7326`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001 | | `income:income.2019.wife_va_pension_acc` | RESOLVED | ER77390: ACCURACY OF SPOUSE VA PENSION-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7318`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1999 | | `income:income.2019.wife_alimony_acc` | RESOLVED | ER77406: ACCURACY OF SPOUSE ALIMONY-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1812`; `PSID/family/2019/FAM2019ER.sps:7334`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2003 | -| `income:income.2019.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7319`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1808`; `PSID/family/2019/FAM2019ER.sps:7321`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7323`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7325`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `EV/phase2-20260927/out/u2-adjudicate.md:76` | +| `income:income.2019.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7319`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1808`; `PSID/family/2019/FAM2019ER.sps:7321`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2000; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7323`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `PSID/family/2019/FAM2019ER.sps:1809`; `PSID/family/2019/FAM2019ER.sps:7325`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2001; `EV/phase2-20260927/out/u2-adjudicate.md:76` | | `income:income.2021.interview` | RESOLVED | ER78002: 2021 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:11`; `PSID/family/2021/FAM2021ER.sps:1382`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 2 | | `income:income.2021.fu_size` | RESOLVED | ER78016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1396`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:224` | | `income:income.2021.head_age` | RESOLVED | ER78017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1397`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6 | | `income:income.2021.head_sex` | RESOLVED | ER78018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1398`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:248` | -| `income:income.2021.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1399`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:77` | +| `income:income.2021.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1399`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:77` | | `income:income.2021.n_children` | RESOLVED | ER78021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1401`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:261` | | `income:income.2021.head_labor` | RESOLVED | ER81642: LABOR INCOME OF REF PERSON-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1224`; `PSID/family/2021/FAM2021ER.sps:5022`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1344 | | `income:income.2021.head_farm` | RESOLVED | ER81621: FARM INCOME OF REF PERSN AND SPOUSE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1217`; `PSID/family/2021/FAM2021ER.sps:5001`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1339 | @@ -682,7 +682,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2021.ofum_asset_acc` | RESOLVED | ER81746: ACCURACY OF OTR FU MEMBR ASSET Y-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1259`; `PSID/family/2021/FAM2021ER.sps:5126`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1371; `PSID/family/2021/FAM2021ER_formats.sps:37106` | | `income:income.2021.wife_va_pension` | RESOLVED | ER81716: SPOUSE VA PENSION-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1249`; `PSID/family/2021/FAM2021ER.sps:5096`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1363 | | `income:income.2021.wife_alimony` | RESOLVED | ER81732: SPOUSE ALIMONY-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1254`; `PSID/family/2021/FAM2021ER.sps:5112`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1367 | -| `income:income.2021.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1400`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:254`; `EV/phase2-20260927/out/u2-adjudicate.md:78` | +| `income:income.2021.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1400`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:254`; `EV/phase2-20260927/out/u2-adjudicate.md:78` | | `income:income.2021.head_farm_acc` | RESOLVED | ER81622: ACC FARM INCOME OF RP AND SPOUSE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1218`; `PSID/family/2021/FAM2021ER.sps:5002`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1339; `PSID/family/2021/FAM2021ER_formats.sps:36643` | | `income:income.2021.head_tanf_acc` | RESOLVED | ER81681: ACCURACY OF RP INCOME FROM TANF-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1237`; `PSID/family/2021/FAM2021ER.sps:5061`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1355; `PSID/family/2021/FAM2021ER_formats.sps:36889` | | `income:income.2021.head_other_welfare_acc` | RESOLVED | ER81685: ACCURACY OF RP OTHER WELFARE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1239`; `PSID/family/2021/FAM2021ER.sps:5065`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1355; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1356; `PSID/family/2021/FAM2021ER_formats.sps:36903` | @@ -722,12 +722,12 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2021.wife_other_retirement_acc` | RESOLVED | ER81725: ACCURACY OF SPOUSE OTHER RETIREMENT-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1252`; `PSID/family/2021/FAM2021ER.sps:5105`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `PSID/family/2021/FAM2021ER_formats.sps:37043` | | `income:income.2021.wife_va_pension_acc` | RESOLVED | ER81717: ACCURACY OF SPOUSE VA PENSION-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1249`; `PSID/family/2021/FAM2021ER.sps:5097`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1363; `PSID/family/2021/FAM2021ER_formats.sps:37015` | | `income:income.2021.wife_alimony_acc` | RESOLVED | ER81733: ACCURACY OF SPOUSE ALIMONY-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1255`; `PSID/family/2021/FAM2021ER.sps:5113`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1367; `PSID/family/2021/FAM2021ER_formats.sps:37071` | -| `income:income.2021.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5098`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5100`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1251`; `PSID/family/2021/FAM2021ER.sps:5102`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `PSID/family/2021/FAM2021ER.sps:1252`; `PSID/family/2021/FAM2021ER.sps:5104`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `EV/phase2-20260927/out/u2-adjudicate.md:79` | +| `income:income.2021.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5098`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1250`; `PSID/family/2021/FAM2021ER.sps:5100`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1364; `PSID/family/2021/FAM2021ER.sps:1251`; `PSID/family/2021/FAM2021ER.sps:5102`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `PSID/family/2021/FAM2021ER.sps:1252`; `PSID/family/2021/FAM2021ER.sps:5104`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1365; `EV/phase2-20260927/out/u2-adjudicate.md:79` | | `income:income.2023.interview` | RESOLVED | ER82002: 2023 FAMILY INTERVIEW (ID) NUMBER. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:11`; `PSID/family/2023/FAM2023ER.sps:1333`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 2 | | `income:income.2023.fu_size` | RESOLVED | ER82017: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1348`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:228` | | `income:income.2023.head_age` | RESOLVED | ER82018: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1349`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6 | | `income:income.2023.head_sex` | RESOLVED | ER82019: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1350`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:252` | -| `income:income.2023.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1351`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:80` | +| `income:income.2023.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1351`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:80` | | `income:income.2023.n_children` | RESOLVED | ER82022: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:18`; `PSID/family/2023/FAM2023ER.sps:1353`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:265` | | `income:income.2023.head_labor` | RESOLVED | ER85496: LABOR INCOME OF REF PERSON-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1176`; `PSID/family/2023/FAM2023ER.sps:4827`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1262 | | `income:income.2023.head_farm` | RESOLVED | ER85475: FARM INCOME OF REF PERSN AND SPOUSE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1169`; `PSID/family/2023/FAM2023ER.sps:4806`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1257 | @@ -810,7 +810,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2023.ofum_asset_acc` | RESOLVED | ER85600: ACCURACY OF OTR FU MEMBR ASSET Y-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1210`; `PSID/family/2023/FAM2023ER.sps:4931`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1289; `PSID/family/2023/FAM2023ER_formats.sps:34690` | | `income:income.2023.wife_va_pension` | RESOLVED | ER85570: SPOUSE VA PENSION-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1200`; `PSID/family/2023/FAM2023ER.sps:4901`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1281 | | `income:income.2023.wife_alimony` | RESOLVED | ER85586: SPOUSE ALIMONY-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1206`; `PSID/family/2023/FAM2023ER.sps:4917`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1285 | -| `income:income.2023.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: the occupant is the in-family code-20 or code-22 person under the relationship rules (section 3; section 16a amendment 1), as the roles registry records them. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1352`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:258`; `EV/phase2-20260927/out/u2-adjudicate.md:81` | +| `income:income.2023.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1352`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:258`; `EV/phase2-20260927/out/u2-adjudicate.md:81` | | `income:income.2023.head_farm_acc` | RESOLVED | ER85476: ACC FARM INCOME OF RP AND SPOUSE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1169`; `PSID/family/2023/FAM2023ER.sps:4807`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1257; `PSID/family/2023/FAM2023ER_formats.sps:34227` | | `income:income.2023.head_tanf_acc` | RESOLVED | ER85535: ACCURACY OF RP INCOME FROM TANF-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1189`; `PSID/family/2023/FAM2023ER.sps:4866`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1273; `PSID/family/2023/FAM2023ER_formats.sps:34473` | | `income:income.2023.head_other_welfare_acc` | RESOLVED | ER85539: ACCURACY OF RP OTHER WELFARE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1190`; `PSID/family/2023/FAM2023ER.sps:4870`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1273; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1274; `PSID/family/2023/FAM2023ER_formats.sps:34487` | @@ -850,7 +850,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2023.wife_other_retirement_acc` | RESOLVED | ER85579: ACCURACY OF SPOUSE OTHER RETIREMENT-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1203`; `PSID/family/2023/FAM2023ER.sps:4910`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `PSID/family/2023/FAM2023ER_formats.sps:34627` | | `income:income.2023.wife_va_pension_acc` | RESOLVED | ER85571: ACCURACY OF SPOUSE VA PENSION-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1201`; `PSID/family/2023/FAM2023ER.sps:4902`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1281; `PSID/family/2023/FAM2023ER_formats.sps:34599` | | `income:income.2023.wife_alimony_acc` | RESOLVED | ER85587: ACCURACY OF SPOUSE ALIMONY-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1206`; `PSID/family/2023/FAM2023ER.sps:4918`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1285; `PSID/family/2023/FAM2023ER_formats.sps:34655` | -| `income:income.2023.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2023/FAM2023ER.sps:1201`; `PSID/family/2023/FAM2023ER.sps:4903`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4905`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4907`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `PSID/family/2023/FAM2023ER.sps:1203`; `PSID/family/2023/FAM2023ER.sps:4909`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `EV/phase2-20260927/out/u2-adjudicate.md:82` | +| `income:income.2023.wife_retirement_annuities` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 3 (historical spouse-retirement crosswalk) as written: this crosswalk stays non-executable. No scalar equivalence is asserted between U1's combined 'WIFE RETIREMENT/ANNUITIES' item and the separately documented spouse pension, annuity, IRA and other-retirement items listed under variables. Those components, spouse IRA income included, stay inside total family money income under section 4 and are never added a second time; U2 reads none of the four as an input of its own. No row changes. The adjudication's question is kept as superseded_question. Non-executable metadata (u2-draft-4 amendment 3): no U2 input or row reads this crosswalk, and no scalar historical equivalence is inferred from it. | `PSID/family/2023/FAM2023ER.sps:1201`; `PSID/family/2023/FAM2023ER.sps:4903`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4905`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1282; `PSID/family/2023/FAM2023ER.sps:1202`; `PSID/family/2023/FAM2023ER.sps:4907`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `PSID/family/2023/FAM2023ER.sps:1203`; `PSID/family/2023/FAM2023ER.sps:4909`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1283; `EV/phase2-20260927/out/u2-adjudicate.md:82` | | `wealth:wealth.2013.wealth1` | RESOLVED | ER58209: IMP WEALTH W/O EQUITY (WEALTH1) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1747`; `PSID/family/2013/FAM2013ER.sps:7032`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1851 | | `wealth:wealth.2013.wealth1_acc` | RESOLVED | ER58210: ACC WEALTH W/O EQUITY (WEALTH1) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1747`; `PSID/family/2013/FAM2013ER.sps:7033`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1852 | | `wealth:wealth.2013.wealth2` | RESOLVED | ER58211: IMP WEALTH W/ EQUITY (WEALTH2) 2013. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2013/FAM2013ER.sps:1747`; `PSID/family/2013/FAM2013ER.sps:7034`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 1852 | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 324b2f409..740f656a7 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -372,7 +372,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. -## Rulings applied to the registries (from 2026-10-12, d637) +## Rulings applied to the registries (from 2026-10-11, d637) A Claude Code session (Opus 5.5, session `943a9dba-…`) applied Max's ruling d637 (2026-09-30; specification §16b) to the U2 source registries, one reviewed commit per ratified amendment. It is not a U2 forecaster or referee, and it computed nothing from real data. @@ -402,6 +402,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 5a: `pension.json` (the four inherited-route amendments, their 40 dependents and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row, the d637 section and 44 rows); `u2_source_registry.py` (5a's scope), `sources.py` (the pension byte pin and a docstring) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_pension_registry.py`, `test_u2_employer_dc.py` and `test_u2_scripts.py`; and this section. - Amendment 6: `weights.json` (the 2017 weight and a d637 record), `individual.json` (the released 2017 weight) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the weight blocker row, the d637 section and two rows); `u2_source_registry.py` (amendment 6's scope), `sources.py` (two byte pins) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Wording: `income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json` and `weights.json` (each `independent_adjudication` note; the wealth `status_scope`) and `u1_identity.json`; the specification's §19; the master record (the milestone-1b D row); `sources.py` (six byte pins); `tests/track_u2/test_u2_mappings.py`; and this section. -- Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261012/`. +- First-round review fixes: `income.json` (amendment 2's ten rules and amendment 3's six resolutions) and `u1_identity.json`; the specification's §19; the master record (the spouse age/sex blocker row, the d637 section and 16 rows); `u2_source_registry.py` (the scope checks) and `sources.py` (the slot-metadata guard and the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. The reviews' own reading is in their records. +- Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index e1ae04512..99b0b5b83 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -84,12 +84,14 @@ #: The u2-draft-4 amendments Max ratified on 2026-09-30 (d637; #: specification section 16b) that a registry commit has applied, each #: with its exact scope: the registry and entries it rules, the ruling -#: they record, the gate fields it fixes (``fields``, exact values) and -#: the fields it removes (``absent``). An entry that records a d637 -#: ruling must be in its amendment's scope and hold those values, and -#: every entry in a scope must record the ruling. Each registry commit -#: adds its amendment here (section 16a, "Registry effect of the -#: rulings"). +#: they record, the adjudication disposition they keep, the gate fields +#: it fixes (``fields``, exact values), the fields it removes +#: (``absent``), the fields that must hold its text (``required``, +#: nonempty) and the fixed values that only its entries may carry +#: (``markers``). An entry that records a d637 ruling must be in its +#: amendment's scope and hold those values, and every entry in a scope +#: must record the ruling. Each registry commit adds its amendment here +#: (section 16a, "Registry effect of the rulings"). RATIFIED_AMENDMENTS: dict[str, dict[str, Any]] = { "2": { "registry": "income", @@ -99,8 +101,11 @@ for route in ("wife_age", "wife_sex") ), "ruling": {**_D637, "amendment": "2"}, + "disposition": "A", "fields": {"status": "RESOLVED", "usage": "slot_metadata_only"}, "absent": ("action", "blocking_dependencies", "question"), + "required": ("use_restriction", "resolution", "superseded_question"), + "markers": ("usage",), }, "3": { "registry": "income", @@ -109,14 +114,22 @@ for wave in (2013, *_LATER_WAVES) ), "ruling": {**_D637, "amendment": "3"}, + "disposition": "A", "fields": { "status": "RESOLVED", "kind": "historical_route_crosswalk", "usage": ( "Documentary crosswalk only; not an executable derived input" ), + "use_restriction": ( + "Non-executable metadata (u2-draft-4 amendment 3): no U2 " + "input or row reads this crosswalk, and no scalar historical " + "equivalence is inferred from it." + ), }, - "absent": ("action", "question"), + "absent": ("action", "question", "variable"), + "required": ("resolution", "superseded_question", "variables"), + "markers": ("use_restriction",), }, "4": { "registry": "wealth", @@ -127,6 +140,7 @@ if route == "wealth2_acc" or wave < 2019 ), "ruling": {**_D637, "amendment": "4"}, + "disposition": "D", "fields": { "status": "RESOLVED", "use_restriction": ( @@ -137,6 +151,20 @@ ), }, "absent": ("action", "blocking_dependencies", "question"), + "required": ("resolution", "source_wording_conflict"), + "markers": ("use_restriction",), + # The corrected targets of section 16a's table (amendment 4). + "targets": { + "wealth.2013.wealth2_acc": "ER58211", + "wealth.2013.checking_saving_acc": "ER58161", + "wealth.2015.wealth2_acc": "ER65408", + "wealth.2015.checking_saving_acc": "ER65358", + "wealth.2017.wealth2_acc": "ER71485", + "wealth.2017.checking_saving_acc": "ER71435", + "wealth.2019.wealth2_acc": "ER77511", + "wealth.2021.wealth2_acc": "ER81838", + "wealth.2023.wealth2_acc": "ER85692", + }, }, "5b": { "registry": "pension", @@ -146,6 +174,7 @@ "amendment": "5b", "replaces_draft_3_lines": [1173, 1174, 1175, 1176], }, + "disposition": "A", "fields": { "status": "RESOLVED", "accepted_plan_types": [5], @@ -160,6 +189,13 @@ "blocking_dependencies", "accepted_conditional_types", ), + "required": ( + "condition", + "resolution", + "superseded_question", + "superseded_refusal", + ), + "markers": ("refused_branch", "off_route_disposition"), }, "5a": { "registry": "pension", @@ -172,8 +208,16 @@ "amendment": "5a", "replaces_draft_3_lines": [1173, 1174, 1175, 1176], }, + "disposition": "D", "fields": {"status": "RESOLVED"}, "absent": ("action", "refusal", "question", "blocking_dependencies"), + "required": ( + "required_amendment", + "resolution", + "ruling_effect", + "superseded_refusal", + ), + "markers": (), }, "6": { "registry": "weights", @@ -184,10 +228,19 @@ "option": "1", "amends_draft_3_lines": [134], }, + "disposition": "A", "fields": {"status": "RESOLVED", "variable": "ER34651"}, "absent": ("action", "refusal", "question", "blocking_dependencies"), + "required": ( + "resolution", + "registration_disclosure", + "superseded_question", + ), + "markers": ("registration_disclosure",), }, } +#: The rulings a registry entry may record. +_RULED_DECISIONS = ("d637", "d1090") #: The d637 amendment each ruled entry belongs to, by (registry, id). _AMENDMENT_OF_ENTRY = { (scope["registry"], entry): name @@ -250,6 +303,10 @@ def _ruling(entry: dict[str, Any]) -> None: _fields( ruling, {"decision": str, "date": str, "specification_section": str} ) + if ruling["decision"] not in _RULED_DECISIONS: + raise SourceAdjudicationError( + f"Unknown ruling {ruling['decision']!r} for {entry['id']}" + ) def _same(first: Any, second: Any) -> bool: @@ -288,6 +345,36 @@ def _ratified_amendment(entry: dict[str, Any], name: str) -> None: raise SourceAdjudicationError( f"{field} remains after the d637 ruling for {entry['id']}" ) + for field in scope["required"]: + if not entry.get(field): + raise SourceAdjudicationError( + f"d637 amendment {amendment} needs {field} for {entry['id']}" + ) + if entry.get("blocking_dependencies"): + raise SourceAdjudicationError( + f"A d637 ruling applies only unblocked, for {entry['id']}" + ) + record = entry.get("adjudication") + if ( + not isinstance(record, dict) + or record.get("disposition") != scope["disposition"] + or not _same(record.get("amendment"), int(amendment[0])) + ): + raise SourceAdjudicationError( + f"Adjudication record is not amendment {amendment}'s " + f"{scope['disposition']} for {entry['id']}" + ) + target = scope.get("targets", {}).get(entry["id"]) + if "targets" in scope and not ( + entry.get("accuracy_for_variable") == target + and entry["source_wording_conflict"].get( + "resolved_accuracy_for_variable" + ) + == target + ): + raise SourceAdjudicationError( + f"Corrected target is not the ratified one for {entry['id']}" + ) def _family_unit_exclusion(entry: dict[str, Any]) -> None: @@ -469,6 +556,10 @@ def _entry_payload(entry: dict[str, Any], name: str) -> None: if entry.get("kind") == "historical_route_crosswalk": _list(entry, "variables", str) _fields(entry, {"usage": str}) + if "variable" in entry: + raise SourceAdjudicationError( + f"A crosswalk declares no scalar variable: {entry['id']}" + ) elif "kind" not in entry or entry["kind"] == "variable": _fields( entry, @@ -696,6 +787,22 @@ def validate_registry(document: dict[str, Any], *, expected_name: str) -> None: raise SourceAdjudicationError( f"{name}:{key} lacks its d637 amendment {amendment} ruling" ) + # A value only an amendment's entries may carry stays inside its scope. + for amendment, scope in RATIFIED_AMENDMENTS.items(): + if scope["registry"] != expected_name: + continue + for entry in entries: + if entry["id"] in scope["entries"]: + continue + for field in scope["markers"]: + if field in entry and ( + field not in scope["fields"] + or _same(entry[field], scope["fields"][field]) + ): + raise SourceAdjudicationError( + f"{field} of d637 amendment {amendment} outside its " + f"scope: {expected_name}:{entry['id']}" + ) def load_registry(name: str) -> dict[str, Any]: diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index edbc3383b..9792a8212 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -244,10 +244,11 @@ #: wording commit moved six registries' bytes once more: each #: ``independent_adjudication`` note now records the rulings, and the #: wealth registry's ``status_scope`` no longer says role assignment is -#: refused. +#: refused. The first-round review fixes moved the income bytes again +#: (amendment 2's rule text and amendment 3's resolution). REGISTRY_SHA256: dict[str, str] = { "income": ( - "034d3f2e3b1d121e544584c650fc317454112500fd900043dc4e7ec0be611562" + "aea295524e689e3464147669c72b64c474208ceefe1efe0d33bcb0381e22dca3" ), "wealth": ( "cebf9bfad6a07d2a0e7ec8c627f30ed10641512c479bc482b91998b9e21294e8" @@ -363,6 +364,9 @@ 2021: ("account",), 2023: ("account",), } +#: The one slot-metadata concept U2 reads: the family file's spouse age, +#: for its slot flag only (amendment 2; :func:`field_specs`). +_SLOT_FLAG_CONCEPTS = frozenset({"wife_age"}) _COUNTED_DISPOSITION = 3 _EXCLUDED_IRA_DISPOSITION = 2 _DC_PERSONS = ("head", "wife") @@ -1169,7 +1173,12 @@ def field_specs( gate: SourceGate, ) -> list[FieldSpec]: """Fixed-width specs of ``concepts`` from registry ``name`` (income or - wealth), each entry passed through ``gate``.""" + wealth), each entry passed through ``gate``. + + A slot-metadata entry (amendment 2, ratified by d637: the later + family-file spouse age and sex) is read only as ``wife_age``, for the + family file's slot flag that :func:`.cohort.income_rows` holds equal + to the roster; any other such concept refuses under either gate.""" if name not in ("income", "wealth"): raise ValueError("field_specs reads the income or wealth registry") @@ -1182,6 +1191,14 @@ def field_specs( f"{name}:{entry_id} is a {entry.get('kind')}: documentary " "only, not an executable input (section 4)" ) + if ( + entry.get("usage") == "slot_metadata_only" + and concept not in _SLOT_FLAG_CONCEPTS + ): + raise U2SourceRefusal( + f"{name}:{entry_id} is slot metadata only (amendment 2, " + "ratified by d637): no U2 input reads it" + ) specs.append( FieldSpec( concept=concept, diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 9d87dcc16..7766908d7 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,367 | -| `artifact` | 4,119 | -| `integration_psid` | 1,387 | +| `unit` | 7,370 | +| `artifact` | 4,135 | +| `integration_psid` | 1,395 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,701** | +| **Total** | **13,728** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 4bbc869fb..4a8cafa84 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,9 +1,9 @@ { "schema_version": 1, "counts": { - "unit": 7367, - "artifact": 4119, - "integration_psid": 1387, + "unit": 7370, + "artifact": 4135, + "integration_psid": 1395, "reproduction_legacy": 520, "oracle_policyengine": 308 } diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index a35d15787..cc1c28264 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -3,6 +3,7 @@ Documentary checks only: no survey records, counts or outcome calculations. """ +import copy import hashlib import html import json @@ -565,7 +566,14 @@ def test_d637_amendment_2_spouse_metadata_never_decides_the_slot(documents): f"('{wording.group(1)}') never decide whether a person occupies " "the spouse slot" in rule ), key - assert "the in-family code-20 or code-22 person" in rule + # Occupancy is the role rules', not a closed list of codes, so the + # rule decides nothing about code 88 (amendment 1c, pending d745). + assert ( + "slot occupancy comes from the relationship rules, as the roles " + "registry's spouse_slot field records them" in rule + ) + assert "code 88's slot status stays TO VERIFY" in rule + assert "code-20 or code-22 person" not in rule assert "recorded sex (ER32000) and derived income-year age" in rule # 2013 is U1's wave: its spouse age and sex keep U1's mapping. for route in ("wife_age", "wife_sex"): @@ -610,6 +618,194 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): assert "blocking_dependencies" not in weight +#: The text fields a d637 ruling writes or keeps. +D637_TEXT_FIELDS = ( + "condition", + "resolution", + "use_restriction", + "ruling_effect", + "registration_disclosure", + "superseded_question", + "superseded_refusal", +) +#: SHA-256 of each applied amendment's ruled text (its entries' text +#: fields, as canonical JSON): no wording moves without this pin moving +#: (reviews of 02e3f3e2 and 06fa7d98). +D637_TEXT_SHA256 = { + "2": ("64a67aae69d9d0bd6c6743328e8169304708003845395830ad628a6ee708887d"), + "3": ("8d93d501819d981c6605b02fde3ff89d05488738146757bd179a84c02e131598"), + "4": ("0b79677a5d1b41a76b17709152bec0d3789d118c75859b9716ea505c3413cd9c"), + "5b": ("7b16f9fdaa76d42d0d9c64dff3b7607a22e476378d35f3a9b1360d94f3c18552"), + "5a": ("1ce1d48c5661374960454e2c2d82a19fbc6a33abb432a50a0df40c773d8b8e76"), + "6": ("ad76188063ca16ddcdcc2d8e2165348293a4c88e9437e8e173765df91ffc0d2f"), +} +#: The operative clauses each amendment's entries must state, by field +#: (``{wave}`` is the entry's wave). +D637_CLAUSES = { + "2": [ + ("use_restriction", "Slot metadata only (u2-draft-4 amendment 2)."), + ( + "use_restriction", + "never decide whether a person occupies the spouse slot", + ), + ("use_restriction", "code 88's slot status stays TO VERIFY"), + ("use_restriction", "whose parts 1a-1c await Max's confirmation"), + ("use_restriction", "never this variable."), + ("resolution", "ratified u2-draft-4 amendment 2"), + ], + "3": [ + ("resolution", "this crosswalk stays non-executable"), + ("resolution", "No scalar equivalence is asserted"), + ( + "resolution", + "spouse IRA income included, stay inside total family money " + "income under section 4 and are never added a second time", + ), + ("resolution", "U2 reads none of the four as an input of its own"), + ("resolution", "No row changes."), + ], + "4": [ + ("use_restriction", "Imputation diagnostics only"), + ("use_restriction", "never changes amounts or sample inclusion"), + ], + "5b": [ + ("resolution", "P46=5 (DC only) reaches P64/P65"), + ("resolution", "P46=7 (combined) reaches P48/P49"), + ("resolution", "is refused pending PSID clarification"), + ("resolution", "off-route under the named disposition off_route"), + ("resolution", "never zero-filled"), + ("condition", "DC-only plan (P46=5)"), + ], + "5a": [ + ("ruling_effect", "Under 5a the {wave} previous-employer account"), + ("ruling_effect", "only P46=5 (DC only) reaches P64/P65"), + ("ruling_effect", "under the named disposition off_route"), + ("ruling_effect", "combined-plan P48/P49 is counted once"), + ("ruling_effect", "IRA-rollover disposition 2 is excluded"), + ], + "6": [ + ("resolution", "took amendment 6's option 1 for blocker B3"), + ("resolution", "ER34651 is used as released"), + ("resolution", "no immigrant exclusion"), + ("resolution", "no recalibration"), + ], +} + + +def d637_texts(documents, amendment): + """Each ruled entry's text fields, in scope order.""" + scope = registry.RATIFIED_AMENDMENTS[amendment] + by_id = {e["id"]: e for e in documents[scope["registry"]]["entries"]} + return [ + { + "id": key, + **{f: by_id[key][f] for f in D637_TEXT_FIELDS if f in by_id[key]}, + } + for key in scope["entries"] + ] + + +def check_d637_texts(documents): + """The d637 entries' wording: each amendment's pinned hash and its + operative clauses.""" + assert set(D637_TEXT_SHA256) == set(registry.RATIFIED_AMENDMENTS) + for amendment, digest in D637_TEXT_SHA256.items(): + texts = d637_texts(documents, amendment) + raw = json.dumps(texts, sort_keys=True, ensure_ascii=False) + assert hashlib.sha256(raw.encode()).hexdigest() == digest, amendment + for item in texts: + wave = item["id"].split(".")[1 if item["id"][0].isalpha() else 0] + for field, clause in D637_CLAUSES[amendment]: + assert clause.format(wave=wave) in item[field], ( + item["id"], + clause, + ) + + +def test_d637_texts_are_pinned(documents): + check_d637_texts(documents) + + +#: (registry, entry, field, old, new): each changes what a d637 entry +#: says and must fail ``check_d637_texts``. +D637_TEXT_MUTATIONS = [ + ( + "income", + "income.2019.wife_age", + "use_restriction", + ", and code 88's slot status stays TO VERIFY", + "", + ), + ( + "income", + "income.2015.wife_sex", + "superseded_question", + "In 2015, how do", + "In 2015, why do", + ), + ( + "income", + "income.2017.wife_retirement_annuities", + "resolution", + " and are never added a second time", + "", + ), + ( + "income", + "income.2013.wife_retirement_annuities", + "resolution", + "No scalar equivalence is asserted", + "A scalar equivalence is asserted", + ), + ( + "pension", + "2015.route.formula_unknown_checkpoint", + "resolution", + "never zero-filled", + "zero-filled", + ), + ( + "pension", + "2021.route.inherited_route_amendment", + "ruling_effect", + "only P46=5 (DC only)", + "P46=1 and P46=5", + ), + ( + "weights", + "2017.cross_section_weight", + "resolution", + "no immigrant exclusion", + "an immigrant exclusion", + ), +] + + +@pytest.mark.parametrize( + "name,key,field,old,new", + D637_TEXT_MUTATIONS, + ids=[f"{m[1]}:{m[2]}" for m in D637_TEXT_MUTATIONS], +) +def test_the_d637_text_checks_catch_each_mutation( + documents, name, key, field, old, new +): + changed = {n: copy.deepcopy(d) for n, d in documents.items()} + entry = next(e for e in changed[name]["entries"] if e["id"] == key) + assert old in entry[field], (key, field) + entry[field] = entry[field].replace(old, new, 1) + with pytest.raises(AssertionError): + check_d637_texts(changed) + # A disclosure item dropped from the 2017 weight fails too. + weights = next( + e + for e in changed["weights"]["entries"] + if e["id"] == "2017.cross_section_weight" + ) + weights["registration_disclosure"]["not_published"].pop() + with pytest.raises(AssertionError): + check_d637_texts(changed) + + def test_corrected_specification_citations(documents): spec = (ROOT / SPEC).read_text(encoding="utf-8").split("\n") corrected = 0 diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index eea9110a3..2ce442cc2 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -827,6 +827,109 @@ def _copy_ruling(document, source, target): status="TO VERIFY", question="reopened" ), ), + # First-round reviews (of 02e3f3e2, 06fa7d98 and 6b047381). + "a2_amendment_record_mismatch": ( + "income", + lambda d: _entry(d, "income.2019.wife_age")["adjudication"].update( + amendment=3 + ), + ), + "a2_restriction_dropped": ( + "income", + lambda d: _entry(d, "income.2017.wife_sex").pop("use_restriction"), + ), + "a2_question_record_dropped": ( + "income", + lambda d: _entry(d, "income.2023.wife_age").pop("superseded_question"), + ), + "a2_usage_on_2013": ( + "income", + lambda d: _entry(d, "income.2013.wife_age").update( + usage="slot_metadata_only" + ), + ), + "a3_variable_added": ( + "income", + lambda d: _entry(d, "income.2013.wife_retirement_annuities").update( + variable="ER58101" + ), + ), + "a3_crosswalk_blocked": ( + "income", + lambda d: _entry(d, "income.2013.wife_retirement_annuities").update( + blocking_dependencies=["roles:2013.relationship.20"] + ), + ), + "a3_restriction_permits_input": ( + "income", + lambda d: _entry(d, "income.2021.wife_retirement_annuities").update( + use_restriction="U2 reads this crosswalk as the spouse input." + ), + ), + "a4_target_reverted": ( + "wealth", + lambda d: _entry(d, "wealth.2013.wealth2_acc").update( + accuracy_for_variable="ER58209" + ), + ), + "a4_conflict_target_reverted": ( + "wealth", + lambda d: _entry(d, "wealth.2015.wealth2_acc")[ + "source_wording_conflict" + ].update(resolved_accuracy_for_variable="ER65406"), + ), + "a4_disposition_changed": ( + "wealth", + lambda d: _entry(d, "wealth.2017.checking_saving_acc")[ + "adjudication" + ].update(disposition="A"), + ), + "a4_restriction_on_an_unruled_flag": ( + "wealth", + lambda d: _entry(d, "wealth.2019.checking_saving_acc").update( + use_restriction=_entry(d, "wealth.2019.wealth2_acc")[ + "use_restriction" + ] + ), + ), + "a4_decision_misspelled": ( + "wealth", + lambda d: _entry(d, "wealth.2019.checking_saving_acc").update( + ruling={ + "decision": "d637 ", + "date": "2026-09-30", + "specification_section": "16b", + } + ), + ), + "a5b_branch_on_2017_checkpoint": ( + "pension", + lambda d: _entry(d, "2017.route.formula_unknown_checkpoint").update( + refused_branch="P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF" + ), + ), + "a5a_ruling_effect_dropped": ( + "pension", + lambda d: _entry(d, "2019.route.inherited_route_amendment").pop( + "ruling_effect" + ), + ), + "a6_disclosure_on_2019_weight": ( + "weights", + lambda d: _entry(d, "2019.cross_section_weight").update( + registration_disclosure=copy.deepcopy( + _entry(d, "2017.cross_section_weight")[ + "registration_disclosure" + ] + ) + ), + ), + "a6_disclosure_dropped": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight").pop( + "registration_disclosure" + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index b2dfecdc1..dccda4152 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -385,8 +385,26 @@ def test_d637_moved_only_the_ruled_gate_fields(): releasable = set().union(*D637_RELEASED.values()) moved, released = set(), {} for name in registry.REGISTRY_NAMES: - before = _gate_projection(json.loads(_at(D1090_COMMIT, name))) - after = _gate_projection(json.loads(_current(name))) + raw_before = json.loads(_at(D1090_COMMIT, name)) + raw_after = json.loads(_current(name)) + before = _gate_projection(raw_before) + after = _gate_projection(raw_after) + # A question or refusal a ruling answers is kept verbatim: compared + # in full, since ``question`` is prose the projection drops. + old_raw = {e["id"]: e for e in raw_before["entries"]} + for entry in raw_after["entries"]: + if (name, entry["id"]) not in ruled: + continue + for kept, field in ( + ("superseded_question", "question"), + ("superseded_refusal", "refusal"), + ): + if kept in entry: + assert entry[kept] == old_raw[entry["id"]][field], ( + name, + entry["id"], + ) + assert field not in entry, (name, entry["id"]) top = _changed_keys(before, after) - {"entries"} assert top == D637_TOP.get(name, set()), name if name in D637_RECORDED: @@ -643,6 +661,44 @@ def test_wealth1_identity_counts_a_broken_identity(u2_inputs, declared_gate): ) +@pytest.mark.parametrize( + "gate_kind", [sources.REGISTRY, sources.INVENTED_DECLARED] +) +def test_slot_metadata_is_read_only_as_the_slot_flag( + gate_kind, committed_registries +): + """Amendment 2 (ratified by d637): the later family-file spouse age and + sex are slot metadata only. The adapter reads the spouse age, for the + slot flag the income rows hold equal to the roster, and refuses the + spouse sex under either gate.""" + for wave in (2015, 2017, 2019, 2021, 2023): + gate = sources.SourceGate(gate_kind, committed_registries) + (spec,) = sources.field_specs("income", wave, ["wife_age"], gate) + assert spec.kind == "age" + with pytest.raises(sources.U2SourceRefusal, match="slot metadata"): + sources.field_specs("income", wave, ["wife_sex"], gate) + assert "wife_sex" not in sources.INCOME_CONCEPTS + + +def test_spouse_age_and_sex_are_read_nowhere_else(): + """Static guard for amendment 2: no U2 module but the adapters and the + invented generator names the family-file spouse age or sex, and U1's + income concept, which U2's rows feed, names neither.""" + from pathlib import Path + + from populace_dynamics.estimates import adjusted_poverty + + package = Path(sources.__file__).parent + allowed = {"sources.py", "invented.py"} + for path in sorted(package.glob("*.py")): + if path.name in allowed: + continue + text = path.read_text(encoding="utf-8") + assert "wife_age" not in text and "wife_sex" not in text, path.name + text = Path(adjusted_poverty.__file__).read_text(encoding="utf-8") + assert "wife_age" not in text and "wife_sex" not in text + + def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): with pytest.raises(sources.U2SourceRefusal, match="documentary"): sources.field_specs( From a68ec6afabf5d6f28fab7dde9c8033b63193fb49 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 22:59:09 -0400 Subject: [PATCH 10/21] Answer the review of the d637 amendment-6 commit Review of 57778fa1 (GPT-6.1 Sol; Subfleet job 20261011-224548-d637-review-a6; REQUEST CHANGES, one should-fix and two nits). - The validator holds the 2017 weight's selection, purpose and registration_disclosure exactly (amendment 6's scope), so the disclosure that registration must state, the substance of option 1's reading of draft-3 line 134, cannot drift with only a re-pin. Five new mutations (coverage bound emptied, documented point overstated, unpublished point dropped, selection excluding the 2017 Immigrant sample, purpose recalibrating) each fail. The clause test now needs "no immigrant exclusion, no reconstructed weight and no recalibration" (finding 1). - The construction note says the open points "remain undocumented", not "unresolved", on a RESOLVED record (finding 3). - Kept: the documented item on the 2017 Immigrant sample's base weights says "formed and integrated with the core sample, for the 2019 weights", which the research record and section 16a support (finding 2). - sources: the weights byte pin; section 19 records the round; the spec is re-pinned. The loader's source preflight still refuses on 12 blockers, all code 88's. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- data/external/track_u2/weights.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 69 ++++++++++++++++++- .../uniform_cut_track_u2/sources.py | 6 +- tests/README-tiers.md | 4 +- tests/tier_counts.json | 2 +- tests/track_u2/test_adjudication_applied.py | 7 +- tests/track_u2/test_source_registries.py | 36 ++++++++++ 10 files changed, 120 insertions(+), 10 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index aee585259..7d884fd3e 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "ea4753b966ebc14e8dc2225e081238c212cf91d10c5e17636c69af6e56647632", + "sha256": "a84533ff619537e43422ffa828889d20abe61eb02a0b6605f255f448da6a934f", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/data/external/track_u2/weights.json b/data/external/track_u2/weights.json index 841fc4035..6055f1c2d 100644 --- a/data/external/track_u2/weights.json +++ b/data/external/track_u2/weights.json @@ -334,7 +334,7 @@ ], "selection": "positive recorded cross-sectional weight; do not infer a domain from empirical ranges printed in codebook", "purpose": "Use reporting-wave individual cross-sectional weight times documentary plan multiplier. No cohort reweighting or immigrant exclusion.", - "construction": "UserGuide2017 p. 62 says ER34651 and ER71571 were updated to include the NIS-2017 sample 'with Release 2 of PSID-2017 data'; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2). The May 2019 notes for the cross-wave individual file's Release 2 say the 2017 Immigrant individuals' weights will be updated in Release 3, and no individual-file document dates ER34651's update to a Release 2. The difference concerns timing only. The staged February 2019 construction report excludes post-1997 immigrants from its ACS calibration population; the currently served official report is byte-identical. The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, donut-hole families and group-quarters post-1997 immigrants excluded from the 2017 ACS estimate) and a calibration-variable selection approach used since 2017. Of the two, only the April 2021 report documents gender, race, region and age as calibration controls in comparisons that include 2017; both say cross-sectional weights for every wave since 1997 are built with a version of the fair-shares method. The 2019 longitudinal-weights report (April 2021) and the 2021 longitudinal-weights report (September 2023) document how the 2017 Immigrant sample's base weights were formed and integrated with the core sample, for the 2019 weights. PSID's later reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' (Chang, Nishimura, Heeringa, Johnson and Sastry) for content the served February 2019 report lacks. Which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for that sample, whose 2017 longitudinal family weight is zero, and the complete 2017 raking specification remain unresolved.", + "construction": "UserGuide2017 p. 62 says ER34651 and ER71571 were updated to include the NIS-2017 sample 'with Release 2 of PSID-2017 data'; that is the 2017 family file's own release series, whose Release 2 added ER71571 (FAM2017ER codebook PDF p. 2). The May 2019 notes for the cross-wave individual file's Release 2 say the 2017 Immigrant individuals' weights will be updated in Release 3, and no individual-file document dates ER34651's update to a Release 2. The difference concerns timing only. The staged February 2019 construction report excludes post-1997 immigrants from its ACS calibration population; the currently served official report is byte-identical. The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, donut-hole families and group-quarters post-1997 immigrants excluded from the 2017 ACS estimate) and a calibration-variable selection approach used since 2017. Of the two, only the April 2021 report documents gender, race, region and age as calibration controls in comparisons that include 2017; both say cross-sectional weights for every wave since 1997 are built with a version of the fair-shares method. The 2019 longitudinal-weights report (April 2021) and the 2021 longitudinal-weights report (September 2023) document how the 2017 Immigrant sample's base weights were formed and integrated with the core sample, for the 2019 weights. PSID's later reports cite a 2019 version of 'PSID Cross-Sectional Individual Weights, 1997-2017' (Chang, Nishimura, Heeringa, Johnson and Sastry) for content the served February 2019 report lacks. Which starting weights, and which integration of joint families with the core sample, the revised 2017 weight used for that sample, whose 2017 longitudinal family weight is zero, and the complete 2017 raking specification remain undocumented.", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 129, diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 2345788dc..21b4f81a1 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1849,6 +1849,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. - Wording: the six registries that record the independent adjudication no longer say its A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; each note records the rulings and that the six code-88 records wait for d745. The wealth registry's scope no longer says role assignment is "refused" by the role registry, which d1090 left stale in that registry. The master record's account of milestone 1b's D resolutions now says the 2017–2023 route's application was released under 5a. No gate field, count or blocker moves. - After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. +- After the review of the amendment-6 commit (GPT-6.1 Sol, Subfleet job `20261011-224548-d637-review-a6`, REQUEST CHANGES; same folder): the validator holds the 2017 weight's selection, purpose and `registration_disclosure` exactly, so the disclosure registration must state cannot drift, and five mutations that empty, overstate or drop part of it, or exclude or recalibrate the sample, each fail. Its construction note now says the open points "remain undocumented", not "unresolved", on a RESOLVED record. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 740f656a7..ebd259480 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -403,6 +403,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 6: `weights.json` (the 2017 weight and a d637 record), `individual.json` (the released 2017 weight) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (summary, table, the weight blocker row, the d637 section and two rows); `u2_source_registry.py` (amendment 6's scope), `sources.py` (two byte pins) and `loader.py` (docstring); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Wording: `income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json` and `weights.json` (each `independent_adjudication` note; the wealth `status_scope`) and `u1_identity.json`; the specification's §19; the master record (the milestone-1b D row); `sources.py` (six byte pins); `tests/track_u2/test_u2_mappings.py`; and this section. - First-round review fixes: `income.json` (amendment 2's ten rules and amendment 3's six resolutions) and `u1_identity.json`; the specification's §19; the master record (the spouse age/sex blocker row, the d637 section and 16 rows); `u2_source_registry.py` (the scope checks) and `sources.py` (the slot-metadata guard and the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. The reviews' own reading is in their records. +- Amendment-6 review fixes: `weights.json` (one word of the 2017 weight's construction note) and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (amendment 6's exact fields) and `sources.py` (the weights byte pin); `tests/track_u2/test_adjudication_applied.py` and `test_source_registries.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 99b0b5b83..9e22f0f0e 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -229,7 +229,74 @@ "amends_draft_3_lines": [134], }, "disposition": "A", - "fields": {"status": "RESOLVED", "variable": "ER34651"}, + "fields": { + "status": "RESOLVED", + "variable": "ER34651", + "selection": ( + "positive recorded cross-sectional weight; do not infer a " + "domain from empirical ranges printed in codebook" + ), + "purpose": ( + "Use reporting-wave individual cross-sectional weight times " + "documentary plan multiplier. No cohort reweighting or " + "immigrant exclusion." + ), + # Option 1's disclosure, exactly (section 16a amendment 6). + "registration_disclosure": { + "documented": [ + ( + "the weight's identity: ER34651, CORE/IMM INDIVIDUAL " + "CROSS-SECTION WT 17, the 2017 individual weight in " + "Table A3 of PSID's April 2021 and June 2026 " + "cross-sectional weight reports" + ), + ( + "the 2017 population control: the ACS one-year PUMS " + "population total, with the CPS not used, and its two " + "named exclusions, individuals in donut-hole families " + "and post-1997 immigrants living in group quarters" + ), + ( + "the calibration-variable selection approach PSID has " + "used since 2017" + ), + ( + "some calibration controls: gender, race, region and " + "age in 10-year groups, in comparisons that include " + "2017 (April 2021 report)" + ), + ( + "how the 2017 Immigrant sample's base weights were " + "formed and integrated with the core sample, for the " + "2019 weights (the 2019 longitudinal-weights report)" + ), + ], + "not_published": [ + ( + "which starting weights the revised 2017 weight used " + "for the 2017 Immigrant sample, whose 2017 " + "longitudinal family weight is zero" + ), + ( + "how that sample's joint families were integrated " + "with the core sample in the revised 2017 weight" + ), + ( + "the complete 2017 raking specification: every " + "calibration variable and interaction, the collapsed " + "categories and the trimming" + ), + ], + "coverage_bound": ( + "The control's documented exclusions bound coverage: the " + "donut-hole group includes post-1997 immigrants who " + "co-reside with individuals born in 1960-1971, with no " + "limit on their own birth year, and post-1997 immigrants " + "in group quarters are excluded too (June 2026 report, " + "Table A2 note)." + ), + }, + }, "absent": ("action", "refusal", "question", "blocking_dependencies"), "required": ( "resolution", diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 9792a8212..ede02025d 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -245,7 +245,9 @@ #: ``independent_adjudication`` note now records the rulings, and the #: wealth registry's ``status_scope`` no longer says role assignment is #: refused. The first-round review fixes moved the income bytes again -#: (amendment 2's rule text and amendment 3's resolution). +#: (amendment 2's rule text and amendment 3's resolution), and the +#: second-round fixes the weights bytes (one word of the 2017 weight's +#: construction note). REGISTRY_SHA256: dict[str, str] = { "income": ( "aea295524e689e3464147669c72b64c474208ceefe1efe0d33bcb0381e22dca3" @@ -266,7 +268,7 @@ "419341b90e0adae2c8a8ee1c2324d405bbd5e0a49faa54a00c19025fc6fd57c9" ), "weights": ( - "6042524ae68d2163bd5b119ecfc0837e6c9e59713b0d50cffe9fc5b788a20e2c" + "456666033dc02e79c9603a7cbe3dfd7b879eaea92e8398c508923aaf73c68221" ), "design": ( "56181372d412811af6d06f5c0d8d1e071049f7fb73c34bd394aaff8f5e0c2fd6" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 7766908d7..e87a6f52b 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -40,8 +40,8 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| | `unit` | 7,370 | -| `artifact` | 4,135 | +| `artifact` | 4,140 | | `integration_psid` | 1,395 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,728** | +| **Total** | **13,733** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 4a8cafa84..c8720f34f 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -2,7 +2,7 @@ "schema_version": 1, "counts": { "unit": 7370, - "artifact": 4135, + "artifact": 4140, "integration_psid": 1395, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index cc1c28264..1bf0f5dcb 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -686,8 +686,11 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): "6": [ ("resolution", "took amendment 6's option 1 for blocker B3"), ("resolution", "ER34651 is used as released"), - ("resolution", "no immigrant exclusion"), - ("resolution", "no recalibration"), + ( + "resolution", + "no immigrant exclusion, no reconstructed weight and no " + "recalibration", + ), ], } diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 2ce442cc2..624897e99 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -930,6 +930,42 @@ def _copy_ruling(document, source, target): "registration_disclosure" ), ), + # Second-round review (of 57778fa1): option 1's disclosure, selection + # and purpose, exactly. + "a6_coverage_bound_emptied": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight")[ + "registration_disclosure" + ].update(coverage_bound=""), + ), + "a6_documented_overstated": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight")[ + "registration_disclosure" + ]["documented"].__setitem__( + 4, "the complete construction of the revised 2017 weight" + ), + ), + "a6_not_published_item_dropped": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight")[ + "registration_disclosure" + ]["not_published"].pop(), + ), + "a6_selection_excludes_immigrants": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight").update( + selection="positive recorded cross-sectional weight; exclude " + "the 2017 Immigrant sample" + ), + ), + "a6_purpose_recalibrates": ( + "weights", + lambda d: _entry(d, "2017.cross_section_weight").update( + purpose=_entry(d, "2017.cross_section_weight")["purpose"] + + " Recalibrate 2017 to ACS." + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( From 5df83f88272373da01a4250bd5d36794add7617f Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 11 Oct 2026 23:05:54 -0400 Subject: [PATCH 11/21] Answer the review of the d637 amendment-5 commits Review of 8de64f51, 72062158 and d07da5dc (Opus 5.5 on a review lane; Subfleet job 20261011-224548-d637-review-a5; REQUEST CHANGES). It judged the section 15 decision right: not editing section 15 now is "the only reading that fits the record". - Amendment 5's texts (the four 5a ruling_effect entries and the 2015 checkpoint's resolution) name both dispositions the builder applies: off_route for formula, DK, refused and NA plans, and the duplicate guard (both_plan_account_items_reasked; duplicate_and_off_route) for a combined plan's re-asked P64/P65 items. They said all went under off_route (finding 1). The 5a text says its route supersedes section 15's generic route while section 15's text waits for section 20 step 5 (finding 6); section 17 item 2 says the fragment is materialized under item 9, and the master record's P64/P65 row says the same. - The validator pins the route entries the builder reads for each later wave: 2017-2023's DC-only checkpoints (accepted 5, rejected 1, 7, 8, 9, no conditional route) and the combined-plan and DC-only plan types, 2015's included (finding 4). Five mutations each fail. - invented: from 2019 an account plan carries a DK amount on its route (the unreported zero) beside the DK-type plan's off-route DK amount; the comments and the module docstring say which is which (finding 2). - Tests: every route entry dc_route requires, in every wave, is re-blocked in memory by a dependency and by a refusing action, and each refuses (104 cases); a P64 item of each later wave is re-blocked through the family-record specs, replacing the empty first-blocker loop (finding 3). An off-route amount, a DK or NA sentinel included, is counted off-route and never as an unreported zero (finding 5). The ruled texts' operative clauses and hashes follow the new wording, with one more mutation (finding 7). The preflight's twelve blockers are pinned exactly, all code 88's (finding 8). The loader's source preflight still refuses on 12 blockers, all code 88's. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/pension.json | 10 +-- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 5 +- docs/design/u2_m1_source_adjudication.md | 12 ++-- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 53 +++++++++++++- .../uniform_cut_track_u2/invented.py | 13 +++- .../uniform_cut_track_u2/sources.py | 5 +- tests/README-tiers.md | 8 +-- tests/tier_counts.json | 6 +- tests/track_u2/test_adjudication_applied.py | 36 ++++++++-- tests/track_u2/test_source_registries.py | 32 +++++++++ tests/track_u2/test_u2_employer_dc.py | 72 ++++++++++++++++--- tests/track_u2/test_u2_mappings.py | 58 ++++++++++++--- 14 files changed, 263 insertions(+), 50 deletions(-) diff --git a/data/external/track_u2/pension.json b/data/external/track_u2/pension.json index d0bc46206..5cf5537a5 100644 --- a/data/external/track_u2/pension.json +++ b/data/external/track_u2/pension.json @@ -8984,7 +8984,7 @@ "condition": "DC-only plan (P46=5), the documentary intersection of the 2015 P62ACKPT heading, its branch label and the FAM2015ER P65 universe", "refused_branch": "P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF", "off_route_disposition": "off_route", - "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5b the 2015 route admits the documentary intersection: P46=5 (DC only) reaches P64/P65, and P46=7 (combined) reaches P48/P49 (pension:2015.route.previous_combined). The disputed P62ACKPT branch, whose heading reads 'P46=1 AND P52=1 AND P62AMT= DK/RF' while its branch label reads only 'P62AMT =DK/RF' (q2015 p. 164), is refused pending PSID clarification: an amount reached only through it is off-route under the named disposition off_route (section 15 employer_dc.excluded), never zero-filled. Formula (1), combined (7), DK (8) and NA or refused (9) plan types are off-route for P64/P65. refused_branch records amendment 5's section 15 fragment entry refused_branches for 2015. The adjudication's question and the route's refusal are kept as superseded_question and superseded_refusal.", + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5b the 2015 route admits the documentary intersection: P46=5 (DC only) reaches P64/P65, and P46=7 (combined) reaches P48/P49 (pension:2015.route.previous_combined). The disputed P62ACKPT branch, whose heading reads 'P46=1 AND P52=1 AND P62AMT= DK/RF' while its branch label reads only 'P62AMT =DK/RF' (q2015 p. 164), is refused pending PSID clarification: an amount reached only through it is off-route under the named disposition off_route (section 15 employer_dc.excluded), never zero-filled. Formula (1), combined (7), DK (8) and NA or refused (9) plan types are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2015.route.duplicate_and_off_route) and the others' under off_route. refused_branch records amendment 5's section 15 fragment entry refused_branches for 2015. The adjudication's question and the route's refusal are kept as superseded_question and superseded_refusal.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -13133,7 +13133,7 @@ "disposition": "D", "amendment": 5 }, - "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2017 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2017.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2017 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2017.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2017.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -17232,7 +17232,7 @@ "disposition": "D", "amendment": 5 }, - "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2019 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2019.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2019 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2019.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2019.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -21709,7 +21709,7 @@ "disposition": "D", "amendment": 5 }, - "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2021 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2021.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2021 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2021.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2021.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", "ruling": { "decision": "d637", "date": "2026-09-30", @@ -26241,7 +26241,7 @@ "disposition": "D", "amendment": 5 }, - "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2023 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2023.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2023 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2023.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2023.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal.", "ruling": { "decision": "d637", "date": "2026-09-30", diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 7d884fd3e..824a088c7 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "a84533ff619537e43422ffa828889d20abe61eb02a0b6605f255f448da6a934f", + "sha256": "3656e1db9aa1b882febd3bdcc9219de9222922e8b417eeb8a77d4474c78347ae", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 21b4f81a1..64d9fab6d 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1650,7 +1650,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1. The 6 TO VERIFY records left are code 88's, open until Max confirms the 1a–1c reading (d745). +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. The 6 TO VERIFY records left are code 88's, open until Max confirms the 1a–1c reading (d745). 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1845,11 +1845,12 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 4: the nine corrected wealth accuracy flags (adjudication D) already held their resolution; each now records the ruling, and its use restriction (imputation diagnostics only; the correction never changes an amount or a sample-inclusion decision) names the ratification in place of "pending Max's ruling", in `data/external/track_u2/wealth.json` and the master record's rows. No U2 path reads these flags, and no count or loader blocker moves. - Amendment 5, before its registry commits: the declared employer-DC route, which only invented data use, follows the ratified replacement fragment printed in §16a. A previous plan's account items count for an account plan (P46 = 5) in every wave and, in 2013 only, for a formula or DK plan too; otherwise they are off-route. §15's own `previous_routes` keeps draft 3's text, because §§3–15 keep it until §20 step 5 materializes the final parameter block (§§16b and 16c; §17 item 9), and where a ratified amendment and §§3–15 differ the amendment governs. A test holds the declared route equal to the §16a fragment and §15's block to draft 3's generic route, so the materialization must change both together. The invented population's 2015–2023 formula and DK previous-plan items become off-route. - Amendment 5b: the 2015 P62ACKPT checkpoint (`pension:2015.route.formula_unknown_checkpoint`) is RESOLVED as the documentary intersection. P46 = 5 reaches P64/P65 and P46 = 7 reaches P48/P49; the disputed branch, 'P46=1 AND P52=1 AND P62AMT=DK/RF' as the §16a fragment's `refused_branches` names it, is refused, so an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. The 34 entries that waited on the checkpoint are released. The registry gate now applies 2015's employer-DC route, and a differential test holds it equal to the declared route. The loader's source preflight now refuses on 58 blockers. The master record counts 1785 RESOLVED, 0 REFUSED and 7 TO VERIFY records. -- Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65, formula, combined, DK, refused and NA plans are off-route under `off_route`, and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). +- Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65; formula, combined, DK, refused and NA plans are off-route for P64/P65, a combined plan's re-asked items under the duplicate guard (`both_plan_account_items_reasked`) and the others' under `off_route`; and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). - Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. - Wording: the six registries that record the independent adjudication no longer say its A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; each note records the rulings and that the six code-88 records wait for d745. The wealth registry's scope no longer says role assignment is "refused" by the role registry, which d1090 left stale in that registry. The master record's account of milestone 1b's D resolutions now says the 2017–2023 route's application was released under 5a. No gate field, count or blocker moves. - After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. - After the review of the amendment-6 commit (GPT-6.1 Sol, Subfleet job `20261011-224548-d637-review-a6`, REQUEST CHANGES; same folder): the validator holds the 2017 weight's selection, purpose and `registration_disclosure` exactly, so the disclosure registration must state cannot drift, and five mutations that empty, overstate or drop part of it, or exclude or recalibrate the sample, each fail. Its construction note now says the open points "remain undocumented", not "unresolved", on a RESOLVED record. +- After the review of the amendment-5 commits (Opus 5.5 on a review lane, Subfleet job `20261011-224548-d637-review-a5`; REQUEST CHANGES, with the §15 decision judged "the only reading that fits the record"): amendment 5's texts now name both dispositions, `off_route` for formula, DK, refused and NA plans and the duplicate guard for a combined plan's re-asked P64/P65 items, as the builder counts them, and say the 5a route supersedes §15's generic route while §15's text waits for §20 step 5. The validator pins the route entries the builder reads for 2015–2023 (the DC-only checkpoint, combined-plan and DC-only plan types). The invented population has, from 2019, a DK amount on an account plan's route as well as one on an off-route plan, with its comments corrected. Tests re-block, in memory, every route entry the employer-DC route requires and a P64 item of each later wave, check that an off-route DK or NA amount is never an unreported zero, and pin the preflight's twelve code-88 blockers exactly. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index dd20a01d3..69f5c896c 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -29,7 +29,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Code 92, 2017–2023 | 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | | Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is proposed in amendment 1. | | 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | -| 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a replaces the §15 contract's generic account/formula/dk previous route with this wave-specific route, so the dependent P64/P65 entries are released. | +| 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a's wave-specific route supersedes the §15 contract's generic account/formula/dk previous route (§16b; §15's text is unchanged until §20 step 5 materializes the final parameter block), so the dependent P64/P65 entries are released. | | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot: the relationship rules in the roles registry do, and code 88's slot status stays TO VERIFY (amendment 1c, pending d745). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D); amendment 4 ratified by Max's ruling d637 | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics, and the corrections never change an amount or a sample-inclusion decision. | @@ -1367,7 +1367,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `pension:2015.route.previous_combined` | RESOLVED | Both previous-employer slots ask P48/P49 only for P46=7 (both). P48=2 is an IRA rollover; P48=3 is left to accumulate. The U2 inherited convention takes only disposition 3. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 165; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2015.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309` | -| `pension:2015.route.formula_unknown_checkpoint` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5b the 2015 route admits the documentary intersection: P46=5 (DC only) reaches P64/P65, and P46=7 (combined) reaches P48/P49 (pension:2015.route.previous_combined). The disputed P62ACKPT branch, whose heading reads 'P46=1 AND P52=1 AND P62AMT= DK/RF' while its branch label reads only 'P62AMT =DK/RF' (q2015 p. 164), is refused pending PSID clarification: an amount reached only through it is off-route under the named disposition off_route (section 15 employer_dc.excluded), never zero-filled. Formula (1), combined (7), DK (8) and NA or refused (9) plan types are off-route for P64/P65. refused_branch records amendment 5's section 15 fragment entry refused_branches for 2015. The adjudication's question and the route's refusal are kept as superseded_question and superseded_refusal. | `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 159; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692; `EV/phase2-20260927/out/u2-adjudicate.md:123` | +| `pension:2015.route.formula_unknown_checkpoint` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5b the 2015 route admits the documentary intersection: P46=5 (DC only) reaches P64/P65, and P46=7 (combined) reaches P48/P49 (pension:2015.route.previous_combined). The disputed P62ACKPT branch, whose heading reads 'P46=1 AND P52=1 AND P62AMT= DK/RF' while its branch label reads only 'P62AMT =DK/RF' (q2015 p. 164), is refused pending PSID clarification: an amount reached only through it is off-route under the named disposition off_route (section 15 employer_dc.excluded), never zero-filled. Formula (1), combined (7), DK (8) and NA or refused (9) plan types are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2015.route.duplicate_and_off_route) and the others' under off_route. refused_branch records amendment 5's section 15 fragment entry refused_branches for 2015. The adjudication's question and the route's refusal are kept as superseded_question and superseded_refusal. | `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 159; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692; `EV/phase2-20260927/out/u2-adjudicate.md:123` | | `pension:2015.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 164; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2015.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2015.pdf` p. 154; `PSID/documentation/capture1/q2015.pdf` p. 159; `PSID/documentation/capture1/q2015.pdf` p. 165; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 647; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 648; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 665; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 692; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 701; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 730; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 731; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 748; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 765; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 766; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 793; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 802; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 831 | @@ -1471,7 +1471,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `pension:2017.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 153; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2017.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2017 p. 152 P62ACKPT gives '1. DB Only (P46=1)', '3. DC Only (P46=5)', '5. All Others (P46=7 or DK/RF)'; DB-only and all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2017 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2017.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:125` | +| `pension:2017.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2017 p. 152 P62ACKPT gives '1. DB Only (P46=1)', '3. DC Only (P46=5)', '5. All Others (P46=7 or DK/RF)'; DB-only and all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2017 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2017.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2017.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:125` | | `pension:2017.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 146; `PSID/documentation/capture1/q2017.pdf` p. 152; `PSID/documentation/capture1/q2017.pdf` p. 152; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2017.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2017.pdf` p. 141; `PSID/documentation/capture1/q2017.pdf` p. 147; `PSID/documentation/capture1/q2017.pdf` p. 153; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 667; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 668; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 685; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 712; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 721; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 750; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 767; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 768; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 785; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 812; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 821; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 822; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 851 | @@ -1575,7 +1575,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `pension:2019.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2019.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2019 p. 163 P62ACKPT gives the same three P62ACKPT branches, followed by 'Plan with Former Employer - Defined Contribution (P46=DC Only)'. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2019 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2019.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:126` | +| `pension:2019.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2019 p. 163 P62ACKPT gives the same three P62ACKPT branches, followed by 'Plan with Former Employer - Defined Contribution (P46=DC Only)'. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2019 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2019.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2019.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:126` | | `pension:2019.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 157; `PSID/documentation/capture1/q2019.pdf` p. 163; `PSID/documentation/capture1/q2019.pdf` p. 163; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2019.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2019.pdf` p. 153; `PSID/documentation/capture1/q2019.pdf` p. 158; `PSID/documentation/capture1/q2019.pdf` p. 164; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2019/fam2019er_codebook.pdf` p. 677; `PSID/family/2019/fam2019er_codebook.pdf` p. 692; `PSID/family/2019/fam2019er_codebook.pdf` p. 718; `PSID/family/2019/fam2019er_codebook.pdf` p. 727; `PSID/family/2019/fam2019er_codebook.pdf` p. 755; `PSID/family/2019/fam2019er_codebook.pdf` p. 770; `PSID/family/2019/fam2019er_codebook.pdf` p. 786; `PSID/family/2019/fam2019er_codebook.pdf` p. 812; `PSID/family/2019/fam2019er_codebook.pdf` p. 821; `PSID/family/2019/fam2019er_codebook.pdf` p. 849 | @@ -1679,7 +1679,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `pension:2021.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 304; `PSID/documentation/capture1/q2021.pdf` p. 305; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2021.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2021 p. 304 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2021 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2021.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:127` | +| `pension:2021.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2021 p. 304 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2021 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2021.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2021.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2021.pdf` p. 304; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:127` | | `pension:2021.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2021.pdf` p. 294; `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 304; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2021.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2021.pdf` p. 287; `PSID/documentation/capture1/q2021.pdf` p. 295; `PSID/documentation/capture1/q2021.pdf` p. 305; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 724; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 738; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 739; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 765; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 774; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 802; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 817; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 832; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 833; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 859; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 868; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 896 | @@ -1784,7 +1784,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `pension:2023.route.previous_dc_only` | RESOLVED | P46=5 routes the two previous-employer plan slots through P62ACKPT to P64/P65. P64=3 means left to accumulate and is the counted disposition. P64=2 is an excluded IRA rollover. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 302; `PSID/documentation/capture1/q2023.pdf` p. 303; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.checkpoint_documentation` | RESOLVED | Exact P62ACKPT wording is retained below; its routing changes across waves and must not be inferred from U1 code constants. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.formula_unknown_checkpoint` | RESOLVED | P62ACKPT sends formula P46=1 and all others (combined, DK, refused/NA) to P69; only P46=5 reaches P63–P65. This is an explicit documentary change from U1. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173` | -| `pension:2023.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2023 p. 302 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2023 previous-employer account route is the wave-specific route above, replacing section 15's generic account/formula/dk previous route: only P46=5 (DC only) reaches P64/P65 (pension:2023.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65 under the named disposition off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:128` | +| `pension:2023.route.inherited_route_amendment` | RESOLVED | Independent adjudication D (amendment 5): q2023 p. 302 P62ACKPT gives '3. DC Only (P46=5)' versus '5. All Others (P46=7, DK, RF, NA)'; all others go to P69. Only P46=5 (DC only) reaches P63-P65; formula, combined, DK, refused and NA plans are off-route for P64/P65. Combined-plan P48/P49 is counted once, disposition 3 is retained and IRA-rollover disposition 2 is excluded. Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 5 as written. Under 5a the 2023 previous-employer account route is the wave-specific route above, which supersedes section 15's generic account/formula/dk previous route (section 16b; section 15's own text is unchanged until section 20 step 5 materializes the final parameter block): only P46=5 (DC only) reaches P64/P65 (pension:2023.route.formula_unknown_checkpoint, accepted plan type 5); formula, combined, DK, refused and NA plans are off-route for P64/P65, each under a named disposition: a combined plan's re-asked P64/P65 items under both_plan_account_items_reasked (the duplicate guard, pension:2023.route.duplicate_and_off_route) and the others' under off_route; combined-plan P48/P49 is counted once, disposition 3 is counted and IRA-rollover disposition 2 is excluded. The application refusal is released with the P64/P65 entries that depended on it, and kept as superseded_refusal. | `PSID/documentation/capture1/q2023.pdf` p. 302; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1173`; `EV/phase2-20260927/out/u2-adjudicate.md:128` | | `pension:2023.route.ira_rollovers` | RESOLVED | P48 and P64 label disposition 2 as rolled over into an IRA and 3 as left to accumulate. Preserve U2 exclusion of disposition 2; this is a registered ownership convention, not a finding from observed account values. | `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.duplicate_and_off_route` | RESOLVED | Use combined-plan P49 at most once; never add combined-plan P65 alongside it. Amounts outside the adjudicated current/previous route remain explicit off-route dispositions. The inherited duplicate guard is preserved even where the later instrument no longer re-asks combined accounts. | `PSID/documentation/capture1/q2023.pdf` p. 292; `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 302; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309` | | `pension:2023.route.amounts_brackets_top_codes` | RESOLVED | P20, P49 and P65 ask balances now and offer follow-up brackets for nonresponse. Exact codebook domains are pinned per variable: current balances NUM(9.0); previous balances NUM(8.0). U2 keeps top codes as recorded, counts DK/refusal as zero with dispositions, and does not use brackets. | `PSID/documentation/capture1/q2023.pdf` p. 285; `PSID/documentation/capture1/q2023.pdf` p. 293; `PSID/documentation/capture1/q2023.pdf` p. 303; `docs/design/boomers2004_1946_55_comparison.md:309`; `docs/design/boomers2004_1946_55_comparison.md:1185`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 711; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 725; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 726; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 752; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 761; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 789; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 804; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 819; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 820; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 846; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 855; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 883 | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index ebd259480..b4e92986f 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -404,6 +404,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Wording: `income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json` and `weights.json` (each `independent_adjudication` note; the wealth `status_scope`) and `u1_identity.json`; the specification's §19; the master record (the milestone-1b D row); `sources.py` (six byte pins); `tests/track_u2/test_u2_mappings.py`; and this section. - First-round review fixes: `income.json` (amendment 2's ten rules and amendment 3's six resolutions) and `u1_identity.json`; the specification's §19; the master record (the spouse age/sex blocker row, the d637 section and 16 rows); `u2_source_registry.py` (the scope checks) and `sources.py` (the slot-metadata guard and the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. The reviews' own reading is in their records. - Amendment-6 review fixes: `weights.json` (one word of the 2017 weight's construction note) and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (amendment 6's exact fields) and `sources.py` (the weights byte pin); `tests/track_u2/test_adjudication_applied.py` and `test_source_registries.py`; and this section. +- Amendment-5 review fixes: `pension.json` (the four 5a `ruling_effect` texts and the 2015 checkpoint's resolution) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row and five rows); `u2_source_registry.py` (the governed routes), `sources.py` (the pension byte pin) and `invented.py` (the 2019–2023 DC items and comments); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py` and `test_u2_employer_dc.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 9e22f0f0e..831d51bf2 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -87,8 +87,10 @@ #: they record, the adjudication disposition they keep, the gate fields #: it fixes (``fields``, exact values), the fields it removes #: (``absent``), the fields that must hold its text (``required``, -#: nonempty) and the fixed values that only its entries may carry -#: (``markers``). An entry that records a d637 ruling must be in its +#: nonempty), the fixed values that only its entries may carry +#: (``markers``) and, for amendment 5, the route entries the builder +#: reads that it governs (``governs``: exact fields, and fields that must +#: be absent). An entry that records a d637 ruling must be in its #: amendment's scope and hold those values, and every entry in a scope #: must record the ruling. Each registry commit adds its amendment here #: (section 16a, "Registry effect of the rulings"). @@ -196,6 +198,10 @@ "superseded_refusal", ), "markers": ("refused_branch", "off_route_disposition"), + "governs": { + "2015.route.previous_combined": ({"accepted_plan_types": [7]}, ()), + "2015.route.previous_dc_only": ({"accepted_plan_types": [5]}, ()), + }, }, "5a": { "registry": "pension", @@ -218,6 +224,29 @@ "superseded_refusal", ), "markers": (), + # The route dc_route reads for each wave: the DC-only checkpoint, + # the combined plan's items and the DC-only plan's (5a). + "governs": { + **{ + f"{wave}.route.formula_unknown_checkpoint": ( + { + "status": "RESOLVED", + "accepted_plan_types": [5], + "rejected_plan_types": [1, 7, 8, 9], + }, + ("accepted_conditional_types",), + ) + for wave in _LATER_WAVES[1:] + }, + **{ + f"{wave}.route.{route}": ({"accepted_plan_types": types}, ()) + for wave in _LATER_WAVES[1:] + for route, types in ( + ("previous_combined", [7]), + ("previous_dc_only", [5]), + ) + }, + }, }, "6": { "registry": "weights", @@ -854,6 +883,26 @@ def validate_registry(document: dict[str, Any], *, expected_name: str) -> None: raise SourceAdjudicationError( f"{name}:{key} lacks its d637 amendment {amendment} ruling" ) + # The route entries an amendment governs hold its ruled values. + for amendment, scope in RATIFIED_AMENDMENTS.items(): + if scope["registry"] != expected_name: + continue + by_id = {entry["id"]: entry for entry in entries} + for key, (fixed, absent) in scope.get("governs", {}).items(): + entry = by_id.get(key) + if entry is None: + raise SourceAdjudicationError( + f"Missing entry {key} that d637 amendment {amendment} " + "governs" + ) + if any( + field not in entry or not _same(entry[field], value) + for field, value in fixed.items() + ) or any(field in entry for field in absent): + raise SourceAdjudicationError( + f"{expected_name}:{key} is not the route d637 amendment " + f"{amendment} ratified" + ) # A value only an amendment's entries may carry stays inside its scope. for amendment, scope in RATIFIED_AMENDMENTS.items(): if scope["registry"] != expected_name: diff --git a/src/populace_dynamics/uniform_cut_track_u2/invented.py b/src/populace_dynamics/uniform_cut_track_u2/invented.py index 6ba18a73a..0d26900df 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/invented.py +++ b/src/populace_dynamics/uniform_cut_track_u2/invented.py @@ -46,8 +46,10 @@ * **employer DC** (row U7): a current-job account, a combined plan whose current type counts, a DK amount, a combined previous plan left to accumulate (and its re-asked account items, a duplicate), account, - formula and DK plans on the declared account route, an IRA rollover - and an off-route amount; + formula and DK plans on the declared account route in 2013 (formula + and DK plans off-route from 2015, amendment 5), a DK amount on an + account plan's route and one on an off-route plan from 2019, an IRA + rollover and an off-route amount; * **design** (section 10): strata including the 2017/2019 refresher strata 88-94 and a singleton stratum. @@ -1070,8 +1072,13 @@ def _dc_raw(family: Mapping[str, Any], wave: int) -> dict[str, int]: # An off-route amount: a formula-only current plan. row["wife_current_type"] = 1 row["wife_current_amount"] = 4_000 - # A DK amount on the route (counted as zero, unreported). + # A DK amount on the DK-type plan, off-route from 2015 + # (amendment 5): counted off-route, never an unreported zero. row["wife_prev1_dc_amount"] = dk8 + # A DK amount on an account plan's route (counted as zero, + # unreported). + row["head_prev2_type"] = 5 + row["head_prev2_dc_amount"] = dk8 return row diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index ede02025d..666a4f756 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -247,7 +247,8 @@ #: refused. The first-round review fixes moved the income bytes again #: (amendment 2's rule text and amendment 3's resolution), and the #: second-round fixes the weights bytes (one word of the 2017 weight's -#: construction note). +#: construction note) and the pension bytes (amendment 5's texts name the +#: duplicate guard for a combined plan's re-asked items). REGISTRY_SHA256: dict[str, str] = { "income": ( "aea295524e689e3464147669c72b64c474208ceefe1efe0d33bcb0381e22dca3" @@ -259,7 +260,7 @@ "1cb368486d689e20e116b775ee2198504b0492fb3c7efa4d6fe7e985a2b1307c" ), "pension": ( - "241aeaddb7e8b134ac5807f944053c4be59458d17bcf68c52177da5cab5328a6" + "6b9ae82b0dec99c56860654d6981fce8f71c4c074c95c85ff9448a612fce8351" ), "roles": ( "79feadac7b4847c545e37cee33784873bb0324b9999cfc2693292af6aaacf43d" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index e87a6f52b..c87edc56b 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,370 | -| `artifact` | 4,140 | -| `integration_psid` | 1,395 | +| `unit` | 7,498 | +| `artifact` | 4,145 | +| `integration_psid` | 1,396 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,733** | +| **Total** | **13,867** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index c8720f34f..6ed85edc7 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,9 +1,9 @@ { "schema_version": 1, "counts": { - "unit": 7370, - "artifact": 4140, - "integration_psid": 1395, + "unit": 7498, + "artifact": 4145, + "integration_psid": 1396, "reproduction_legacy": 520, "oracle_policyengine": 308 } diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 1bf0f5dcb..fb6731dea 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -635,8 +635,8 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): "2": ("64a67aae69d9d0bd6c6743328e8169304708003845395830ad628a6ee708887d"), "3": ("8d93d501819d981c6605b02fde3ff89d05488738146757bd179a84c02e131598"), "4": ("0b79677a5d1b41a76b17709152bec0d3789d118c75859b9716ea505c3413cd9c"), - "5b": ("7b16f9fdaa76d42d0d9c64dff3b7607a22e476378d35f3a9b1360d94f3c18552"), - "5a": ("1ce1d48c5661374960454e2c2d82a19fbc6a33abb432a50a0df40c773d8b8e76"), + "5b": ("ca59c757b3b2b3f9402643c51ae910c16f9dd5927848d03fed327172cb38b513"), + "5a": ("7f2b79ac154d570a4f246f7f985f4aaef47ba40c3506dc0b12f5707ba69da24e"), "6": ("ad76188063ca16ddcdcc2d8e2165348293a4c88e9437e8e173765df91ffc0d2f"), } #: The operative clauses each amendment's entries must state, by field @@ -674,12 +674,32 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): ("resolution", "is refused pending PSID clarification"), ("resolution", "off-route under the named disposition off_route"), ("resolution", "never zero-filled"), + ( + "resolution", + "a combined plan's re-asked P64/P65 items under " + "both_plan_account_items_reasked", + ), ("condition", "DC-only plan (P46=5)"), ], "5a": [ ("ruling_effect", "Under 5a the {wave} previous-employer account"), ("ruling_effect", "only P46=5 (DC only) reaches P64/P65"), - ("ruling_effect", "under the named disposition off_route"), + ( + "ruling_effect", + "which supersedes section 15's generic account/formula/dk " + "previous route", + ), + ( + "ruling_effect", + "section 15's own text is unchanged until section 20 step 5", + ), + ( + "ruling_effect", + "a combined plan's re-asked P64/P65 items under " + "both_plan_account_items_reasked (the duplicate guard, " + "pension:{wave}.route.duplicate_and_off_route) and the others' " + "under off_route", + ), ("ruling_effect", "combined-plan P48/P49 is counted once"), ("ruling_effect", "IRA-rollover disposition 2 is excluded"), ], @@ -774,6 +794,13 @@ def test_d637_texts_are_pinned(documents): "only P46=5 (DC only)", "P46=1 and P46=5", ), + ( + "pension", + "2019.route.inherited_route_amendment", + "ruling_effect", + "both_plan_account_items_reasked", + "off_route", + ), ( "weights", "2017.cross_section_weight", @@ -1352,7 +1379,8 @@ def test_section_16a_recommendations_and_options(): "nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT " "checkpoint under 5b, released the 2017\N{EN DASH}2023 P64/P65 " "records under 5a and resolved the 2017 weight ER34651 under " - "amendment 6's option 1. The 6 TO VERIFY records left are code " + "amendment 6's option 1; the \N{SECTION SIGN}15 fragment itself is " + "materialized under item 9. The 6 TO VERIFY records left are code " "88's, open until Max confirms the 1a\N{EN DASH}1c reading (d745).", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 624897e99..f80ff78f0 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -966,6 +966,38 @@ def _copy_ruling(document, source, target): + " Recalibrate 2017 to ACS." ), ), + # Second-round review (of the amendment-5 commits): the routes + # dc_route reads are the ruled ones. + "a5a_checkpoint_widened": ( + "pension", + lambda d: _entry(d, "2021.route.formula_unknown_checkpoint").update( + accepted_conditional_types=[1, 8] + ), + ), + "a5a_checkpoint_admits_formula": ( + "pension", + lambda d: _entry(d, "2017.route.formula_unknown_checkpoint").update( + accepted_plan_types=[5, 1] + ), + ), + "a5a_combined_widened": ( + "pension", + lambda d: _entry(d, "2019.route.previous_combined").update( + accepted_plan_types=[7, 1] + ), + ), + "a5a_dc_only_widened": ( + "pension", + lambda d: _entry(d, "2023.route.previous_dc_only").update( + accepted_plan_types=[5, 8] + ), + ), + "a5b_combined_widened": ( + "pension", + lambda d: _entry(d, "2015.route.previous_combined").update( + accepted_plan_types=[7, 1] + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_employer_dc.py b/tests/track_u2/test_u2_employer_dc.py index e6f97cc30..70a7611c7 100644 --- a/tests/track_u2/test_u2_employer_dc.py +++ b/tests/track_u2/test_u2_employer_dc.py @@ -103,6 +103,30 @@ def test_declared_account_items_route(wave, plan_type): assert out["employer_dc_off_route_items"] == int(not counted) +@pytest.mark.parametrize("wave", [2013, 2019]) +@pytest.mark.parametrize("plan_type", [5, 1, 8, 9]) +@pytest.mark.parametrize( + "amount", [15_000, 10**8 - 2, 10**8 - 1], ids=["15000", "dk", "na"] +) +def test_an_off_route_amount_is_never_zero_filled(wave, plan_type, amount): + """Amendment 5 (ratified by d637): an off-route plan's P65 amount, a + DK or NA sentinel included, is counted off-route and never as an + unreported zero; an on-route sentinel is the unreported zero (review + of the amendment-5 commits, finding 5).""" + out = _balance( + _declared(wave), + wife_prev2_type=plan_type, + wife_prev2_dc_disposition=3, + wife_prev2_dc_amount=amount, + ) + on_route = plan_type == 5 or (wave == 2013 and plan_type in (1, 8)) + sentinel = amount != 15_000 + assert out["employer_dc_off_route_items"] == int(not on_route) + assert out["employer_dc_unreported"] == int(on_route and sentinel) + assert out["employer_dc_items"] == int(on_route and not sentinel) + assert out["employer_dc"] == (amount if on_route and not sentinel else 0) + + def _section_16a_fragment() -> dict: """Amendment 5's ``previous_routes`` fragment as section 16a prints it.""" import json @@ -246,25 +270,57 @@ def test_2013_registry_route_equals_the_declared_route_and_u1_rule( REGISTRY_DC_WAVES = sources.SUPPORT_WAVES -@pytest.mark.parametrize("wave", [2015, 2017, 2019, 2021, 2023]) -def test_a_blocked_later_route_refuses_under_the_registry_gate( - wave, committed_registries, monkeypatch +#: Every route entry ``dc_route`` requires, by wave. +_DC_ROUTE_ENTRIES = [ + (wave, route) + for wave in sources.SUPPORT_WAVES + for route in ( + "current_job", + "previous_combined", + "previous_dc_only", + "formula_unknown_checkpoint", + "ira_rollovers", + "duplicate_and_off_route", + "amounts_brackets_top_codes", + "respondent_slots", + *(("inherited_route_amendment",) if wave >= 2017 else ()), + ) +] + + +@pytest.mark.parametrize( + "block, message", + [ + ({"blocking_dependencies": ["pension:example"]}, "blocked by"), + ({"action": "refuse_example"}, "refuse_example"), + ], + ids=["blocked", "refusing"], +) +@pytest.mark.parametrize( + "wave, route", + _DC_ROUTE_ENTRIES, + ids=[f"{w}.{r}" for w, r in _DC_ROUTE_ENTRIES], +) +def test_a_blocked_route_refuses_under_the_registry_gate( + wave, route, block, message, committed_registries, monkeypatch ): - """The registry gate still refuses a route entry that is blocked, as - each later wave's was before the d637 5b and 5a commits; the entry is + """The registry gate refuses the wave's route if any entry it requires + is blocked or refusing, as 2015's checkpoint and 2017-2023's + inherited-route amendments were before the d637 5b and 5a commits + (review of the amendment-5 commits, finding 3); each entry is re-blocked in memory only.""" original = sources.RegistrySet.entry - key = f"{wave}.route.previous_dc_only" + key = f"{wave}.route.{route}" def entry(self, name, entry_id): found = original(self, name, entry_id) if (name, entry_id) == ("pension", key): - return {**found, "blocking_dependencies": ["pension:example"]} + return {**found, **block} return found monkeypatch.setattr(sources.RegistrySet, "entry", entry) gate = sources.SourceGate(sources.REGISTRY, committed_registries) - with pytest.raises(sources.U2SourceRefusal, match="blocked by"): + with pytest.raises(sources.U2SourceRefusal, match=message): sources.dc_route(wave, gate) diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index dccda4152..5a5f11f48 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -729,7 +729,9 @@ def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): #: The waves whose family-record specs (income, wealth and employer-DC -#: items) the registry gate applies: every support wave. Until the d1090 +#: items) the registry gate applies: every support wave; a re-blocked +#: entry still refuses (``test_registry_gate_refuses_a_reblocked_family_ +#: record``). Until the d1090 #: registry commit (section 16c), 2015's income entries depended on the #: refused 2015 code-20 route and 2019-2023's on the refused code-90/92 #: routes; until the d637 amendment-2 commit (section 16b), every later @@ -737,23 +739,42 @@ def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): #: and 5a commits, the P64/P65 items waited on 2015's P62ACKPT checkpoint #: and on 2017-2023's inherited-route amendments. OPEN_WAVES_RESOLVED = sources.SUPPORT_WAVES -FIRST_BLOCKER: dict[int, str] = {} -def test_registry_gate_refuses_every_open_wave(registry_gate): +def test_registry_gate_applies_every_support_wave(registry_gate): # 2013: every mapping U2 reads is resolved (the 2013 meanings are # U1's); 2015-2023 since the d637 commits for amendments 2, 5b and - # 5a. A wave still open would meet its first blocked entry. + # 5a. for wave in OPEN_WAVES_RESOLVED: specs = loader.family_record_specs(wave, registry_gate) concepts = {spec.concept for spec in specs} assert set(sources.INCOME_CONCEPTS) <= concepts assert {"wealth1", "vehicles", "head_current_amount"} <= concepts assert all(spec.gate == sources.REGISTRY for spec in specs) - for wave, blocker in FIRST_BLOCKER.items(): - with pytest.raises(sources.U2SourceRefusal) as error: - loader.family_record_specs(wave, registry_gate) - assert blocker in str(error.value), (wave, str(error.value)[:200]) + + +@pytest.mark.parametrize("wave", (2015, 2017, 2019, 2021, 2023)) +def test_registry_gate_refuses_a_reblocked_family_record( + wave, committed_registries, monkeypatch +): + """A later wave meets the blocker of any family-record entry, as each + did before the d637 commits; the first P64 item is re-blocked in + memory only (review of the amendment-5 commits, finding 3).""" + original = sources.RegistrySet.entry + key = f"{wave}.head_prev1_dc_disposition" + blocker = f"pension:{wave}.route.inherited_route_amendment" + + def entry(self, name, entry_id): + found = original(self, name, entry_id) + if (name, entry_id) == ("pension", key): + return {**found, "blocking_dependencies": [blocker]} + return found + + monkeypatch.setattr(sources.RegistrySet, "entry", entry) + gate = sources.SourceGate(sources.REGISTRY, committed_registries) + with pytest.raises(sources.U2SourceRefusal) as error: + loader.family_record_specs(wave, gate) + assert f"pension:{key}: blocked by {blocker}" in str(error.value) @pytest.mark.parametrize("wave", sources.SUPPORT_WAVES) @@ -844,9 +865,26 @@ def spy(event, args): def test_source_preflight_lists_the_open_entries(committed_registries): with pytest.raises(loader.U2LoaderRefusal) as error: loader.source_preflight(committed_registries) - assert "weights:2017.cross_section_weight" in str(error.value) or ( - "registry blockers" in str(error.value) + # Since the d637 registry commits the only blockers are code 88's + # (amendment 1c, pending d745): TO VERIFY and its refusal, each wave. + assert str(error.value).startswith("12 registry blockers refuse") + # The preflight's own rule: an absent code (code 92 in 2013 and 2015) + # passes when it is RESOLVED; every other entry counts its blockers. + refused = [] + for name, entry_id in loader.required_entries(committed_registries): + entry = committed_registries.entry(name, entry_id) + if name == "roles" and entry.get("present") is False: + assert entry["status"] == "RESOLVED", entry_id + continue + refused += sources.blockers(entry, name) + assert len(refused) == 12 + assert sorted({reason.split(": ")[0] for reason in refused}) == sorted( + f"roles:{wave}.relationship.88" for wave in sources.SUPPORT_WAVES ) + for wave in sources.SUPPORT_WAVES: + key = f"roles:{wave}.relationship.88" + assert f"{key}: refuse_substantive_routing" in refused + assert any(r.startswith(f"{key}: TO VERIFY: ") for r in refused) entries = loader.required_entries(committed_registries) assert ("weights", "2017.cross_section_weight") in entries assert ("roles", "2019.relationship.92") in entries From 1f05dd05b480962fbff4eaca9de0a5f142d835cc Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 05:08:55 -0400 Subject: [PATCH 12/21] Answer the delta review of the d637 answers and wording Delta review of 190d4aef, 1d23ed37, 55687071 and f32d8d0a (GPT-6.1 Sol; Subfleet job 20261011-230633-d637-review-r2; REQUEST CHANGES, three should-fix findings and five nits). It found every earlier finding resolved or reasonably declined. - The six adjudication notes said "rulings_applied records what each ruling changed here", but the support registry has none. They now say "a registry's rulings_applied, where present, records what each ruling changed in it". The individual registry, whose 2017 weight amendment 6 released, gains a d637 record, as d1090 recorded itself where it only released dependencies (finding 1). - The note test requires each note to equal the adjudication's own sentences plus the new ending exactly; an inserted sentence fails (finding 2). - The wording test re-pins each amendment's hash to the mutated text and still fails on an operative clause, so the clauses are tested on their own; the superseded-question case, which no clause covers, is left to the registry differential, which compares it in full; the dropped disclosure item is a separate test on a fresh copy (finding 3). - Nits: two sources docstrings name the roster's spouse-slot occupant (the role rule with spouse_slot), not a closed list of codes; the 2015 re-block uses the 2015 checkpoint, its real blocker; the static guard covers the U2 scripts too, except the capture scripts, which build the documentary registries, and allows only the dry run's count of invented DK ages; a malformed source_wording_conflict refuses with SourceAdjudicationError; a stale test docstring is corrected (findings 4-8). - sources: seven byte pins; section 19 records the round; the spec is re-pinned. The loader's source preflight still refuses on 12 blockers, all code 88's. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 2 +- data/external/track_u2/individual.json | 11 +++++ data/external/track_u2/pension.json | 2 +- data/external/track_u2/roles.json | 2 +- data/external/track_u2/support.json | 2 +- data/external/track_u2/u1_identity.json | 2 +- data/external/track_u2/wealth.json | 2 +- data/external/track_u2/weights.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 2 + .../uniform_cut_track_u2/sources.py | 26 ++++++----- tests/track_u2/test_adjudication_applied.py | 37 +++++++++------ tests/track_u2/test_u2_mappings.py | 46 ++++++++++++++++--- 14 files changed, 101 insertions(+), 39 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index b2bd559b8..49851b436 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -28015,7 +28015,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/individual.json b/data/external/track_u2/individual.json index 33af1aabd..73bf6d37a 100644 --- a/data/external/track_u2/individual.json +++ b/data/external/track_u2/individual.json @@ -4941,5 +4941,16 @@ } ] } + ], + "rulings_applied": [ + { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendments": [ + "6" + ], + "note": "Max's ruling d637 (2026-09-30; specification section 16b) took amendment 6's option 1 for the 2017 cross-sectional weight (weights:2017.cross_section_weight, ER34651). Its registry commit released this registry's 2017.weight from its dependency on that record; no entry here records a ruling." + } ] } diff --git a/data/external/track_u2/pension.json b/data/external/track_u2/pension.json index 5cf5537a5..54ab4f495 100644 --- a/data/external/track_u2/pension.json +++ b/data/external/track_u2/pension.json @@ -26536,7 +26536,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/roles.json b/data/external/track_u2/roles.json index 55ca89398..40c114782 100644 --- a/data/external/track_u2/roles.json +++ b/data/external/track_u2/roles.json @@ -1882,7 +1882,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/support.json b/data/external/track_u2/support.json index 04dd0bedf..6ec342548 100644 --- a/data/external/track_u2/support.json +++ b/data/external/track_u2/support.json @@ -966,6 +966,6 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." } } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 824a088c7..129fc6e58 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "3656e1db9aa1b882febd3bdcc9219de9222922e8b417eeb8a77d4474c78347ae", + "sha256": "478605152fab1b029efc602ebc2d8b414ec530ef2306d88e1cce7ce841f36bcd", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/data/external/track_u2/wealth.json b/data/external/track_u2/wealth.json index 66209a5c0..cbab8aea4 100644 --- a/data/external/track_u2/wealth.json +++ b/data/external/track_u2/wealth.json @@ -9063,7 +9063,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/data/external/track_u2/weights.json b/data/external/track_u2/weights.json index 6055f1c2d..c28323496 100644 --- a/data/external/track_u2/weights.json +++ b/data/external/track_u2/weights.json @@ -553,7 +553,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); rulings_applied records what each ruling changed here. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." }, "rulings_applied": [ { diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 64d9fab6d..340c34cbf 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1848,9 +1848,10 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65; formula, combined, DK, refused and NA plans are off-route for P64/P65, a combined plan's re-asked items under the duplicate guard (`both_plan_account_items_reasked`) and the others' under `off_route`; and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). - Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. - Wording: the six registries that record the independent adjudication no longer say its A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; each note records the rulings and that the six code-88 records wait for d745. The wealth registry's scope no longer says role assignment is "refused" by the role registry, which d1090 left stale in that registry. The master record's account of milestone 1b's D resolutions now says the 2017–2023 route's application was released under 5a. No gate field, count or blocker moves. -- After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. +- After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept, and out of the U2 scripts other than the registry capture scripts, except the dry run's count of invented DK ages. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. - After the review of the amendment-6 commit (GPT-6.1 Sol, Subfleet job `20261011-224548-d637-review-a6`, REQUEST CHANGES; same folder): the validator holds the 2017 weight's selection, purpose and `registration_disclosure` exactly, so the disclosure registration must state cannot drift, and five mutations that empty, overstate or drop part of it, or exclude or recalibrate the sample, each fail. Its construction note now says the open points "remain undocumented", not "unresolved", on a RESOLVED record. - After the review of the amendment-5 commits (Opus 5.5 on a review lane, Subfleet job `20261011-224548-d637-review-a5`; REQUEST CHANGES, with the §15 decision judged "the only reading that fits the record"): amendment 5's texts now name both dispositions, `off_route` for formula, DK, refused and NA plans and the duplicate guard for a combined plan's re-asked P64/P65 items, as the builder counts them, and say the 5a route supersedes §15's generic route while §15's text waits for §20 step 5. The validator pins the route entries the builder reads for 2015–2023 (the DC-only checkpoint, combined-plan and DC-only plan types). The invented population has, from 2019, a DK amount on an account plan's route as well as one on an off-route plan, with its comments corrected. Tests re-block, in memory, every route entry the employer-DC route requires and a P64 item of each later wave, check that an off-route DK or NA amount is never an unreported zero, and pin the preflight's twelve code-88 blockers exactly. +- After the delta review of these answers and the wording commit (GPT-6.1 Sol, Subfleet job `20261011-230633-d637-review-r2`; REQUEST CHANGES, three should-fix findings, every earlier finding resolved or reasonably declined): the six adjudication notes say a registry's `rulings_applied` records a ruling "where present", since the support registry has none, and the individual registry records amendment 6's release of its 2017 weight; the note test requires each note to be exactly the adjudication's sentences plus the new ending; and the wording test, with each amendment's hash re-pinned to a mutated text, still fails on an operative clause, so the clauses are tested on their own. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index b4e92986f..adb7e1f57 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -405,6 +405,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - First-round review fixes: `income.json` (amendment 2's ten rules and amendment 3's six resolutions) and `u1_identity.json`; the specification's §19; the master record (the spouse age/sex blocker row, the d637 section and 16 rows); `u2_source_registry.py` (the scope checks) and `sources.py` (the slot-metadata guard and the income byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. The reviews' own reading is in their records. - Amendment-6 review fixes: `weights.json` (one word of the 2017 weight's construction note) and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (amendment 6's exact fields) and `sources.py` (the weights byte pin); `tests/track_u2/test_adjudication_applied.py` and `test_source_registries.py`; and this section. - Amendment-5 review fixes: `pension.json` (the four 5a `ruling_effect` texts and the 2015 checkpoint's resolution) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row and five rows); `u2_source_registry.py` (the governed routes), `sources.py` (the pension byte pin) and `invented.py` (the 2019–2023 DC items and comments); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py` and `test_u2_employer_dc.py`; and this section. +- Delta-review fixes: the six adjudication notes and a d637 record in `individual.json`, and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (a type check), `sources.py` (seven byte pins and two docstrings); `tests/track_u2/test_adjudication_applied.py` and `test_u2_mappings.py`; and this section. The amendment-5 answers also changed `tests/track_u2/test_u2_scripts.py`'s variant expectations. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 831d51bf2..6a9b66881 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -461,6 +461,8 @@ def _ratified_amendment(entry: dict[str, Any], name: str) -> None: f"{scope['disposition']} for {entry['id']}" ) target = scope.get("targets", {}).get(entry["id"]) + if "targets" in scope: + _fields(entry, {"source_wording_conflict": dict}) if "targets" in scope and not ( entry.get("accuracy_for_variable") == target and entry["source_wording_conflict"].get( diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 666a4f756..950085a4d 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -248,28 +248,31 @@ #: (amendment 2's rule text and amendment 3's resolution), and the #: second-round fixes the weights bytes (one word of the 2017 weight's #: construction note) and the pension bytes (amendment 5's texts name the -#: duplicate guard for a combined plan's re-asked items). +#: duplicate guard for a combined plan's re-asked items). The third +#: round moved seven registries' bytes: the six adjudication notes say +#: ``rulings_applied`` records a ruling "where present", and the +#: individual registry records amendment 6's release. REGISTRY_SHA256: dict[str, str] = { "income": ( - "aea295524e689e3464147669c72b64c474208ceefe1efe0d33bcb0381e22dca3" + "ddf49639c5678c35d525438b595393d7c55cba00956c23e42f68515ac56446d5" ), "wealth": ( - "cebf9bfad6a07d2a0e7ec8c627f30ed10641512c479bc482b91998b9e21294e8" + "4fa58c6621738f071fbbc3e0e4ec3b2f6f0db9bcb6a942b43684137aa829a716" ), "individual": ( - "1cb368486d689e20e116b775ee2198504b0492fb3c7efa4d6fe7e985a2b1307c" + "283c65c46ccfbc6b3a5b700ec7d0d1783e61c931352455b842828cc0186cedda" ), "pension": ( - "6b9ae82b0dec99c56860654d6981fce8f71c4c074c95c85ff9448a612fce8351" + "bd22e07a8576ea9239818ca40377839c5c3151fffb403f3e5f1ef1549da546cf" ), "roles": ( - "79feadac7b4847c545e37cee33784873bb0324b9999cfc2693292af6aaacf43d" + "b7521b6b9919e4ca7369c110894377c02ee8ef9e5f51e4637332248eed35290b" ), "support": ( - "419341b90e0adae2c8a8ee1c2324d405bbd5e0a49faa54a00c19025fc6fd57c9" + "533c39187214fef6626e565362b2d97ff393b8d1f0034ffe365a7c87dadada6d" ), "weights": ( - "456666033dc02e79c9603a7cbe3dfd7b879eaea92e8398c508923aaf73c68221" + "afefd67942e57076775d262849a3edbd292b1158a1a741c0b3811153d87ae738" ), "design": ( "56181372d412811af6d06f5c0d8d1e071049f7fb73c34bd394aaff8f5e0c2fd6" @@ -280,8 +283,8 @@ #: over the ten rows; section 4). ``wife_age`` gives the family file's #: spouse-slot flag (not 0: "no Spouse/Partner in FU"), which rows U3 (the #: couple convention for new enrollment) and U4 (the unit size) read once -#: :func:`.cohort.income_rows` has held it equal to the roster's code-20 -#: or code-22 occupant. The spouse age and sex are slot metadata only +#: :func:`.cohort.income_rows` has held it equal to the roster's unique +#: spouse-slot occupant (the person whose role rule has ``spouse_slot``). The spouse age and sex are slot metadata only #: (amendment 2, ratified by d637; section 16b): they never decide slot #: occupancy, and a disagreement refuses. INCOME_CONCEPTS: tuple[str, ...] = ( @@ -1459,7 +1462,8 @@ def decode_income(frame: pd.DataFrame) -> pd.DataFrame: the family file reports the designated spouse *income slot* occupied (section 3); an age of 999 (DK; NA) still marks it occupied. This is slot metadata only (amendment 2, ratified by d637): occupancy comes - from the roster's code-20 or code-22 person, and + from the roster's unique person whose role rule has ``spouse_slot``, + and :func:`.cohort.income_rows` refuses any observation whose flag disagrees with it. """ diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index fb6731dea..9483495e9 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -552,8 +552,10 @@ def test_d637_rulings_are_recorded_on_their_entries(documents, monkeypatch): def test_d637_amendment_2_spouse_metadata_never_decides_the_slot(documents): """Amendment 2 (section 16a; ratified by d637): the later family-file spouse age and sex are slot metadata only. Each record quotes its own - inapplicability wording as wording that decides no slot, and names the - relationship code, recorded sex and derived age as what decides.""" + inapplicability wording as wording that decides no slot, defers slot + occupancy to the roles registry (code 88's left open, pending d745), + and names the recorded sex and derived age as what age- and + sex-dependent rules use.""" entries = {e["id"]: e for e in documents["income"]["entries"]} for key in registry.RATIFIED_AMENDMENTS["2"]["entries"]: entry = entries[key] @@ -759,13 +761,6 @@ def test_d637_texts_are_pinned(documents): ", and code 88's slot status stays TO VERIFY", "", ), - ( - "income", - "income.2015.wife_sex", - "superseded_question", - "In 2015, how do", - "In 2015, why do", - ), ( "income", "income.2017.wife_retirement_annuities", @@ -817,21 +812,37 @@ def test_d637_texts_are_pinned(documents): ids=[f"{m[1]}:{m[2]}" for m in D637_TEXT_MUTATIONS], ) def test_the_d637_text_checks_catch_each_mutation( - documents, name, key, field, old, new + documents, name, key, field, old, new, monkeypatch ): + """Each mutation fails the hash pin and, with the hash re-pinned to the + mutated text as an editor would, still fails an operative clause + (review of the answers, finding 3). A rewritten superseded question + is caught by the registry differential, which compares it in full.""" changed = {n: copy.deepcopy(d) for n, d in documents.items()} entry = next(e for e in changed[name]["entries"] if e["id"] == key) assert old in entry[field], (key, field) entry[field] = entry[field].replace(old, new, 1) with pytest.raises(AssertionError): check_d637_texts(changed) - # A disclosure item dropped from the 2017 weight fails too. - weights = next( + amendment = entry["ruling"]["amendment"] + raw = json.dumps( + d637_texts(changed, amendment), sort_keys=True, ensure_ascii=False + ) + monkeypatch.setitem( + D637_TEXT_SHA256, amendment, hashlib.sha256(raw.encode()).hexdigest() + ) + with pytest.raises(AssertionError, match=r"\("): + check_d637_texts(changed) + + +def test_a_dropped_disclosure_item_fails_the_text_check(documents): + changed = {n: copy.deepcopy(d) for n, d in documents.items()} + weight = next( e for e in changed["weights"]["entries"] if e["id"] == "2017.cross_section_weight" ) - weights["registration_disclosure"]["not_published"].pop() + weight["registration_disclosure"]["not_published"].pop() with pytest.raises(AssertionError): check_d637_texts(changed) diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 5a5f11f48..789cfaee5 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -348,6 +348,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "income": {"rulings_applied", "independent_adjudication"}, "wealth": {"rulings_applied", "independent_adjudication", "status_scope"}, "pension": {"rulings_applied", "independent_adjudication"}, + "individual": {"rulings_applied"}, "roles": {"independent_adjudication"}, "support": {"independent_adjudication"}, "weights": {"rulings_applied", "independent_adjudication"}, @@ -356,8 +357,9 @@ def test_d1090_moved_only_the_ruled_gate_fields(): D637_ADJUDICATION_NOTE_END = ( "A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's " "ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on " - "2026-10-10 (d1090); rulings_applied records what each ruling changed " - "here. The six code-88 role records (amendment 1c) stay TO VERIFY " + "2026-10-10 (d1090); a registry's rulings_applied, where present, " + "records what each ruling changed " + "in it. The six code-88 role records (amendment 1c) stay TO VERIFY " "pending Max's confirmation of the 1a-1c reading (d745)." ) #: The amendments each registry's ``rulings_applied`` d637 record lists. @@ -366,6 +368,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "wealth": ["4"], "pension": ["5b", "5a"], "weights": ["6"], + "individual": ["6"], } @@ -423,9 +426,10 @@ def test_d637_moved_only_the_ruled_gate_fields(): old_meta = before["independent_adjudication"] new_meta = after["independent_adjudication"] assert _changed_keys(old_meta, new_meta) == {"note"}, name - assert new_meta["note"].endswith(D637_ADJUDICATION_NOTE_END) - assert new_meta["note"].startswith( + # Exactly the adjudication's own sentences, then the new end. + assert new_meta["note"] == ( old_meta["note"].split("A items stay TO VERIFY")[0] + + D637_ADJUDICATION_NOTE_END ), name if "status_scope" in top: assert after["status_scope"] == before["status_scope"].replace( @@ -682,8 +686,11 @@ def test_slot_metadata_is_read_only_as_the_slot_flag( def test_spouse_age_and_sex_are_read_nowhere_else(): """Static guard for amendment 2: no U2 module but the adapters and the - invented generator names the family-file spouse age or sex, and U1's - income concept, which U2's rows feed, names neither.""" + invented generator names the family-file spouse age or sex, no U2 + script does but for the dry run's count of invented DK ages, and U1's + income concept, which U2's rows feed, names neither. The capture + scripts are left out: the income one names both because it builds the + documentary registries from the codebooks, and runs nothing.""" from pathlib import Path from populace_dynamics.estimates import adjusted_poverty @@ -697,6 +704,25 @@ def test_spouse_age_and_sex_are_read_nowhere_else(): assert "wife_age" not in text and "wife_sex" not in text, path.name text = Path(adjusted_poverty.__file__).read_text(encoding="utf-8") assert "wife_age" not in text and "wife_sex" not in text + root = Path(registry.REGISTRY_DIRECTORY).parents[2] + scripts = [ + path + for path in sorted((root / "scripts").glob("*track_u2*.py")) + if not path.name.startswith("capture_") + ] + assert len(scripts) >= 4 + uses = [ + (path.name, line.strip()) + for path in scripts + for line in path.read_text(encoding="utf-8").split("\n") + if "wife_age" in line or "wife_sex" in line + ] + assert uses == [ + ( + "track_u2_dry_run.py", + '"spouse_age_dk_na": int((raw["wife_age"] == 999).sum()),', + ) + ] def test_crosswalks_are_never_executable_inputs(declared_gate, registry_gate): @@ -762,7 +788,13 @@ def test_registry_gate_refuses_a_reblocked_family_record( memory only (review of the amendment-5 commits, finding 3).""" original = sources.RegistrySet.entry key = f"{wave}.head_prev1_dc_disposition" - blocker = f"pension:{wave}.route.inherited_route_amendment" + # The blocker each wave's P64 items had before the d637 commits. + route = ( + "formula_unknown_checkpoint" + if wave == 2015 + else "inherited_route_amendment" + ) + blocker = f"pension:{wave}.route.{route}" def entry(self, name, entry_id): found = original(self, name, entry_id) From 4b43ca238387eb71935af0338025e2f96dfc2934 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 06:06:26 -0400 Subject: [PATCH 13/21] Cover every U2-owned script in the spouse age/sex guard Review of e8352add (GPT-6.1 Sol; Subfleet job 20261012-051305-d637-review-r3; REQUEST CHANGES, one finding and one optional nit; the other seven findings of the delta review RESOLVED). - The static guard scanned scripts/*track_u2*.py only, so the U2-owned scripts/u2_u1_differential.py could read the family-file spouse age or sex unnoticed. It now scans every U2-owned script (the isolation test's scripts/*track_u2* and scripts/u2_*), still leaving out the capture scripts and allowing only the dry run's count of invented DK ages. - Every d637 rulings_applied record has exactly its five keys and the ruling's prefix, and the individual registry's note is pinned exactly. - Section 19 records it; the spec is re-pinned. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 4 +- tests/track_u2/test_u2_mappings.py | 38 ++++++++++++++++--- 3 files changed, 35 insertions(+), 9 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 129fc6e58..3b3387150 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "478605152fab1b029efc602ebc2d8b414ec530ef2306d88e1cce7ce841f36bcd", + "sha256": "f69cff3df5627abceb2d6c80e45699ad3fff0b109c0d7de8aed5f7aca674abb9", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 340c34cbf..784720853 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1848,10 +1848,10 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 5a: the four 2017–2023 `inherited_route_amendment` entries, resolved from documents by the adjudication (D), now apply: only P46 = 5 reaches P64/P65; formula, combined, DK, refused and NA plans are off-route for P64/P65, a combined plan's re-asked items under the duplicate guard (`both_plan_account_items_reasked`) and the others' under `off_route`; and combined-plan P48/P49 is counted once. Each keeps its refusal pending the ruling as `superseded_refusal`, and the 40 P64/P65 entries that depended on them are released. The registry gate now applies every wave's family records and employer-DC route, each route equal to the declared one. The loader's source preflight now refuses on 14 blockers: code 88 (12) and the 2017 weight (2). - Amendment 6: the 2017 cross-sectional weight (`weights:2017.cross_section_weight`) is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134. ER34651 is used as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration. Its `registration_disclosure` lists what registration states: what PSID has documented about the revised weight and what it has not published, as option 1 specifies, and the coverage bound set by the population control's exclusions. PSID's 2026-10-07 answer (research record §3) added nothing on the undocumented points. `individual:2017.weight` is released. The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745). The master record counts 1786 RESOLVED, 0 REFUSED and 6 TO VERIFY records. - Wording: the six registries that record the independent adjudication no longer say its A items "stay TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling"; each note records the rulings and that the six code-88 records wait for d745. The wealth registry's scope no longer says role assignment is "refused" by the role registry, which d1090 left stale in that registry. The master record's account of milestone 1b's D resolutions now says the 2017–2023 route's application was released under 5a. No gate field, count or blocker moves. -- After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept, and out of the U2 scripts other than the registry capture scripts, except the dry run's count of invented DK ages. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. +- After the first round of independent reviews (GPT-6.1 Sol, Subfleet jobs `20261011-221957-d637-review-a2`, `-222335-d637-review-a3` and `-222713-d637-review-a4`; amendment 2 REQUEST CHANGES, amendments 3 and 4 APPROVE with findings; records in `EV/rulings-records-20261011/`): amendment 2's rule no longer names the slot's occupants, which would have decided code 88's slot status, a part of 1c that awaits d745; it defers to the roles registry's `spouse_slot` rule with that caveat. The field adapter refuses the spouse sex, and any slot-metadata concept but the spouse age, under either gate, and a static test keeps both names out of every other U2 module and U1's income concept, and out of the U2-owned scripts (`scripts/*track_u2*` and `scripts/u2_*`) other than the registry capture scripts, except the dry run's count of invented DK ages. Amendment 3's resolution says U2 reads none of the four components as an input of its own. The validator now holds each applied amendment to its adjudication disposition and amendment number and to its required text fields, holds amendment 4's nine corrected targets, refuses a ruling whose decision is not d637 or d1090 and an amendment's distinctive value outside its scope, forbids a scalar variable on a crosswalk, and pins amendment 3's restriction text. Tests pin each amendment's ruled wording by hash and operative clause, with mutations that each fail, and the registry differential compares every kept question and refusal with the d1090 registries' text in full. - After the review of the amendment-6 commit (GPT-6.1 Sol, Subfleet job `20261011-224548-d637-review-a6`, REQUEST CHANGES; same folder): the validator holds the 2017 weight's selection, purpose and `registration_disclosure` exactly, so the disclosure registration must state cannot drift, and five mutations that empty, overstate or drop part of it, or exclude or recalibrate the sample, each fail. Its construction note now says the open points "remain undocumented", not "unresolved", on a RESOLVED record. - After the review of the amendment-5 commits (Opus 5.5 on a review lane, Subfleet job `20261011-224548-d637-review-a5`; REQUEST CHANGES, with the §15 decision judged "the only reading that fits the record"): amendment 5's texts now name both dispositions, `off_route` for formula, DK, refused and NA plans and the duplicate guard for a combined plan's re-asked P64/P65 items, as the builder counts them, and say the 5a route supersedes §15's generic route while §15's text waits for §20 step 5. The validator pins the route entries the builder reads for 2015–2023 (the DC-only checkpoint, combined-plan and DC-only plan types). The invented population has, from 2019, a DK amount on an account plan's route as well as one on an off-route plan, with its comments corrected. Tests re-block, in memory, every route entry the employer-DC route requires and a P64 item of each later wave, check that an off-route DK or NA amount is never an unreported zero, and pin the preflight's twelve code-88 blockers exactly. -- After the delta review of these answers and the wording commit (GPT-6.1 Sol, Subfleet job `20261011-230633-d637-review-r2`; REQUEST CHANGES, three should-fix findings, every earlier finding resolved or reasonably declined): the six adjudication notes say a registry's `rulings_applied` records a ruling "where present", since the support registry has none, and the individual registry records amendment 6's release of its 2017 weight; the note test requires each note to be exactly the adjudication's sentences plus the new ending; and the wording test, with each amendment's hash re-pinned to a mutated text, still fails on an operative clause, so the clauses are tested on their own. +- After the delta review of these answers and the wording commit (GPT-6.1 Sol, Subfleet job `20261011-230633-d637-review-r2`; REQUEST CHANGES, three should-fix findings, every earlier finding resolved or reasonably declined): the six adjudication notes say a registry's `rulings_applied` records a ruling "where present", since the support registry has none, and the individual registry records amendment 6's release of its 2017 weight; the note test requires each note to be exactly the adjudication's sentences plus the new ending; and the wording test, with each amendment's hash re-pinned to a mutated text, still fails on an operative clause, so the clauses are tested on their own. Its review (Subfleet job `20261012-051305-d637-review-r3`, GPT-6.1 Sol) found the static guard missed `scripts/u2_*`; it now covers every U2-owned script, and every d637 `rulings_applied` record's keys, and the individual registry's note exactly, are pinned. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 789cfaee5..1414fc07d 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -353,6 +353,17 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "support": {"independent_adjudication"}, "weights": {"rulings_applied", "independent_adjudication"}, } +#: The exact note of a d637 record in a registry where the ruling only +#: released a dependency (the individual registry, amendment 6). +D637_RECORD_NOTES = { + "individual": ( + "Max's ruling d637 (2026-09-30; specification section 16b) took " + "amendment 6's option 1 for the 2017 cross-sectional weight " + "(weights:2017.cross_section_weight, ER34651). Its registry commit " + "released this registry's 2017.weight from its dependency on that " + "record; no entry here records a ruling." + ), +} #: The wording commit's note, after the adjudication's own sentences. D637_ADJUDICATION_NOTE_END = ( "A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's " @@ -419,6 +430,18 @@ def test_d637_moved_only_the_ruled_gate_fields(): "16b", ) assert record["amendments"] == D637_RECORDED[name], name + assert set(record) == { + "decision", + "date", + "specification_section", + "amendments", + "note", + }, name + assert record["note"].startswith( + "Max's ruling d637 (2026-09-30; specification section 16b) " + ), name + if name in D637_RECORD_NOTES: + assert record["note"] == D637_RECORD_NOTES[name], name assert after["rulings_applied"][:-1] == before.get( "rulings_applied", [] ), name @@ -705,12 +728,15 @@ def test_spouse_age_and_sex_are_read_nowhere_else(): text = Path(adjusted_poverty.__file__).read_text(encoding="utf-8") assert "wife_age" not in text and "wife_sex" not in text root = Path(registry.REGISTRY_DIRECTORY).parents[2] - scripts = [ - path - for path in sorted((root / "scripts").glob("*track_u2*.py")) - if not path.name.startswith("capture_") - ] - assert len(scripts) >= 4 + # The U2-owned scripts (test_u2_isolation.py: scripts/*track_u2* and + # scripts/u2_*), less the capture scripts. + owned = { + *(root / "scripts").glob("*track_u2*.py"), + *(root / "scripts").glob("u2_*.py"), + } + scripts = sorted(p for p in owned if not p.name.startswith("capture_")) + assert "u2_u1_differential.py" in {p.name for p in scripts} + assert len(scripts) >= 5 uses = [ (path.name, line.strip()) for path in scripts From 9d58b3a71619889ec3e83d95e679a34aba6f5de6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 09:23:27 -0400 Subject: [PATCH 14/21] Record Max's confirmation of U2 amendment 1a-1c (d745) Max confirmed on 2026-10-12 (d745) that his d637 ruling ratified u2-draft-4 amendment 1's parts 1a-1c as written: the 2015 code-90 and 2017 code-90/92 OFUM routes, 2013's inherited meanings and the code-88 convention. Section 16b said his answer "will be recorded here". - Section 16b ends with the confirmation, quoting the decision log ("yes: d637 ratified U2 amendments 1a-1c as written (Max in chat 2026-10-12)") and saying what now governs; the rest of section 16b is unchanged. The test holds the d637 text before the new paragraph to its pinned hash and the paragraph to its exact lines; four mutations each fail. - The version and status lines, section 17 items 1 and 2 and section 20 step 1 point to the confirmation; their pinned clauses move with them. Section 19 records it; the spec is re-pinned. No registry-cited line and no line before section 16a other than lines 3 and 4 changes. - Master record: the summary, the code-88 blocker row, the pointer to section 16b and the d637 section's introduction. No registry entry changes here; the registry effects (section 16a, "Record 1a-1c") follow in their own commits. The loader still refuses on 12 blockers, all code 88's. Nothing reads PSID records. On master (after #524's final commit 15409bd4), one d1090 mutation appended "B1 and B2 remain held." to section 17 item 1's last sentence, "This item stays open only for 1a-1c, until Max confirms."; item 1 now ends "This item is closed.", so the mutation targets that. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 23 ++++- docs/design/u2_m1_source_adjudication.md | 10 +- docs/design/u2_m1b_exposure.md | 2 + tests/README-tiers.md | 4 +- tests/tier_counts.json | 2 +- tests/track_u2/test_adjudication_applied.py | 94 +++++++++++++++---- 7 files changed, 106 insertions(+), 31 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 3b3387150..dbf3e4934 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "f69cff3df5627abceb2d6c80e45699ad3fff0b109c0d7de8aed5f7aca674abb9", + "sha256": "41b652b2eb5d5d48608ac3b55a0d8589d6919fc903b9efaf46f9e2eaf75071ba", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 784720853..dd034a9c5 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1,7 +1,7 @@ # Boomers 2004, 1946–55: specification for held-out target U2 -- **Version:** `u2-draft-4` (milestone 1b, 2026-09-28; revised 2026-09-29 after the independent review; Max's rulings recorded 2026-09-30 and 2026-10-10): proposed amendments 1–6 in §16a, ruled by Max on 2026-09-30 (d637) as recorded in §16b, with blockers B1 and B2 ruled on 2026-10-10 (d1090) as recorded in §16c. Previous version: `u2-draft-3` (round 2). -- **Status:** decisions ruled for `u2-draft-3`. On 2026-09-28 Max approved the §16 defaults and ratification of that draft (d514, in chat). Per §20, the version becomes `u2-ratified-1` by the merge that completes §20 steps 2–5 and materializes the final parameter block; that merge needs no further ruling unless it changes this document's substance. `u2-draft-4` adds §16a, whose proposed amendments change substance, so each needed Max's ruling first. He ruled on 2026-09-30 (d637; §16b): B3 is resolved by option 1, amendments 2–5 are ratified as written, and blockers B1 and B2 were held until PSID answered. PSID answered on 2026-10-07, and on 2026-10-10 he ruled on B1 and B2 (d1090; §16c): B1 takes option 2 and B2 option 1, each an exclusion under a named disposition, ratified as amendments to draft-3 line 160 (B1) and to draft-3 lines 152, 154 and 158 for 2019–2023 (B2). No blocker hold remains. Amendment 1's parts 1a–1c, which the d637 ruling's list does not name, are read as ratified pending Max's confirmation (d745; §16b). Not registered or authorized for execution. +- **Version:** `u2-draft-4` (milestone 1b, 2026-09-28; revised 2026-09-29 after the independent review; Max's rulings recorded 2026-09-30, 2026-10-10 and 2026-10-12): proposed amendments 1–6 in §16a, ruled by Max on 2026-09-30 (d637) as recorded in §16b, with blockers B1 and B2 ruled on 2026-10-10 (d1090) as recorded in §16c, and amendment 1's parts 1a–1c confirmed on 2026-10-12 (d745) as recorded in §16b. Previous version: `u2-draft-3` (round 2). +- **Status:** decisions ruled for `u2-draft-3`. On 2026-09-28 Max approved the §16 defaults and ratification of that draft (d514, in chat). Per §20, the version becomes `u2-ratified-1` by the merge that completes §20 steps 2–5 and materializes the final parameter block; that merge needs no further ruling unless it changes this document's substance. `u2-draft-4` adds §16a, whose proposed amendments change substance, so each needed Max's ruling first. He ruled on 2026-09-30 (d637; §16b): B3 is resolved by option 1, amendments 2–5 are ratified as written, and blockers B1 and B2 were held until PSID answered. PSID answered on 2026-10-07, and on 2026-10-10 he ruled on B1 and B2 (d1090; §16c): B1 takes option 2 and B2 option 1, each an exclusion under a named disposition, ratified as amendments to draft-3 line 160 (B1) and to draft-3 lines 152, 154 and 158 for 2019–2023 (B2). No blocker hold remains. Amendment 1's parts 1a–1c, which the d637 ruling's list does not name, were read as ratified pending Max's confirmation, and on 2026-10-12 he confirmed that d637 ratified them as written (d745; §16b). Not registered or authorized for execution. - **Intended file:** `docs/design/boomers2004_1946_55_comparison.md`. - **Specification identity:** `boomers2004_1946_55_uniform_cut`. - **Target identity:** `U2`. Row identifiers are local to this specification: **row U2** means “no SSI response.” @@ -1607,6 +1607,12 @@ The verifier is the independent milestone-1b verifier (workflow `wf_07a4bfac-595 - PSID's answers, when they come, go into the research record. Any change they bring to the declared interpretation needs an explicit reviewed amendment (draft-3 line 160), and nothing proceeds on a count or an outcome. - No count, statistic or outcome was computed to record this ruling, and no file on the restricted list was opened for reading. A file-name search that scanned the whole evidence folder, and two issue #42 listings that touched restricted result comments, are disclosed with the other incidental exposures in `docs/design/u2_m1b_exposure.md` (2026-09-30 section); none touches U2. +**Max's confirmation of amendment 1a–1c (2026-10-12, d745).** Max confirmed the reading above on 2026-10-12. His answer, as recorded in the chief-of-staff decision log: + +> "yes: d637 ratified U2 amendments 1a-1c as written (Max in chat 2026-10-12)" + +So amendment 1's parts 1a–1c are ratified as written by d637. The 2015 code-90 route, the 2017 code-90 and code-92 routes, 2013's inherited meanings and the code-88 convention govern, with 1a's completeness disclosure at registration; the wave table governs outside blockers B1 and B2, which d1090 settled (§16c). Where this section reads 1a–1c as ratified pending Max's confirmation (d745), that confirmation is now given; the rest of this section is unchanged, as the record of the ruling and the reading. Each part's registry effect (§16a, "Registry effect of the rulings": "Record 1a–1c") is its own reviewed commit (§17 item 2). No count, statistic or outcome was computed to record this confirmation, and no file on the restricted list was opened for reading. + ## 16c. Max's ruling on blockers B1 and B2 (2026-10-10, d1090) > **RULED on 2026-10-10.** Max ruled on blockers B1 and B2 on 2026-10-10 (decision d1090), after PSID answered the documentary questions sent on 2026-09-30 (research record §§1–2). This section records the ruling, the two amendments to draft 3 that it ratifies and their effect. Where §16a proposes, or §16b holds, an option for B1 or B2, this section governs; §16a and §16b stay unchanged as the record of what was proposed and first ruled. @@ -1649,8 +1655,8 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: -1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. This item stays open only for 1a–1c, until Max confirms. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. The 6 TO VERIFY records left are code 88's, open until Max confirms the 1a–1c reading (d745). +1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. **Confirmed 2026-10-12 (d745; §16b):** amendment 1's parts 1a–1c are ratified as written. This item is closed. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. The 6 TO VERIFY records left are code 88's, open until amendment 1c's registry commit lands (Max confirmed 1a–1c on 2026-10-12, d745). 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1854,11 +1860,18 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - After the delta review of these answers and the wording commit (GPT-6.1 Sol, Subfleet job `20261011-230633-d637-review-r2`; REQUEST CHANGES, three should-fix findings, every earlier finding resolved or reasonably declined): the six adjudication notes say a registry's `rulings_applied` records a ruling "where present", since the support registry has none, and the individual registry records amendment 6's release of its 2017 weight; the note test requires each note to be exactly the adjudication's sentences plus the new ending; and the wording test, with each amendment's hash re-pinned to a mutated text, still fails on an operative clause, so the clauses are tested on their own. Its review (Subfleet job `20261012-051305-d637-review-r3`, GPT-6.1 Sol) found the static guard missed `scripts/u2_*`; it now covers every U2-owned script, and every d637 `rulings_applied` record's keys, and the individual registry's note exactly, are pinned. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. +**`u2-draft-4`, Max's confirmation of amendment 1a–1c recorded (2026-10-12, d745):** + +- §16b ends with Max's confirmation, quoted as the decision log records it: d637 ratified amendment 1's parts 1a–1c as written. The rest of §16b is unchanged, and a test holds it to its d637 text. The version and status lines, §17 items 1 and 2 and §20 step 1 point to the confirmation. +- Changed no line that a registry cites and no line before §16a other than lines 3 and 4. The specification pin in `data/external/track_u2/u1_identity.json` moves to the new hash; its `version` stays `u2-draft-4`. +- The registry effects (§16a: "Record 1a–1c") follow in their own reviewed commits. +- No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. + ## 20. Ratification and execution record **Pending.** -1. Resolve the proposed decisions and source-verification blockers. **Decisions: done (Max, d514, 2026-09-28, adopting every §16 default).** Source-verification blockers remain for steps 2–5. **`u2-draft-4` amendments 1–6 (§16a): ruled by Max on 2026-09-30 (d637; §16b).** B3 is resolved by option 1. B1 and B2 were held until PSID answered; **Max ruled on them on 2026-10-10 (d1090; §16c)**: B1 by option 2 and B2 by option 1, each an exclusion under a named disposition. This step stays open only because amendment 1's parts 1a–1c await Max's confirmation of the reading in §16b (d745). +1. Resolve the proposed decisions and source-verification blockers. **Decisions: done (Max, d514, 2026-09-28, adopting every §16 default).** Source-verification blockers remain for steps 2–5. **`u2-draft-4` amendments 1–6 (§16a): ruled by Max on 2026-09-30 (d637; §16b).** B3 is resolved by option 1. B1 and B2 were held until PSID answered; **Max ruled on them on 2026-10-10 (d1090; §16c)**: B1 by option 2 and B2 by option 1, each an exclusion under a named disposition. Max confirmed the reading of amendment 1's parts 1a–1c in §16b on 2026-10-12 (d745), so this step is done. 2. Complete implementation, manifests and parameter capture. 3. Complete independent mapping review, invented tests, historical-isolation checks and exact U1 differential evidence. 4. Complete the authorized pre-registration structural, reconciliation and F17 component pass; resolve mapping failures. diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index 69f5c896c..211e622a5 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,7 +2,7 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2026-09-28) to apply the independent adjudication `EV/phase2-20260927/out/u2-adjudicate.md` (SHA-256 `a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662`, verdict ERRORS FOUND). This record does not ratify, register or execute U2. No PSID records were read and no estimator was run. Milestone 1b writes the proposed revision `u2-draft-4` into the specification's new §16a; every line the registries cite keeps its draft-3 text and line number. -**1786 RESOLVED; 0 REFUSED; 6 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 6 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for his confirmation of the 1a–1c reading (d745). +**1786 RESOLVED; 0 REFUSED; 6 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 6 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for amendment 1c's registry commit (Max confirmed the 1a–1c reading on 2026-10-12, d745). | Registry | RESOLVED | REFUSED | TO VERIFY | |---|---:|---:|---:| @@ -27,7 +27,7 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | 2015 male code 20 | RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | The adjudication refuses the male code-20 spouse-slot route: q2015 pp. 89 and 166 admit only CYAQRTH=202, 222 and ER60020 (p. 6) lists only female. Part B found that official documentation routes every documented kind of male legal spouse in 2015 away from code 20: through 2015 a husband is Head (UserGuide2017 PDF p. 13; FAQ question 73), a husband who is not Head is code 90 and 'was asked the same questions as an Ofum' (FAQ question 74), and same-sex partners are code 98 with the ER34304 flag (IND2023ER_codebook p. 978; UserGuide2015 PDF p. 33; UserGuide2017 PDF p. 35). The 2015 Section J qualifier classes values 102-222 as female (q2015 p. 190). No document says code 20 is never male in 2015, and FAQ question 70's general sentence allows a same-sex Spouse/Partner couple from 2015, so the documents do not settle how a male Head's legally married same-sex spouse was coded. PSID answered on 2026-10-07 that no male should have code 20 in 2015 (research record §1). Max ruled on 2026-10-10 (d1090; specification §16c, option 1d-2, amending draft-3 line 160): every 2015 family-unit observation that contains a person recorded male with code 20 is excluded from every row under `male code 20 in 2015 family unit`. | | Code 90, 2015–2023 | 2015 and 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | 2015 and 2017 OFUM collection is documented (q2015 pp. 89, 104, 166; q2017 pp. 5, 95; FAQ question 74). From 2019 the Big OFUM gate admits only CYAQRTH=301-882 or 951-982, so 901/902 get no Section G income questions (q2019 p. 103; q2021 p. 205; q2023 p. 203); whether their income enters the aggregates is not documented online, and PSID's 2026-10-07 answer did not say (research record §2). Max ruled on 2026-10-10 (d1090; specification §16c, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019–2023): every 2019–2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under `uncooperative spouse or partner in family unit`. | | Code 92, 2017–2023 | 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | -| Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is proposed in amendment 1. | +| Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is amendment 1c's, ratified by d637 as Max confirmed on 2026-10-12 (d745); its registry commit applies it. | | 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a's wave-specific route supersedes the §15 contract's generic account/formula/dk previous route (§16b; §15's text is unchanged until §20 step 5 materializes the final parameter block), so the dependent P64/P65 entries are released. | | Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot: the relationship rules in the roles registry do, and code 88's slot status stays TO VERIFY (amendment 1c, pending d745). | @@ -53,7 +53,7 @@ The independent adjudication (`EV/phase2-20260927/out/u2-adjudicate.md`) confirm | A: open | 24 | Still TO VERIFY; each question now names its disposition, amendment and adjudication line | | Dependencies released | 248 entries | `roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92` no longer block dependents. 2015 slot maps stay blocked by the refused code-20 route and 2019–2023 slot maps by the refused 90/92 routes; the 2017 slot maps no longer carry a role blocker. `pension:2015.route.respondent_slots` now depends on `roles:2015.relationship.20` | -Part B's documentary research is recorded in `docs/design/u2_m1b_psid_research.md`, its sources in `tests/data/track_u2/psid_docs/manifest.json`, and the proposed amendments 1–6 in the specification's §16a (`u2-draft-4`), on which Max ruled on 2026-09-30 (d637; recorded in §16b) and, for blockers B1 and B2, on 2026-10-10 after PSID answered (d1090; recorded in §16c). The complete reading record is `docs/design/u2_m1b_exposure.md`. +Part B's documentary research is recorded in `docs/design/u2_m1b_psid_research.md`, its sources in `tests/data/track_u2/psid_docs/manifest.json`, and the proposed amendments 1–6 in the specification's §16a (`u2-draft-4`), on which Max ruled on 2026-09-30 (d637; recorded in §16b) and, for blockers B1 and B2, on 2026-10-10 after PSID answered (d1090; recorded in §16c); he confirmed on 2026-10-12 that d637 ratified amendment 1's parts 1a–1c as written (d745; recorded in §16b). The complete reading record is `docs/design/u2_m1b_exposure.md`. ## Max's ruling d1090 applied (2026-10-10) @@ -70,7 +70,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu ## Max's ruling d637 applied (registry commits from 2026-10-11) -Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): amendments 2–5 ratified as written and amendment 6's option 1 ratified as an explicit amendment to draft-3 line 134. Each amendment's registry effect is its own reviewed commit, and `RATIFIED_AMENDMENTS` in `src/populace_dynamics/data/u2_source_registry.py` pins each applied amendment's entries, ruling and gate fields. Each ruled entry keeps its adjudication record and gains a `ruling`; an open question or refusal the ruling answers is kept as `superseded_question` or `superseded_refusal`. Amendment 1's parts 1a–1c, and the six code-88 records with them, wait for Max's confirmation (d745). +Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): amendments 2–5 ratified as written and amendment 6's option 1 ratified as an explicit amendment to draft-3 line 134. Each amendment's registry effect is its own reviewed commit, and `RATIFIED_AMENDMENTS` in `src/populace_dynamics/data/u2_source_registry.py` pins each applied amendment's entries, ruling and gate fields. Each ruled entry keeps its adjudication record and gains a `ruling`; an open question or refusal the ruling answers is kept as `superseded_question` or `superseded_refusal`. Max confirmed on 2026-10-12 that d637 ratified amendment 1's parts 1a–1c as written (d745), and their registry commits are listed here too. | Amendment | Records | Registry effect | |---|---:|---| @@ -81,7 +81,7 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 5a: the 2017–2023 previous-employer route | 4 + 40 released | `pension:{2017,2019,2021,2023}.route.inherited_route_amendment` keep their documentary resolution (adjudication D) and now apply: each drops its `refuse_pending_amendment_5_ruling` action, keeps its refusal as `superseded_refusal` and gains a `ruling_effect`. The 40 entries that depended on them (each wave's P64 and P65 items of both persons' two previous plans, `previous_dc_only` and `duplicate_and_off_route`) are released, none with a dependency left. The registry gate now applies every wave's employer-DC route, each equal to the declared route | | 6: the 2017 cross-sectional weight | 1 + 1 released | `weights:2017.cross_section_weight` is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration, and a `registration_disclosure` listing what PSID has documented (its identity, the ACS one-year PUMS control and its two named exclusions, the calibration-variable selection approach, some calibration controls, the 2017 Immigrant sample's base weights as formed for the 2019 weights) and has not published (that sample's starting weights and joint-family integration in the revised 2017 weight, and the complete 2017 raking specification), with the coverage bound the control's exclusions set. `individual:2017.weight` is released | -The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, pending d745): each wave's code-88 role entry is TO VERIFY and refuses its substantive routing. +The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, whose registry commit follows): each wave's code-88 role entry is TO VERIFY and refuses its substantive routing. ## One row per source route or field diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index adb7e1f57..6fbf446ab 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -383,6 +383,7 @@ A Claude Code session (Opus 5.5, session `943a9dba-…`) applied Max's ruling d6 - The chief-of-staff decision log, filtered for d637, d745 and d1090; the filter also matched d1242, whose one-line summary concerns B2's count unit. - In the repository, at #524's head and then on this branch: the specification (header, §3's relationship amendment, §15's parameter block and §§16–20), the research record's §3, the master record (summary, blocker rows, the d1090 section and the rows of the entries changed), this file's 2026-10-10 section, the eight registries and `u1_identity.json`, `src/populace_dynamics/data/u2_source_registry.py`, the U2 package's `sources.py`, `loader.py`, `cohort.py`, `invented.py`, `rows.py` and `estimator.py` (in part), `scripts/track_u2_dry_run.py` (in part), the tests under `tests/track_u2/` that the brief names, and `tests/README-tiers.md` with `tests/tier_counts.json`. - `git worktree list`, the state, checks and file lists of #524 and #525, and #525's diff to `sources.py` and `loader.py`. +- On 2026-10-12, the decision log's record of d745 (its ruling, date and note), after the coordinating session relayed it. ### Not opened @@ -406,6 +407,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment-6 review fixes: `weights.json` (one word of the 2017 weight's construction note) and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (amendment 6's exact fields) and `sources.py` (the weights byte pin); `tests/track_u2/test_adjudication_applied.py` and `test_source_registries.py`; and this section. - Amendment-5 review fixes: `pension.json` (the four 5a `ruling_effect` texts and the 2015 checkpoint's resolution) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row and five rows); `u2_source_registry.py` (the governed routes), `sources.py` (the pension byte pin) and `invented.py` (the 2019–2023 DC items and comments); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py` and `test_u2_employer_dc.py`; and this section. - Delta-review fixes: the six adjudication notes and a d637 record in `individual.json`, and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (a type check), `sources.py` (seven byte pins and two docstrings); `tests/track_u2/test_adjudication_applied.py` and `test_u2_mappings.py`; and this section. The amendment-5 answers also changed `tests/track_u2/test_u2_scripts.py`'s variant expectations. +- Max's confirmation of 1a–1c (d745): the specification (lines 3 and 4, §16b's closing paragraph, §17 items 1 and 2, §19 and §20 step 1) and `u1_identity.json`; the master record (the summary, the code-88 blocker row, the pointer to §16b and the d637 section); `tests/track_u2/test_adjudication_applied.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/tests/README-tiers.md b/tests/README-tiers.md index c87edc56b..105695ef4 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -41,7 +41,7 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 |---|---:| | `unit` | 7,498 | | `artifact` | 4,145 | -| `integration_psid` | 1,396 | +| `integration_psid` | 1,400 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,867** | +| **Total** | **13,871** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 6ed85edc7..807cae109 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -3,7 +3,7 @@ "counts": { "unit": 7498, "artifact": 4145, - "integration_psid": 1396, + "integration_psid": 1400, "reproduction_legacy": 520, "oracle_policyengine": 308 } diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 9483495e9..2da87145a 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -1161,6 +1161,31 @@ def test_section_16a_recommendations_and_options(): "312ac7673e2b3c16cd67b1fe8bb3c68cf1fc5e2e40c6055e9cf616e2043bcdab" ), } +#: Max's confirmation of amendment 1a-1c (2026-10-12, d745), appended to +#: section 16b, which says his answer "will be recorded here": every line. +D745_RECORD = [ + "**Max's confirmation of amendment 1a\N{EN DASH}1c (2026-10-12, " + "d745).** Max confirmed the reading above on 2026-10-12. His answer, " + "as recorded in the chief-of-staff decision log:", + "", + '> "yes: d637 ratified U2 amendments 1a-1c as written (Max in chat ' + '2026-10-12)"', + "", + "So amendment 1's parts 1a\N{EN DASH}1c are ratified as written by " + "d637. The 2015 code-90 route, the 2017 code-90 and code-92 routes, " + "2013's inherited meanings and the code-88 convention govern, with " + "1a's completeness disclosure at registration; the wave table governs " + "outside blockers B1 and B2, which d1090 settled " + "(\N{SECTION SIGN}16c). Where this section reads 1a\N{EN DASH}1c as " + "ratified pending Max's confirmation (d745), that confirmation is now " + "given; the rest of this section is unchanged, as the record of the " + "ruling and the reading. Each part's registry effect " + '(\N{SECTION SIGN}16a, "Registry effect of the rulings": "Record ' + '1a\N{EN DASH}1c") is its own reviewed commit (\N{SECTION SIGN}17 ' + "item 2). No count, statistic or outcome was computed to record this " + "confirmation, and no file on the restricted list was opened for " + "reading.", +] #: Max's d1090 ruling (2026-10-10): the §16a option each blocker takes, #: the option's amendment statement as §16a words it, and its named #: disposition. @@ -1211,11 +1236,13 @@ def test_section_16a_recommendations_and_options(): "header_version": [ "- **Version:** `u2-draft-4` (milestone 1b, 2026-09-28; revised " "2026-09-29 after the independent review; Max's rulings recorded " - "2026-09-30 and 2026-10-10): proposed amendments 1\N{EN DASH}6 in " - "\N{SECTION SIGN}16a, ruled by Max on 2026-09-30 (d637) as recorded " - "in \N{SECTION SIGN}16b, with blockers B1 and B2 ruled on " - "2026-10-10 (d1090) as recorded in \N{SECTION SIGN}16c. Previous " - "version: `u2-draft-3` (round 2).", + "2026-09-30, 2026-10-10 and 2026-10-12): proposed amendments " + "1\N{EN DASH}6 in \N{SECTION SIGN}16a, ruled by Max on 2026-09-30 " + "(d637) as recorded in \N{SECTION SIGN}16b, with blockers B1 and B2 " + "ruled on 2026-10-10 (d1090) as recorded in \N{SECTION SIGN}16c, " + "and amendment 1's parts 1a\N{EN DASH}1c confirmed on 2026-10-12 " + "(d745) as recorded in \N{SECTION SIGN}16b. Previous version: " + "`u2-draft-3` (round 2).", ], "header_status": [ "- **Status:** decisions ruled for `u2-draft-3`. On 2026-09-28 Max " @@ -1235,7 +1262,8 @@ def test_section_16a_recommendations_and_options(): "amendments to draft-3 line 160 (B1) and to draft-3 lines 152, 154 " "and 158 for 2019\N{EN DASH}2023 (B2). No blocker hold remains. " "Amendment 1's parts 1a\N{EN DASH}1c, which the d637 ruling's list " - "does not name, are read as ratified pending Max's confirmation " + "does not name, were read as ratified pending Max's confirmation, " + "and on 2026-10-12 he confirmed that d637 ratified them as written " "(d745; \N{SECTION SIGN}16b). Not registered or authorized for " "execution.", ], @@ -1378,8 +1406,9 @@ def test_section_16a_recommendations_and_options(): "(d1090; \N{SECTION SIGN}16c), after PSID answered:** B1 takes " "option 2, with its amendment to draft-3 line 160 ratified, and B2 " "takes option 1, with its amendment to draft-3 lines 152, 154 and " - "158 ratified for 2019\N{EN DASH}2023. This item stays open only " - "for 1a\N{EN DASH}1c, until Max confirms.", + "158 ratified for 2019\N{EN DASH}2023. **Confirmed 2026-10-12 " + "(d745; \N{SECTION SIGN}16b):** amendment 1's parts " + "1a\N{EN DASH}1c are ratified as written. This item is closed.", "2. Each ruling is applied to the registries in its own reviewed " "commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 " "refusals with \N{SECTION SIGN}16c's exclusions and released their " @@ -1392,7 +1421,8 @@ def test_section_16a_recommendations_and_options(): "records under 5a and resolved the 2017 weight ER34651 under " "amendment 6's option 1; the \N{SECTION SIGN}15 fragment itself is " "materialized under item 9. The 6 TO VERIFY records left are code " - "88's, open until Max confirms the 1a\N{EN DASH}1c reading (d745).", + "88's, open until amendment 1c's registry commit lands (Max " + "confirmed 1a\N{EN DASH}1c on 2026-10-12, d745).", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " @@ -1409,9 +1439,9 @@ def test_section_16a_recommendations_and_options(): "\N{SECTION SIGN}16b).** B3 is resolved by option 1. B1 and B2 were " "held until PSID answered; **Max ruled on them on 2026-10-10 " "(d1090; \N{SECTION SIGN}16c)**: B1 by option 2 and B2 by option 1, " - "each an exclusion under a named disposition. This step stays open " - "only because amendment 1's parts 1a\N{EN DASH}1c await Max's " - "confirmation of the reading in \N{SECTION SIGN}16b (d745).", + "each an exclusion under a named disposition. Max confirmed the " + "reading of amendment 1's parts 1a\N{EN DASH}1c in " + "\N{SECTION SIGN}16b on 2026-10-12 (d745), so this step is done.", ], } @@ -1492,6 +1522,15 @@ def test_section_16c_records_the_d1090_ruling(): SECTION_SHA256.items(), starts[1:3], strict=True ): text = spec[spec.index(heading) : end] + if heading == "## 16b. ": + # Max's d745 confirmation is appended; the d637 text before it + # is unchanged, and the confirmation is exactly D745_RECORD. + text, appended = text.split(D745_RECORD[0], 1) + assert (D745_RECORD[0] + appended).split("\n") == [ + *D745_RECORD, + "", + "", + ] assert hashlib.sha256(text.encode()).hexdigest() == digest, heading proposals = spec[starts[0] : starts[1]] record = spec[starts[2] : starts[3]] @@ -1511,6 +1550,28 @@ def test_section_16c_records_the_d1090_ruling(): check_d1090_record(spec) +@pytest.mark.parametrize( + "old, new", + [ + ('"yes: d637 ratified', '"no: d637 did not ratify'), + ("are ratified as written by d637", "stay proposed"), + ("the code-88 convention govern", "the code-88 refusal govern"), + ("is its own reviewed commit", "lands without review"), + ], +) +def test_the_d745_record_catches_each_mutation(old, new): + """Section 16b's d745 paragraph is pinned line by line, and the d637 + text before it by hash: a changed word fails.""" + spec = (ROOT / SPEC).read_text(encoding="utf-8") + start = spec.index(D745_RECORD[0]) + end = spec.index("## 16c. ") + assert old in spec[start:end] + mutated = spec[:start] + spec[start:end].replace(old, new, 1) + spec[end:] + section = mutated[mutated.index("## 16b. ") : mutated.index("## 16c. ")] + _, appended = section.split(D745_RECORD[0], 1) + assert (D745_RECORD[0] + appended).split("\n") != [*D745_RECORD, "", ""] + + #: Mutations of the d1090 record that change what it says, each by #: place: every one must fail ``check_d1090_record`` (review of e4dd350, #: finding 2, and the counts of finding 1). @@ -1564,7 +1625,7 @@ def test_section_16c_records_the_d1090_ruling(): ), ( "execution", - "of the reading in \N{SECTION SIGN}16b (d745).", + "so this step is done.", "only if the structural pass finds nonzero exclusions.", ), ( @@ -1607,10 +1668,9 @@ def test_section_16c_records_the_d1090_ruling(): ), ( "outstanding", - "This item stays open only for 1a\N{EN DASH}1c, until Max " - "confirms.", - "This item stays open only for 1a\N{EN DASH}1c, until Max " - "confirms.\n B1 and B2 remain held.", + "1a\N{EN DASH}1c are ratified as written. This item is closed.", + "1a\N{EN DASH}1c are ratified as written. This item is closed.\n" + " B1 and B2 remain held.", ), ("outstanding", "and B2 takes option 1,", "and B2 takes option 2,"), ("outstanding", "each disclosed and deciding nothing", "each disclosed"), From 112abf03ebd76762eee0ef422fd54ee90828868c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 09:29:05 -0400 Subject: [PATCH 15/21] Apply d637 amendment 1a to the U2 roles registry Max's ruling d637 ratified u2-draft-4 amendment 1's parts 1a-1c as written, as he confirmed on 2026-10-12 (d745; section 16b). This is the 1a registry commit (section 16a, "Registry effect of the rulings": "Record 1a-1c"). - roles.json: the three documented routes, 2015 code 90 and 2017 codes 90 and 92 (adjudication D, already RESOLVED), record the ruling (d637, amendment 1a, confirmed_by d745, confirmed_on 2026-10-12) and a ruling_effect, and each gains the completeness_disclosure 1a requires at registration: the OFUM route is documented, but not how completely the income of a person about whom answers were withheld was reported (FAQ question 74; PSID's documented processing of missing OFUM data, by deduction). Their resolutions, roles and annuity and marital rules are unchanged. A d637 rulings_applied record is added. - u2_source_registry: 1a's scope (entries, ruling with the confirmation, disposition D, fixed role fields, required texts; the disclosure only on these entries); sources: the roles byte pin. - No role rule, count or loader blocker moves (12, all code 88's). - Master record: the code-90 and code-92 blocker rows, the d637 section and three rows. Section 17 item 2 and section 19 record it; the spec is re-pinned. Tests: the d637 differential, the text pins and clauses, and five new scope mutations cover 1a. The master-record test now also fails on a duplicated d637 section or amendment row. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/roles.json | 39 +++++++++++++++++++ data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 11 +++--- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 27 +++++++++++++ .../uniform_cut_track_u2/sources.py | 6 ++- tests/README-tiers.md | 4 +- tests/tier_counts.json | 2 +- tests/track_u2/test_adjudication_applied.py | 33 ++++++++++++++-- tests/track_u2/test_source_registries.py | 31 +++++++++++++++ tests/track_u2/test_u2_mappings.py | 5 ++- 12 files changed, 148 insertions(+), 16 deletions(-) diff --git a/data/external/track_u2/roles.json b/data/external/track_u2/roles.json index 40c114782..eebaf171b 100644 --- a/data/external/track_u2/roles.json +++ b/data/external/track_u2/roles.json @@ -576,6 +576,16 @@ "line": 105, "disposition": "D", "amendment": 1 + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 90 is an OFUM income role outside the spouse slot in 2015, with the annuity and marital rules recorded here, and registration states the completeness_disclosure.", + "completeness_disclosure": "The 2015 code-90 OFUM route is documented; how completely the uncooperative legal spouse's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2015ER pp. 1968-1975), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1a", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" } }, { @@ -842,6 +852,16 @@ "line": 107, "disposition": "D", "amendment": 1 + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 90 is an OFUM income role outside the spouse slot in 2017, with the annuity and marital rules recorded here, and registration states the completeness_disclosure.", + "completeness_disclosure": "The 2017 code-90 OFUM route is documented; how completely the uncooperative legal spouse's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2017ER pp. 2021-2028), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1a", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" } }, { @@ -899,6 +919,16 @@ "line": 108, "disposition": "D", "amendment": 1 + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 92 is an OFUM income role outside the spouse slot in 2017, with the annuity and marital rules recorded here, and registration states the completeness_disclosure.", + "completeness_disclosure": "The 2017 code-92 OFUM route is documented; how completely the uncooperative partner's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2017ER pp. 2021-2028), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a).", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1a", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" } }, { @@ -1890,6 +1920,15 @@ "date": "2026-10-10", "specification_section": "16c", "note": "Max's ruling d1090 (2026-10-10; specification section 16c) on blockers B1 and B2: the seven role refusals and pension:2015.route.respondent_slots are replaced by the ratified family-unit exclusions (B1, section 16a option 1d-2, amending draft-3 line 160; B2, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019-2023), and the dependencies on the seven role entries are released. Each entry keeps its adjudication record (disposition F) and its superseded refusal text." + }, + { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendments": [ + "1a" + ], + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1; on 2026-10-12 Max confirmed that it ratified amendment 1's parts 1a-1c as written too (d745; section 16b). Applied here, each in its own reviewed commit: 1a records the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 OFUM routes, with the completeness disclosure registration states. Each ruled entry keeps its adjudication record, if any, and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index dbf3e4934..965d847b9 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "41b652b2eb5d5d48608ac3b55a0d8589d6919fc903b9efaf46f9e2eaf75071ba", + "sha256": "3bb021c5570e6814a8b34be8698488244e8d25f83a7b196d7e66d4fc205ebaad", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index dd034a9c5..3a2094690 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1656,7 +1656,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. **Confirmed 2026-10-12 (d745; §16b):** amendment 1's parts 1a–1c are ratified as written. This item is closed. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. The 6 TO VERIFY records left are code 88's, open until amendment 1c's registry commit lands (Max confirmed 1a–1c on 2026-10-12, d745). +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. Max confirmed 1a–1c on 2026-10-12 (d745), and the 1a commit recorded the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 routes. The 6 TO VERIFY records left are code 88's, open until amendment 1c's registry commit lands. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1865,6 +1865,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - §16b ends with Max's confirmation, quoted as the decision log records it: d637 ratified amendment 1's parts 1a–1c as written. The rest of §16b is unchanged, and a test holds it to its d637 text. The version and status lines, §17 items 1 and 2 and §20 step 1 point to the confirmation. - Changed no line that a registry cites and no line before §16a other than lines 3 and 4. The specification pin in `data/external/track_u2/u1_identity.json` moves to the new hash; its `version` stays `u2-draft-4`. - The registry effects (§16a: "Record 1a–1c") follow in their own reviewed commits. +- Amendment 1a: the three documented routes (`roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92`, adjudication D) record the ruling, confirmed by d745, and each gains the completeness disclosure 1a requires at registration: the OFUM route is documented, but not how completely the income of a person about whom answers were withheld was reported. No role rule, count or loader blocker moves. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index 211e622a5..a9a58e812 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -25,8 +25,8 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | Blocker | Status | Required resolution or amendment | |---|---|---| | 2015 male code 20 | RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | The adjudication refuses the male code-20 spouse-slot route: q2015 pp. 89 and 166 admit only CYAQRTH=202, 222 and ER60020 (p. 6) lists only female. Part B found that official documentation routes every documented kind of male legal spouse in 2015 away from code 20: through 2015 a husband is Head (UserGuide2017 PDF p. 13; FAQ question 73), a husband who is not Head is code 90 and 'was asked the same questions as an Ofum' (FAQ question 74), and same-sex partners are code 98 with the ER34304 flag (IND2023ER_codebook p. 978; UserGuide2015 PDF p. 33; UserGuide2017 PDF p. 35). The 2015 Section J qualifier classes values 102-222 as female (q2015 p. 190). No document says code 20 is never male in 2015, and FAQ question 70's general sentence allows a same-sex Spouse/Partner couple from 2015, so the documents do not settle how a male Head's legally married same-sex spouse was coded. PSID answered on 2026-10-07 that no male should have code 20 in 2015 (research record §1). Max ruled on 2026-10-10 (d1090; specification §16c, option 1d-2, amending draft-3 line 160): every 2015 family-unit observation that contains a person recorded male with code 20 is excluded from every row under `male code 20 in 2015 family unit`. | -| Code 90, 2015–2023 | 2015 and 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | 2015 and 2017 OFUM collection is documented (q2015 pp. 89, 104, 166; q2017 pp. 5, 95; FAQ question 74). From 2019 the Big OFUM gate admits only CYAQRTH=301-882 or 951-982, so 901/902 get no Section G income questions (q2019 p. 103; q2021 p. 205; q2023 p. 203); whether their income enters the aggregates is not documented online, and PSID's 2026-10-07 answer did not say (research record §2). Max ruled on 2026-10-10 (d1090; specification §16c, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019–2023): every 2019–2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under `uncooperative spouse or partner in family unit`. | -| Code 92, 2017–2023 | 2017 RESOLVED (D); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | +| Code 90, 2015–2023 | 2015 and 2017 RESOLVED (D; amendment 1a ratified by d637, confirmed by d745); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | 2015 and 2017 OFUM collection is documented (q2015 pp. 89, 104, 166; q2017 pp. 5, 95; FAQ question 74). From 2019 the Big OFUM gate admits only CYAQRTH=301-882 or 951-982, so 901/902 get no Section G income questions (q2019 p. 103; q2021 p. 205; q2023 p. 203); whether their income enters the aggregates is not documented online, and PSID's 2026-10-07 answer did not say (research record §2). Max ruled on 2026-10-10 (d1090; specification §16c, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019–2023): every 2019–2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under `uncooperative spouse or partner in family unit`. | +| Code 92, 2017–2023 | 2017 RESOLVED (D; amendment 1a ratified by d637, confirmed by d745); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | | Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is amendment 1c's, ratified by d637 as Max confirmed on 2026-10-12 (d745); its registry commit applies it. | | 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a's wave-specific route supersedes the §15 contract's generic account/formula/dk previous route (§16b; §15's text is unchanged until §20 step 5 materializes the final parameter block), so the dependent P64/P65 entries are released. | @@ -80,6 +80,7 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 5b: the 2015 P62ACKPT checkpoint | 1 + 34 released | `pension:2015.route.formula_unknown_checkpoint` is RESOLVED: accepted plan type 5 (DC only), rejected 1, 7, 8 and 9, the refused branch `P62ACKPT: P46=1 AND P52=1 AND P62AMT=DK/RF` (amendment 5's section 15 fragment, `refused_branches`) and the named disposition `off_route`; its question and refusal are kept as `superseded_question` and `superseded_refusal`. The 34 entries that depended on it (for each person's two previous plans, the six P52 status mentions and the P64 and P65 items, and the `previous_dc_only` and `duplicate_and_off_route` routes) are released, none with a dependency left. The registry gate now applies 2015's employer-DC route, equal to the declared route | | 5a: the 2017–2023 previous-employer route | 4 + 40 released | `pension:{2017,2019,2021,2023}.route.inherited_route_amendment` keep their documentary resolution (adjudication D) and now apply: each drops its `refuse_pending_amendment_5_ruling` action, keeps its refusal as `superseded_refusal` and gains a `ruling_effect`. The 40 entries that depended on them (each wave's P64 and P65 items of both persons' two previous plans, `previous_dc_only` and `duplicate_and_off_route`) are released, none with a dependency left. The registry gate now applies every wave's employer-DC route, each equal to the declared route | | 6: the 2017 cross-sectional weight | 1 + 1 released | `weights:2017.cross_section_weight` is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration, and a `registration_disclosure` listing what PSID has documented (its identity, the ACS one-year PUMS control and its two named exclusions, the calibration-variable selection approach, some calibration controls, the 2017 Immigrant sample's base weights as formed for the 2019 weights) and has not published (that sample's starting weights and joint-family integration in the revised 2017 weight, and the complete 2017 raking specification), with the coverage bound the control's exclusions set. `individual:2017.weight` is released | +| 1a: documented routes (confirmed by d745) | 3 | `roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92` keep their documentary resolution (adjudication D) and record the ruling (`ruling` with `confirmed_by` d745) and a `ruling_effect`; each gains the `completeness_disclosure` registration states: the OFUM route is documented, but not how completely a person about whom answers were withheld had their income reported. No role rule, count or loader blocker moves | The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, whose registry commit follows): each wave's code-88 role entry is TO VERIFY and refuses its substantive routing. @@ -1802,14 +1803,14 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `roles:2015.relationship.20` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1d-2, ratified as an explicit amendment to draft-3 line 160: 2015 code 20 occupies the spouse slot as documented, and every 2015 family-unit observation that contains a person recorded male (ER32000 = 1) with 2015 relationship code 20 is excluded from every row under the named disposition male code 20 in 2015 family unit. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. This replaces the adjudication's refusal, kept as superseded_refusal; family_unit_exclusion.condition records that the exclusion concerns sex=1. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md section 1): official documentation routes every documented kind of male legal spouse in 2015 away from code 20. Through 2015 the husband of a heterosexual couple is Head (UserGuide2017 PDF p. 13; FAQ question 73), a husband who is not Head is 'Husband of Head' code 90 and 'was asked the same questions as an Ofum' (FAQ question 74), and 2015 same-sex partners are code 98 'Other nonrelatives (includes same-sex partners ...)' flagged by ER34304 (IND2023ER_codebook p. 978; UserGuide2015 PDF p. 33; UserGuide2017 PDF p. 35). The 2015 Section J qualifier classes values 102-222 as female, where the 2017 qualifier classes 101, 201 and 221 as male (research record section 1, item 11; q2015 p. 190; q2017 p. 184). No document states that code 20 is never male in 2015, and FAQ question 70 says generally that from 2015 a Spouse/Partner couple 'can consist of heterosexual or same sex couples', so the documents do not settle how a male Head's legally married same-sex spouse was coded. The 2015 'RTH Lookup List' named for CYAQRTH (q2015 p. 5) is absent from the staged documentation. PSID answered on 2026-10-07 that no male should have code 20 in 2015 (research record section 1), and Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:22970`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `PSID/documentation/capture1/q2015.pdf` p. 89; `PSID/documentation/capture1/q2015.pdf` p. 166; `EV/phase2-20260927/out/u2-adjudicate.md:103` | | `roles:2015.relationship.22` | RESOLVED | Partner--female cohabitor who has lived with Head for 12 months or more; 2013 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:22971`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `roles:2015.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 104). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:22996`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:104` | -| `roles:2015.relationship.90` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. I p. 978: 'Uncooperative legal spouse of Head'. The 2015 spouse income and pension loops admit only the Wife/'Wife' codes (q2015 p. 89 G49CKPT and p. 166 P70CKPT: 'CYFUHU=1, 4 & CYAQRTH=202, 222'), while the Big OFUM income series admits 'FUP1YEAR[I].AQRTH.ORD>222' (q2015 p. 104). Code-90 income is collected in the OFUM series and excluded from the standard spouse loop. The declared legal-spouse annuity and marital-resolution conventions are unchanged. | `PSID/ind2023er/IND2023ER_formats.sps:22997`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2015.pdf` p. 89; `PSID/documentation/capture1/q2015.pdf` p. 104; `PSID/documentation/capture1/q2015.pdf` p. 166; `tests/data/track_u2/psid_docs/FAQ_20260813.html:833`; `EV/phase2-20260927/out/u2-adjudicate.md:105` | +| `roles:2015.relationship.90` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. I p. 978: 'Uncooperative legal spouse of Head'. The 2015 spouse income and pension loops admit only the Wife/'Wife' codes (q2015 p. 89 G49CKPT and p. 166 P70CKPT: 'CYFUHU=1, 4 & CYAQRTH=202, 222'), while the Big OFUM income series admits 'FUP1YEAR[I].AQRTH.ORD>222' (q2015 p. 104). Code-90 income is collected in the OFUM series and excluded from the standard spouse loop. The declared legal-spouse annuity and marital-resolution conventions are unchanged. Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 90 is an OFUM income role outside the spouse slot in 2015, with the annuity and marital rules recorded here, and registration states the completeness_disclosure. The 2015 code-90 OFUM route is documented; how completely the uncooperative legal spouse's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2015ER pp. 1968-1975), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a). | `PSID/ind2023er/IND2023ER_formats.sps:22997`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2015.pdf` p. 89; `PSID/documentation/capture1/q2015.pdf` p. 104; `PSID/documentation/capture1/q2015.pdf` p. 166; `tests/data/track_u2/psid_docs/FAQ_20260813.html:833`; `EV/phase2-20260927/out/u2-adjudicate.md:105` | | `roles:2015.relationship.92` | RESOLVED | Code absent from complete wave format block; refuse if supplied. | `PSID/ind2023er/IND2023ER_formats.sps:22969`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:152` | | `roles:2017.relationship.10` | RESOLVED | Reference Person in 2017; 2015 Reference Person who was mover-out nonresponse by the time of the 2017 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:24449`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2017.relationship.20` | RESOLVED | Legal Spouse in 2017; 2015 Spouse who was mover-out nonresponse by the time of the 2017 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:24450`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | | `roles:2017.relationship.22` | RESOLVED | Partner--cohabitor who has lived with Reference Person for 12 months or more; 2015 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:24451`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | | `roles:2017.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 106). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:24476`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:106` | -| `roles:2017.relationship.90` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. q2017 p. 5 lists '901. Uncooperative Male Spouse' and '902. Uncooperative Female Spouse'; p. 95 routes the spouse income repeat to 'Spouse/Partner (CYAQRTH=201-222, CYFUHU=FU, FUMI)' and the Big OFUM series to 'AQRTH>222'. Codes 901/902 fall in the OFUM gate and outside the spouse loop. The declared legal-spouse annuity and marital-resolution conventions are unchanged. | `PSID/ind2023er/IND2023ER_formats.sps:24477`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2017.pdf` p. 5; `PSID/documentation/capture1/q2017.pdf` p. 95; `EV/phase2-20260927/out/u2-adjudicate.md:107` | -| `roles:2017.relationship.92` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. q2017 p. 5 lists '921. Uncooperative Male Partner' and '922. Uncooperative Female Partner'; p. 95's spouse gate (CYAQRTH=201-222) excludes them and its OFUM gate (AQRTH>222) includes them. I p. 1097: 'Uncooperative partner of Reference Person'. The declared legal-spouse annuity and marital-resolution conventions are unchanged. | `PSID/ind2023er/IND2023ER_formats.sps:24478`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:152`; `PSID/documentation/capture1/q2017.pdf` p. 5; `PSID/documentation/capture1/q2017.pdf` p. 95; `EV/phase2-20260927/out/u2-adjudicate.md:108` | +| `roles:2017.relationship.90` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. q2017 p. 5 lists '901. Uncooperative Male Spouse' and '902. Uncooperative Female Spouse'; p. 95 routes the spouse income repeat to 'Spouse/Partner (CYAQRTH=201-222, CYFUHU=FU, FUMI)' and the Big OFUM series to 'AQRTH>222'. Codes 901/902 fall in the OFUM gate and outside the spouse loop. The declared legal-spouse annuity and marital-resolution conventions are unchanged. Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 90 is an OFUM income role outside the spouse slot in 2017, with the annuity and marital rules recorded here, and registration states the completeness_disclosure. The 2017 code-90 OFUM route is documented; how completely the uncooperative legal spouse's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2017ER pp. 2021-2028), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a). | `PSID/ind2023er/IND2023ER_formats.sps:24477`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2017.pdf` p. 5; `PSID/documentation/capture1/q2017.pdf` p. 95; `EV/phase2-20260927/out/u2-adjudicate.md:107` | +| `roles:2017.relationship.92` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. q2017 p. 5 lists '921. Uncooperative Male Partner' and '922. Uncooperative Female Partner'; p. 95's spouse gate (CYAQRTH=201-222) excludes them and its OFUM gate (AQRTH>222) includes them. I p. 1097: 'Uncooperative partner of Reference Person'. The declared legal-spouse annuity and marital-resolution conventions are unchanged. Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 92 is an OFUM income role outside the spouse slot in 2017, with the annuity and marital rules recorded here, and registration states the completeness_disclosure. The 2017 code-92 OFUM route is documented; how completely the uncooperative partner's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2017ER pp. 2021-2028), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a). | `PSID/ind2023er/IND2023ER_formats.sps:24478`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:152`; `PSID/documentation/capture1/q2017.pdf` p. 5; `PSID/documentation/capture1/q2017.pdf` p. 95; `EV/phase2-20260927/out/u2-adjudicate.md:108` | | `roles:2019.relationship.10` | RESOLVED | Reference Person in 2019; 2017 Reference Person who was mover-out nonresponse by the time of the 2019 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:25976`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2019.relationship.20` | RESOLVED | Legal Spouse in 2019; 2017 Spouse who was mover-out nonresponse by the time of the 2019 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:25977`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | | `roles:2019.relationship.22` | RESOLVED | Partner--cohabitor who has lived with Reference Person for 12 months or more; 2017 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:25978`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 6fbf446ab..2c37744a6 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -408,6 +408,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment-5 review fixes: `pension.json` (the four 5a `ruling_effect` texts and the 2015 checkpoint's resolution) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the P64/P65 blocker row and five rows); `u2_source_registry.py` (the governed routes), `sources.py` (the pension byte pin) and `invented.py` (the 2019–2023 DC items and comments); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py` and `test_u2_employer_dc.py`; and this section. - Delta-review fixes: the six adjudication notes and a d637 record in `individual.json`, and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (a type check), `sources.py` (seven byte pins and two docstrings); `tests/track_u2/test_adjudication_applied.py` and `test_u2_mappings.py`; and this section. The amendment-5 answers also changed `tests/track_u2/test_u2_scripts.py`'s variant expectations. - Max's confirmation of 1a–1c (d745): the specification (lines 3 and 4, §16b's closing paragraph, §17 items 1 and 2, §19 and §20 step 1) and `u1_identity.json`; the master record (the summary, the code-88 blocker row, the pointer to §16b and the d637 section); `tests/track_u2/test_adjudication_applied.py`; and this section. +- Amendment 1a: `roles.json` (the three documented routes and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the code-90 and code-92 blocker rows, the d637 section and three rows); `u2_source_registry.py` (1a's scope) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 6a9b66881..56f104539 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -81,6 +81,9 @@ "specification_section": "16b", } _LATER_WAVES = (2015, 2017, 2019, 2021, 2023) +#: Max confirmed on 2026-10-12 that d637 ratified amendment 1's parts +#: 1a-1c as written (d745; specification section 16b). +_D745 = {"confirmed_by": "d745", "confirmed_on": "2026-10-12"} #: The u2-draft-4 amendments Max ratified on 2026-09-30 (d637; #: specification section 16b) that a registry commit has applied, each #: with its exact scope: the registry and entries it rules, the ruling @@ -95,6 +98,30 @@ #: must record the ruling. Each registry commit adds its amendment here #: (section 16a, "Registry effect of the rulings"). RATIFIED_AMENDMENTS: dict[str, dict[str, Any]] = { + "1a": { + "registry": "roles", + "entries": ( + "2015.relationship.90", + "2017.relationship.90", + "2017.relationship.92", + ), + "ruling": {**_D637, "amendment": "1a", **_D745}, + "disposition": "D", + "fields": { + "status": "RESOLVED", + "action": "documented_rule", + "income_role": "ofum", + "spouse_slot": False, + }, + "absent": ( + "refusal", + "question", + "blocking_dependencies", + "family_unit_exclusion", + ), + "required": ("resolution", "ruling_effect", "completeness_disclosure"), + "markers": ("completeness_disclosure",), + }, "2": { "registry": "income", "entries": tuple( diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 950085a4d..0a02de1bd 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -251,7 +251,9 @@ #: duplicate guard for a combined plan's re-asked items). The third #: round moved seven registries' bytes: the six adjudication notes say #: ``rulings_applied`` records a ruling "where present", and the -#: individual registry records amendment 6's release. +#: individual registry records amendment 6's release. Max's d745 +#: confirmation (2026-10-12) of amendment 1a-1c moved the roles bytes: +#: 1a records the ruling on the documented code-90/92 routes. REGISTRY_SHA256: dict[str, str] = { "income": ( "ddf49639c5678c35d525438b595393d7c55cba00956c23e42f68515ac56446d5" @@ -266,7 +268,7 @@ "bd22e07a8576ea9239818ca40377839c5c3151fffb403f3e5f1ef1549da546cf" ), "roles": ( - "b7521b6b9919e4ca7369c110894377c02ee8ef9e5f51e4637332248eed35290b" + "faf8440ca4c11c61bd0689eb29ea02446eb0aebbb93c388feb625bfdaeeb753c" ), "support": ( "533c39187214fef6626e565362b2d97ff393b8d1f0034ffe365a7c87dadada6d" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 105695ef4..de3f718f7 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -40,8 +40,8 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| | `unit` | 7,498 | -| `artifact` | 4,145 | +| `artifact` | 4,150 | | `integration_psid` | 1,400 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,871** | +| **Total** | **13,876** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 807cae109..21dcf95c2 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -2,7 +2,7 @@ "schema_version": 1, "counts": { "unit": 7498, - "artifact": 4145, + "artifact": 4150, "integration_psid": 1400, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 2da87145a..b600d4efa 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -80,6 +80,7 @@ def quoted_spans(markdown): #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. D637_APPLIED = { + "1a": "D", "2": "A", "3": "A", "4": "D", @@ -622,6 +623,7 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): #: The text fields a d637 ruling writes or keeps. D637_TEXT_FIELDS = ( + "completeness_disclosure", "condition", "resolution", "use_restriction", @@ -634,6 +636,7 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): #: fields, as canonical JSON): no wording moves without this pin moving #: (reviews of 02e3f3e2 and 06fa7d98). D637_TEXT_SHA256 = { + "1a": ("2f4bca660f85b99cac371c970bb02fe3ae3978e5e52c12d661dd3fdd5690b88d"), "2": ("64a67aae69d9d0bd6c6743328e8169304708003845395830ad628a6ee708887d"), "3": ("8d93d501819d981c6605b02fde3ff89d05488738146757bd179a84c02e131598"), "4": ("0b79677a5d1b41a76b17709152bec0d3789d118c75859b9716ea505c3413cd9c"), @@ -644,6 +647,21 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): #: The operative clauses each amendment's entries must state, by field #: (``{wave}`` is the entry's wave). D637_CLAUSES = { + "1a": [ + ( + "ruling_effect", + "ratified amendment 1's parts 1a-1c as written, as Max confirmed " + "on 2026-10-12 (d745; section 16b)", + ), + ("ruling_effect", "Under 1a this documented route governs"), + ("ruling_effect", "an OFUM income role outside the spouse slot"), + ("completeness_disclosure", "OFUM route is documented"), + ("completeness_disclosure", "income was reported is not"), + ( + "completeness_disclosure", + "Registration discloses this (section 16a amendment 1a).", + ), + ], "2": [ ("use_restriction", "Slot metadata only (u2-draft-4 amendment 2)."), ( @@ -1420,9 +1438,11 @@ def test_section_16a_recommendations_and_options(): "checkpoint under 5b, released the 2017\N{EN DASH}2023 P64/P65 " "records under 5a and resolved the 2017 weight ER34651 under " "amendment 6's option 1; the \N{SECTION SIGN}15 fragment itself is " - "materialized under item 9. The 6 TO VERIFY records left are code " - "88's, open until amendment 1c's registry commit lands (Max " - "confirmed 1a\N{EN DASH}1c on 2026-10-12, d745).", + "materialized under item 9. Max confirmed 1a\N{EN DASH}1c on " + "2026-10-12 (d745), and the 1a commit recorded the ruling on the " + "documented 2015 code-90 and 2017 code-90 and code-92 routes. The 6 " + "TO VERIFY records left are code 88's, open until amendment 1c's " + "registry commit lands.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " @@ -1814,6 +1834,13 @@ def test_released_dependencies(documents): def test_master_record_matches_registries(documents): text = MASTER.read_text(encoding="utf-8") + # One section per ruling's registry commits, each listing every + # applied d637 amendment once. + assert text.count("\n## Max's ruling d637 applied ") == 1 + for amendment in registry.RATIFIED_AMENDMENTS: + assert ( + len(re.findall(rf"^\| {amendment}: ", text, re.M)) == 1 + ), amendment rows = dict( re.findall(r"^\| `([a-z]+:[^`]+)` \| ([A-Z ]+?) \| ", text, re.M) ) diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index f80ff78f0..08cf5a2ca 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -998,6 +998,37 @@ def _copy_ruling(document, source, target): accepted_plan_types=[7, 1] ), ), + # Amendment 1a (d637, confirmed by d745). + "a1a_ruling_on_2013_code_90": ( + "roles", + lambda d: _copy_ruling( + d, "2015.relationship.90", "2013.relationship.90" + ), + ), + "a1a_confirmation_dropped": ( + "roles", + lambda d: _entry(d, "2017.relationship.92")["ruling"].pop( + "confirmed_by" + ), + ), + "a1a_disclosure_dropped": ( + "roles", + lambda d: _entry(d, "2017.relationship.90").pop( + "completeness_disclosure" + ), + ), + "a1a_into_the_slot": ( + "roles", + lambda d: _entry(d, "2015.relationship.90").update(spouse_slot=True), + ), + "a1a_disclosure_on_code_20": ( + "roles", + lambda d: _entry(d, "2017.relationship.20").update( + completeness_disclosure=_entry(d, "2017.relationship.90")[ + "completeness_disclosure" + ] + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 1414fc07d..16107f7a6 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -280,6 +280,8 @@ def test_d1090_moved_only_the_ruled_gate_fields(): #: entry changes, by amendment (prose keys excluded: ``question`` is #: prose, so its move to ``superseded_question`` shows as the new key). D637_RULED_FIELDS = { + # Amendment 1a's three D items keep their documentary resolution. + "1a": {"ruling_effect", "completeness_disclosure", "ruling"}, "2": { "status", "usage", @@ -349,7 +351,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "wealth": {"rulings_applied", "independent_adjudication", "status_scope"}, "pension": {"rulings_applied", "independent_adjudication"}, "individual": {"rulings_applied"}, - "roles": {"independent_adjudication"}, + "roles": {"rulings_applied", "independent_adjudication"}, "support": {"independent_adjudication"}, "weights": {"rulings_applied", "independent_adjudication"}, } @@ -380,6 +382,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "pension": ["5b", "5a"], "weights": ["6"], "individual": ["6"], + "roles": ["1a"], } From f9951402cc61b05b747070263b9d553378dec6ac Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 10:05:29 -0400 Subject: [PATCH 16/21] Apply d637 amendment 1b to the U2 roles registry Max's ruling d637 ratified u2-draft-4 amendment 1's parts 1a-1c as written, as he confirmed on 2026-10-12 (d745; section 16b). This is the 1b registry commit: 2013 keeps the inherited meanings. - roles.json: 2013.relationship.90 ('Legal husband of Head', resolved in milestone 1 with no adjudication record) records the ruling (d637, amendment 1b, confirmed_by d745) and a ruling_effect: code 90 keeps the section 3 rules of lines 147, 150, 156 and 175, an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. The d637 rulings_applied record lists 1b. - u2_source_registry: 1b's scope, the first with no adjudication disposition (its entry must carry no adjudication record); sources: the roles byte pin. - No role rule, count or loader blocker moves (12, all code 88's). - Master record: the 2013 code-90 row and the d637 section. Section 17 item 2 and section 19 record it; the spec is re-pinned. Tests: the d637 differential, the text pins and clauses, and three new scope mutations cover 1b (meaning changed, an invented adjudication record, the ruling copied to 2013 code 20). Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/roles.json | 14 ++++++-- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 3 +- docs/design/u2_m1_source_adjudication.md | 3 +- docs/design/u2_m1b_exposure.md | 1 + .../data/u2_source_registry.py | 32 ++++++++++++++++++- .../uniform_cut_track_u2/sources.py | 5 +-- tests/README-tiers.md | 4 +-- tests/tier_counts.json | 2 +- tests/track_u2/test_adjudication_applied.py | 29 +++++++++++++---- tests/track_u2/test_source_registries.py | 23 +++++++++++++ tests/track_u2/test_u2_mappings.py | 3 +- 12 files changed, 103 insertions(+), 18 deletions(-) diff --git a/data/external/track_u2/roles.json b/data/external/track_u2/roles.json index eebaf171b..4b2f673f3 100644 --- a/data/external/track_u2/roles.json +++ b/data/external/track_u2/roles.json @@ -281,6 +281,15 @@ "line": 154 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:154 concerns the later-wave code-90 definition; lines 147, 150, 156 and 175 state the code-90 role, the 2013 meanings, the annuity pairing and the 2013 label test. The 2013 label remains 'Legal husband of Head' (IND2023ER_codebook.pdf p. 916)." + }, + "ruling_effect": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1b 2013 keeps the inherited meanings: code 90 is 'Legal husband of Head' (IND2023ER codebook p. 916), with the rules of section 3 lines 147, 150, 156 and 175: an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1b", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" } }, { @@ -1926,9 +1935,10 @@ "date": "2026-09-30", "specification_section": "16b", "amendments": [ - "1a" + "1a", + "1b" ], - "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1; on 2026-10-12 Max confirmed that it ratified amendment 1's parts 1a-1c as written too (d745; section 16b). Applied here, each in its own reviewed commit: 1a records the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 OFUM routes, with the completeness disclosure registration states. Each ruled entry keeps its adjudication record, if any, and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1; on 2026-10-12 Max confirmed that it ratified amendment 1's parts 1a-1c as written too (d745; section 16b). Applied here, each in its own reviewed commit: 1a records the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 OFUM routes, with the completeness disclosure registration states; 1b records the ruling on 2013's inherited code-90 meaning. Each ruled entry keeps its adjudication record, if any, and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 965d847b9..6eeeb81bd 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "3bb021c5570e6814a8b34be8698488244e8d25f83a7b196d7e66d4fc205ebaad", + "sha256": "9c27dbf4577f960b08fdc10d8ba804c2bf26d83ff434c748a15d7f5592560e94", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index 3a2094690..fafcd59a8 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1656,7 +1656,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. **Confirmed 2026-10-12 (d745; §16b):** amendment 1's parts 1a–1c are ratified as written. This item is closed. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. Max confirmed 1a–1c on 2026-10-12 (d745), and the 1a commit recorded the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 routes. The 6 TO VERIFY records left are code 88's, open until amendment 1c's registry commit lands. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. Max confirmed 1a–1c on 2026-10-12 (d745), and the 1a and 1b commits recorded the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 routes and on 2013's inherited code-90 meaning. The 6 TO VERIFY records left are code 88's, open until amendment 1c's registry commit lands. 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1866,6 +1866,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Changed no line that a registry cites and no line before §16a other than lines 3 and 4. The specification pin in `data/external/track_u2/u1_identity.json` moves to the new hash; its `version` stays `u2-draft-4`. - The registry effects (§16a: "Record 1a–1c") follow in their own reviewed commits. - Amendment 1a: the three documented routes (`roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92`, adjudication D) record the ruling, confirmed by d745, and each gains the completeness disclosure 1a requires at registration: the OFUM route is documented, but not how completely the income of a person about whom answers were withheld was reported. No role rule, count or loader blocker moves. +- Amendment 1b: `roles:2013.relationship.90` ('Legal husband of Head', resolved in milestone 1 with no adjudication record) records the ruling, confirmed by d745: 2013 keeps the inherited meanings, an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. No role rule, count or loader blocker moves. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index a9a58e812..abd6746b9 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -81,6 +81,7 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 5a: the 2017–2023 previous-employer route | 4 + 40 released | `pension:{2017,2019,2021,2023}.route.inherited_route_amendment` keep their documentary resolution (adjudication D) and now apply: each drops its `refuse_pending_amendment_5_ruling` action, keeps its refusal as `superseded_refusal` and gains a `ruling_effect`. The 40 entries that depended on them (each wave's P64 and P65 items of both persons' two previous plans, `previous_dc_only` and `duplicate_and_off_route`) are released, none with a dependency left. The registry gate now applies every wave's employer-DC route, each equal to the declared route | | 6: the 2017 cross-sectional weight | 1 + 1 released | `weights:2017.cross_section_weight` is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration, and a `registration_disclosure` listing what PSID has documented (its identity, the ACS one-year PUMS control and its two named exclusions, the calibration-variable selection approach, some calibration controls, the 2017 Immigrant sample's base weights as formed for the 2019 weights) and has not published (that sample's starting weights and joint-family integration in the revised 2017 weight, and the complete 2017 raking specification), with the coverage bound the control's exclusions set. `individual:2017.weight` is released | | 1a: documented routes (confirmed by d745) | 3 | `roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92` keep their documentary resolution (adjudication D) and record the ruling (`ruling` with `confirmed_by` d745) and a `ruling_effect`; each gains the `completeness_disclosure` registration states: the OFUM route is documented, but not how completely a person about whom answers were withheld had their income reported. No role rule, count or loader blocker moves | +| 1b: 2013's inherited meanings (confirmed by d745) | 1 | `roles:2013.relationship.90` ('Legal husband of Head', resolved in milestone 1 with no adjudication record) records the ruling and a `ruling_effect`: 2013 keeps the inherited meanings, an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. No role rule, count or loader blocker moves | The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, whose registry commit follows): each wave's code-88 role entry is TO VERIFY and refuses its substantive routing. @@ -1797,7 +1798,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `roles:2013.relationship.20` | RESOLVED | Legal Wife in 2013; 2011 Wife who was mover-out nonresponse by the time of the 2013 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:21919`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 6 | | `roles:2013.relationship.22` | RESOLVED | "Wife"--female cohabitor who has lived with Head for 12 months or more; 2011 "Wife" who was mover-out nonresponse...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:21920`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 6 | | `roles:2013.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 102). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:21945`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:102` | -| `roles:2013.relationship.90` | RESOLVED | Legal husband of Head; declared income role ofum; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. Citation corrected (amendment 7, EV/phase2-20260927/out/u2-adjudicate.md line 59): docs/design/boomers2004_1946_55_comparison.md:154 concerns the later-wave code-90 definition; lines 147, 150, 156 and 175 state the code-90 role, the 2013 meanings, the annuity pairing and the 2013 label test. The 2013 label remains 'Legal husband of Head' (IND2023ER_codebook.pdf p. 916). | `PSID/ind2023er/IND2023ER_formats.sps:21946`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:147`; `docs/design/boomers2004_1946_55_comparison.md:150`; `docs/design/boomers2004_1946_55_comparison.md:156`; `docs/design/boomers2004_1946_55_comparison.md:175`; `EV/phase2-20260927/out/u2-adjudicate.md:59` | +| `roles:2013.relationship.90` | RESOLVED | Legal husband of Head; declared income role ofum; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. Citation corrected (amendment 7, EV/phase2-20260927/out/u2-adjudicate.md line 59): docs/design/boomers2004_1946_55_comparison.md:154 concerns the later-wave code-90 definition; lines 147, 150, 156 and 175 state the code-90 role, the 2013 meanings, the annuity pairing and the 2013 label test. The 2013 label remains 'Legal husband of Head' (IND2023ER_codebook.pdf p. 916). Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1b 2013 keeps the inherited meanings: code 90 is 'Legal husband of Head' (IND2023ER codebook p. 916), with the rules of section 3 lines 147, 150, 156 and 175: an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. | `PSID/ind2023er/IND2023ER_formats.sps:21946`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:147`; `docs/design/boomers2004_1946_55_comparison.md:150`; `docs/design/boomers2004_1946_55_comparison.md:156`; `docs/design/boomers2004_1946_55_comparison.md:175`; `EV/phase2-20260927/out/u2-adjudicate.md:59` | | `roles:2013.relationship.92` | RESOLVED | Code absent from complete wave format block; refuse if supplied. | `PSID/ind2023er/IND2023ER_formats.sps:21918`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:152` | | `roles:2015.relationship.10` | RESOLVED | Head in 2015; 2013 Head who was mover-out nonresponse by the time of the 2015 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:22969`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2015.relationship.20` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1d-2, ratified as an explicit amendment to draft-3 line 160: 2015 code 20 occupies the spouse slot as documented, and every 2015 family-unit observation that contains a person recorded male (ER32000 = 1) with 2015 relationship code 20 is excluded from every row under the named disposition male code 20 in 2015 family unit. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. This replaces the adjudication's refusal, kept as superseded_refusal; family_unit_exclusion.condition records that the exclusion concerns sex=1. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md section 1): official documentation routes every documented kind of male legal spouse in 2015 away from code 20. Through 2015 the husband of a heterosexual couple is Head (UserGuide2017 PDF p. 13; FAQ question 73), a husband who is not Head is 'Husband of Head' code 90 and 'was asked the same questions as an Ofum' (FAQ question 74), and 2015 same-sex partners are code 98 'Other nonrelatives (includes same-sex partners ...)' flagged by ER34304 (IND2023ER_codebook p. 978; UserGuide2015 PDF p. 33; UserGuide2017 PDF p. 35). The 2015 Section J qualifier classes values 102-222 as female, where the 2017 qualifier classes 101, 201 and 221 as male (research record section 1, item 11; q2015 p. 190; q2017 p. 184). No document states that code 20 is never male in 2015, and FAQ question 70 says generally that from 2015 a Spouse/Partner couple 'can consist of heterosexual or same sex couples', so the documents do not settle how a male Head's legally married same-sex spouse was coded. The 2015 'RTH Lookup List' named for CYAQRTH (q2015 p. 5) is absent from the staged documentation. PSID answered on 2026-10-07 that no male should have code 20 in 2015 (research record section 1), and Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:22970`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `PSID/documentation/capture1/q2015.pdf` p. 89; `PSID/documentation/capture1/q2015.pdf` p. 166; `EV/phase2-20260927/out/u2-adjudicate.md:103` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 2c37744a6..2a0ac53d5 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -409,6 +409,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Delta-review fixes: the six adjudication notes and a d637 record in `individual.json`, and `u1_identity.json`; the specification's §19; `u2_source_registry.py` (a type check), `sources.py` (seven byte pins and two docstrings); `tests/track_u2/test_adjudication_applied.py` and `test_u2_mappings.py`; and this section. The amendment-5 answers also changed `tests/track_u2/test_u2_scripts.py`'s variant expectations. - Max's confirmation of 1a–1c (d745): the specification (lines 3 and 4, §16b's closing paragraph, §17 items 1 and 2, §19 and §20 step 1) and `u1_identity.json`; the master record (the summary, the code-88 blocker row, the pointer to §16b and the d637 section); `tests/track_u2/test_adjudication_applied.py`; and this section. - Amendment 1a: `roles.json` (the three documented routes and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the code-90 and code-92 blocker rows, the d637 section and three rows); `u2_source_registry.py` (1a's scope) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. +- Amendment 1b: `roles.json` (2013's code-90 record and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the 2013 code-90 row and the d637 section); `u2_source_registry.py` (1b's scope, the first with no adjudication disposition) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 56f104539..43635e8b6 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -122,6 +122,31 @@ "required": ("resolution", "ruling_effect", "completeness_disclosure"), "markers": ("completeness_disclosure",), }, + # 2013's inherited code-90 meaning was RESOLVED in milestone 1 and + # carries no adjudication record (disposition None). + "1b": { + "registry": "roles", + "entries": ("2013.relationship.90",), + "ruling": {**_D637, "amendment": "1b", **_D745}, + "disposition": None, + "fields": { + "status": "RESOLVED", + "label": "Legal husband of Head", + "action": "documented_rule", + "income_role": "ofum", + "spouse_slot": False, + "legal_spouse_annuity": True, + "marital_resolution": True, + }, + "absent": ( + "refusal", + "question", + "blocking_dependencies", + "family_unit_exclusion", + ), + "required": ("ruling_effect",), + "markers": (), + }, "2": { "registry": "income", "entries": tuple( @@ -478,7 +503,12 @@ def _ratified_amendment(entry: dict[str, Any], name: str) -> None: f"A d637 ruling applies only unblocked, for {entry['id']}" ) record = entry.get("adjudication") - if ( + if scope["disposition"] is None: + if record is not None: + raise SourceAdjudicationError( + f"Unexpected adjudication record for {entry['id']}" + ) + elif ( not isinstance(record, dict) or record.get("disposition") != scope["disposition"] or not _same(record.get("amendment"), int(amendment[0])) diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 0a02de1bd..57171d019 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -253,7 +253,8 @@ #: ``rulings_applied`` records a ruling "where present", and the #: individual registry records amendment 6's release. Max's d745 #: confirmation (2026-10-12) of amendment 1a-1c moved the roles bytes: -#: 1a records the ruling on the documented code-90/92 routes. +#: 1a records the ruling on the documented code-90/92 routes, 1b on 2013's +#: inherited code-90 meaning. REGISTRY_SHA256: dict[str, str] = { "income": ( "ddf49639c5678c35d525438b595393d7c55cba00956c23e42f68515ac56446d5" @@ -268,7 +269,7 @@ "bd22e07a8576ea9239818ca40377839c5c3151fffb403f3e5f1ef1549da546cf" ), "roles": ( - "faf8440ca4c11c61bd0689eb29ea02446eb0aebbb93c388feb625bfdaeeb753c" + "3a2924122555a612d976f9dbd3e4d764f133fede0467a65b2c9eb67932e60c51" ), "support": ( "533c39187214fef6626e565362b2d97ff393b8d1f0034ffe365a7c87dadada6d" diff --git a/tests/README-tiers.md b/tests/README-tiers.md index de3f718f7..be57615ea 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -40,8 +40,8 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| | `unit` | 7,498 | -| `artifact` | 4,150 | +| `artifact` | 4,153 | | `integration_psid` | 1,400 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,876** | +| **Total** | **13,879** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 21dcf95c2..07830b191 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -2,7 +2,7 @@ "schema_version": 1, "counts": { "unit": 7498, - "artifact": 4150, + "artifact": 4153, "integration_psid": 1400, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index b600d4efa..139932702 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -81,6 +81,7 @@ def quoted_spans(markdown): #: the adjudication disposition its entries carry. D637_APPLIED = { "1a": "D", + "1b": None, "2": "A", "3": "A", "4": "D", @@ -520,9 +521,11 @@ def test_d637_rulings_are_recorded_on_their_entries(documents, monkeypatch): ruling["specification_section"], ruling["amendment"], ) == ("d637", "2026-09-30", "16b", amendment) - record = entry["adjudication"] + # 2013's code-90 meaning (1b) has no adjudication record. + record = entry.get("adjudication", {"disposition": None}) assert record["disposition"] == D637_APPLIED[amendment], key - assert record["amendment"] == number, key + if record["disposition"] is not None: + assert record["amendment"] == number, key assert entry["status"] == "RESOLVED", key assert any( "d637" in entry.get(field, "") @@ -637,6 +640,7 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): #: (reviews of 02e3f3e2 and 06fa7d98). D637_TEXT_SHA256 = { "1a": ("2f4bca660f85b99cac371c970bb02fe3ae3978e5e52c12d661dd3fdd5690b88d"), + "1b": ("ca251770719feb29812705e2ef7d3868ec6f95a925806e40792f1fda63c69be8"), "2": ("64a67aae69d9d0bd6c6743328e8169304708003845395830ad628a6ee708887d"), "3": ("8d93d501819d981c6605b02fde3ff89d05488738146757bd179a84c02e131598"), "4": ("0b79677a5d1b41a76b17709152bec0d3789d118c75859b9716ea505c3413cd9c"), @@ -662,6 +666,19 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): "Registration discloses this (section 16a amendment 1a).", ), ], + "1b": [ + ( + "ruling_effect", + "ratified amendment 1's parts 1a-1c as written, as Max confirmed " + "on 2026-10-12 (d745; section 16b)", + ), + ("ruling_effect", "Under 1b 2013 keeps the inherited meanings"), + ("ruling_effect", "'Legal husband of Head'"), + ("ruling_effect", "section 3 lines 147, 150, 156 and 175"), + ("ruling_effect", "an OFUM income role outside the spouse slot"), + ("ruling_effect", "the co-resident legal-spouse annuity life"), + ("ruling_effect", "relationship-based marital resolution"), + ], "2": [ ("use_restriction", "Slot metadata only (u2-draft-4 amendment 2)."), ( @@ -1439,10 +1456,10 @@ def test_section_16a_recommendations_and_options(): "records under 5a and resolved the 2017 weight ER34651 under " "amendment 6's option 1; the \N{SECTION SIGN}15 fragment itself is " "materialized under item 9. Max confirmed 1a\N{EN DASH}1c on " - "2026-10-12 (d745), and the 1a commit recorded the ruling on the " - "documented 2015 code-90 and 2017 code-90 and code-92 routes. The 6 " - "TO VERIFY records left are code 88's, open until amendment 1c's " - "registry commit lands.", + "2026-10-12 (d745), and the 1a and 1b commits recorded the ruling on " + "the documented 2015 code-90 and 2017 code-90 and code-92 routes and " + "on 2013's inherited code-90 meaning. The 6 TO VERIFY records left " + "are code 88's, open until amendment 1c's registry commit lands.", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 08cf5a2ca..985dd8320 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -1029,6 +1029,29 @@ def _copy_ruling(document, source, target): ] ), ), + "a1b_meaning_changed": ( + "roles", + lambda d: _entry(d, "2013.relationship.90").update( + legal_spouse_annuity=False + ), + ), + "a1b_adjudication_invented": ( + "roles", + lambda d: _entry(d, "2013.relationship.90").update( + adjudication={ + "source": "EV/phase2-20260927/out/u2-adjudicate.md", + "line": 59, + "disposition": "D", + "amendment": 1, + } + ), + ), + "a1b_ruling_on_2013_code_20": ( + "roles", + lambda d: _copy_ruling( + d, "2013.relationship.90", "2013.relationship.20" + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 16107f7a6..fe530f2ed 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -282,6 +282,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): D637_RULED_FIELDS = { # Amendment 1a's three D items keep their documentary resolution. "1a": {"ruling_effect", "completeness_disclosure", "ruling"}, + "1b": {"ruling_effect", "ruling"}, "2": { "status", "usage", @@ -382,7 +383,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "pension": ["5b", "5a"], "weights": ["6"], "individual": ["6"], - "roles": ["1a"], + "roles": ["1a", "1b"], } From edff85457a42477a75e3e141206f2c711e9ee569 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 10:22:47 -0400 Subject: [PATCH 17/21] Follow d637 amendment 1c in the declared U2 role rules Max's ruling d637 ratified u2-draft-4 amendment 1's parts 1a-1c as written, as he confirmed on 2026-10-12 (d745; section 16b). Amendment 1c gives code 88, the first-year cohabitor, a convention in every wave: an OFUM income role on the family basis (its SSI in section 8's OFUM unit), not the spouse slot, no legal-spouse annuity life, no relationship-based marital resolution, its recorded legal marital history, and the inherited administrative birth support. - sources: the declared code-88 rule (invented data only) is that convention, RoleRule(wave, 88, True, "ofum", False, False, False, True), in place of the refusal; the role table and the docstrings of the module, SourceGate, declared_role_rules and RoleContext say so. The registry context still refuses code 88: the roles registry leaves it TO VERIFY until the 1c registry commit. - cohort and estimator docstrings: code 88 has the OFUM role and never resolves a marital history. - invented: the two code-88 variants' notes (the variants are unchanged). - dry run: each role context's code-88 check builds U0 with and without the cohabitor and records whether observations and dispositions are equal, or the refusal; the summary line follows. - Section 19 records it; the spec is re-pinned. The dated milestone-2 report is left as its record of milestone 2. Tests: the declared code-88 rule is pinned exactly; a code-88 cohabitor added to a family that supplies observations changes no observation or disposition under the declared context and refuses under the registry context; in a B2-excluded unit the exclusion still applies, counted. The dry run's code-88 checks and variant outcomes follow. No registry entry changes; the loader still refuses on 12 blockers. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1b_exposure.md | 1 + scripts/track_u2_dry_run.py | 60 ++++++++++++---- .../uniform_cut_track_u2/cohort.py | 15 ++-- .../uniform_cut_track_u2/estimator.py | 2 +- .../uniform_cut_track_u2/invented.py | 16 +++-- .../uniform_cut_track_u2/sources.py | 60 +++++++++------- tests/track_u2/test_u2_roles.py | 71 +++++++++++++------ tests/track_u2/test_u2_scripts.py | 26 +++++-- 10 files changed, 172 insertions(+), 82 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 6eeeb81bd..e8b7918bd 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "9c27dbf4577f960b08fdc10d8ba804c2bf26d83ff434c748a15d7f5592560e94", + "sha256": "c9862af57c16af27ade448d5fb4e811dd18cdf5a55cb0a2f75c9580ad661ebcc", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index fafcd59a8..ae14f5b99 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1867,6 +1867,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - The registry effects (§16a: "Record 1a–1c") follow in their own reviewed commits. - Amendment 1a: the three documented routes (`roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92`, adjudication D) record the ruling, confirmed by d745, and each gains the completeness disclosure 1a requires at registration: the OFUM route is documented, but not how completely the income of a person about whom answers were withheld was reported. No role rule, count or loader blocker moves. - Amendment 1b: `roles:2013.relationship.90` ('Legal husband of Head', resolved in milestone 1 with no adjudication record) records the ruling, confirmed by d745: 2013 keeps the inherited meanings, an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. No role rule, count or loader blocker moves. +- Amendment 1c, builder: the declared role rules (invented data only) give code 88 amendment 1c's convention: the OFUM income role on the family basis outside the spouse slot, no legal-spouse annuity life, no relationship-based marital resolution and the inherited administrative birth support. A code-88 first-year cohabitor added to an invented family changes no observation or disposition. Until the 1c registry commit, the registry context still refuses code 88 and the loader still refuses on its 12 blockers. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 2a0ac53d5..9805fc78c 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -410,6 +410,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Max's confirmation of 1a–1c (d745): the specification (lines 3 and 4, §16b's closing paragraph, §17 items 1 and 2, §19 and §20 step 1) and `u1_identity.json`; the master record (the summary, the code-88 blocker row, the pointer to §16b and the d637 section); `tests/track_u2/test_adjudication_applied.py`; and this section. - Amendment 1a: `roles.json` (the three documented routes and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the code-90 and code-92 blocker rows, the d637 section and three rows); `u2_source_registry.py` (1a's scope) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 1b: `roles.json` (2013's code-90 record and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the 2013 code-90 row and the d637 section); `u2_source_registry.py` (1b's scope, the first with no adjudication disposition) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. +- Amendment 1c, builder: `sources.py` (the declared code-88 rule and its docstrings), `cohort.py` and `estimator.py` (docstrings), `invented.py` (the two code-88 variant notes) and `scripts/track_u2_dry_run.py` (the code-88 checks); `u1_identity.json`; the specification's §19; `tests/track_u2/test_u2_roles.py` and `test_u2_scripts.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/scripts/track_u2_dry_run.py b/scripts/track_u2_dry_run.py index 81b1c06b6..410f8bb8e 100644 --- a/scripts/track_u2_dry_run.py +++ b/scripts/track_u2_dry_run.py @@ -20,8 +20,11 @@ seven/eight-asset WEALTH1 identities, every employer-DC route); * under the committed registries, the source gate refuses each wave the milestone-1 record leaves open, the registry role context refuses the - TO VERIFY relationship codes, code 88 refuses under every context, and - the loader's preflight refuses before any PSID file is opened; + TO VERIFY relationship codes (code 88 until amendment 1c's registry + commit), the declared context builds a code-88 first-year cohabitor's + family under amendment 1c's OFUM convention with every observation + unchanged, and the loader's preflight refuses before any PSID file is + opened; * the registered runner refuses these invented inputs and parameters; * each tested route from other data to the invented declared rules and routes refuses: relabelled or hand-copied invented provenance, frames @@ -612,6 +615,32 @@ def _registry_context_check( return out +def _code_88_check( + inputs: cohort.U2Inputs, + code88: cohort.U2Inputs, + context: sources.RoleContext, +) -> dict[str, Any]: + """Build U0 with and without the code-88 first-year cohabitor under + ``context``: equal under amendment 1c's OFUM convention, refused + where the context leaves code 88 open.""" + + try: + with_88 = cohort.build_u2_cohort(code88, role_context=context) + except Exception as error: # recorded, not swallowed + return { + "refused": True, + "error": type(error).__name__, + "message": str(error)[:600], + } + base = cohort.build_u2_cohort(inputs, role_context=context) + return { + "refused": False, + "rule": context.rule(2019, 88).as_dict(), + "observations_equal": with_88.observations.equals(base.observations), + "dispositions_equal": with_88.dispositions.equals(base.dispositions), + } + + def _invented_gate_refusals( inputs: cohort.U2Inputs, params: parameters.U2Parameters, @@ -852,13 +881,15 @@ def checks( "registry_context_on_invented_inputs": _registry_context_check( inputs, births, registry_roles, declared ), - "code_88_under_registry_context": _refusal( - lambda: cohort.build_u2_cohort( - code88, role_context=registry_roles, births=births - ) + # Amendment 1c (d637, confirmed by d745): a code-88 + # first-year cohabitor has the OFUM role outside the spouse + # slot, so adding one changes no observation or disposition; + # a context that leaves code 88 open refuses. + "code_88_under_registry_context": _code_88_check( + inputs, code88, registry_roles ), - "code_88_under_declared_context": _refusal( - lambda: cohort.build_u2_cohort(code88, role_context=declared) + "code_88_under_declared_context": _code_88_check( + inputs, code88, declared ), "declared_context_on_non_invented_inputs": _refusal( lambda: cohort.build_u2_cohort( @@ -1138,11 +1169,14 @@ def results_markdown(result: dict[str, Any]) -> str: .get("family_unit_exclusions", {}) .items() ) - + ". Refusals: code 88 under the registry context " - f"{role['code_88_under_registry_context']['refused']};" - " code 88 under the declared context " - f"{role['code_88_under_declared_context']['refused']};" - " declared context on non-invented inputs " + + ". Code 88 (amendment 1c's OFUM convention): refused under the " + "registry context " + f"{role['code_88_under_registry_context']['refused']}, under the " + "declared context " + f"{role['code_88_under_declared_context']['refused']} (U0 " + "unchanged: " + f"{role['code_88_under_declared_context'].get('observations_equal')}" + "). Refusals: declared context on non-invented inputs " f"{role['declared_context_on_non_invented_inputs']['refused']}.", "- Loader preflight refuses before any PSID file is opened: " f"{check['loader_preflight']['refused']}.", diff --git a/src/populace_dynamics/uniform_cut_track_u2/cohort.py b/src/populace_dynamics/uniform_cut_track_u2/cohort.py index 1c106196f..e3703b234 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/cohort.py +++ b/src/populace_dynamics/uniform_cut_track_u2/cohort.py @@ -43,13 +43,14 @@ family_unit_exclusion`). An observation already disposed of (not in the file, not present, zero weight, sex unknown) keeps that disposition. The income role follows the income slot: 10 head, 20 and 22 the spouse -slot (``wife``, irrespective of sex), 90 and 92 (2013-2017) and every -other code OFUM. The primary annuity lives are the unique code-10 person -and the unique co-resident legal spouse (code 20 or 90), each with -recorded sex and derived income-year age; a uniquely paired head and -legal spouse resolve an otherwise unresolved marital history as married, -each naming the other. Code 22 and 92 never resolve; a missing or ambiguous pairing -stays unresolved and is counted. +slot (``wife``, irrespective of sex), 88 (the first-year cohabitor, +amendment 1c), 90 and 92 (2013-2017) and every other code OFUM. The +primary annuity lives are the unique code-10 person and the unique +co-resident legal spouse (code 20 or 90), each with recorded sex and +derived income-year age; a uniquely paired head and legal spouse resolve +an otherwise unresolved marital history as married, each naming the +other. Codes 22, 88 and 92 never resolve; a missing or ambiguous +pairing stays unresolved and is counted. **Weights, design and units.** Observation weight = the reporting wave's core/immigrant cross-section weight times the planned multiplier; no diff --git a/src/populace_dynamics/uniform_cut_track_u2/estimator.py b/src/populace_dynamics/uniform_cut_track_u2/estimator.py index 54db97745..9e635ec75 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/estimator.py +++ b/src/populace_dynamics/uniform_cut_track_u2/estimator.py @@ -27,7 +27,7 @@ registry context, and the invented declared context runs only on invented rows. The income units follow the section 3 income slots already on the rows: ``member_role`` (head, the spouse slot ``wife``, - or ``ofum``, which includes codes 90 and 92), ``wife_present`` (the + or ``ofum``, which includes codes 88, 90 and 92), ``wife_present`` (the designated spouse income slot) and the head/legal-spouse annuity lives. * **Provenance.** As U1: ``invented`` for tests and dry runs; diff --git a/src/populace_dynamics/uniform_cut_track_u2/invented.py b/src/populace_dynamics/uniform_cut_track_u2/invented.py index 0d26900df..6a3bd1301 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/invented.py +++ b/src/populace_dynamics/uniform_cut_track_u2/invented.py @@ -1271,7 +1271,7 @@ def check_invented_inputs(inputs: cohort.U2Inputs) -> dict[str, Any]: def with_code_88_cohabitor(inputs: cohort.U2Inputs) -> cohort.U2Inputs: - """The invented inputs plus a code-88 first-year cohabitor (refusal). + """The invented inputs plus a code-88 first-year cohabitor (OFUM). The ``code_88_first_year_cohabitor`` variant of ``inputs``' seed. """ @@ -1548,8 +1548,11 @@ def _sex_unknown_target_in_excluded_unit_2019( _VARIANT_NOTES: dict[str, str] = { "code_88_first_year_cohabitor": ( "a code-88 first-year cohabitor joins the first 2019 family; " - "section 3 declares no substantive rule for code 88, so every " - "role context refuses (refusal case)" + "under amendment 1c's convention (d637, confirmed by d745) it " + "has the OFUM income role outside the spouse slot, is no annuity " + "life and resolves no marital history, so every observation and " + "disposition is unchanged; a role context that leaves code 88 " + "open refuses" ), "missing_family_record_2019": ( "the first 2019 family-file income record is dropped; an " @@ -1589,9 +1592,10 @@ def _sex_unknown_target_in_excluded_unit_2019( ), "code_88_in_excluded_unit_2019": ( "a code-88 first-year cohabitor joins the 2019 family of U0's " - "first observation that the section 16c B2 exclusion removes: the " - "code-88 refusal still refuses, never hidden by the exclusion " - "(refusal case)" + "first observation that the section 16c B2 exclusion removes: " + "under amendment 1c it has the OFUM role, and the unit stays " + "excluded, counted under its disposition; a role context that " + "leaves code 88 open refuses, never hidden by the exclusion" ), "zero_weight_target_in_excluded_unit_2015": ( "the target of U0's first 2015 observation that the B1 exclusion " diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 57171d019..963be597f 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -38,9 +38,8 @@ :class:`FieldSpec` and :class:`DcRoute` are plain value objects, so a caller who hand-assembles them with the declared types is not stopped; no U2 entry point does so. - Where the specification declares nothing (code 88's substantive - routing, the historical combined spouse-retirement crosswalk) it - refuses too. + Where the specification declares nothing (the historical combined + spouse-retirement crosswalk) it refuses too. The role rules follow the same split in a :class:`RoleContext`, which is bound to its kind: a ``registry`` context holds exactly the committed @@ -60,8 +59,9 @@ the registered path builds every object it uses itself and rechecks the pinned identities before any PSID record is read. -**Role rules** (section 3 relationship-code amendment, with the two -family-unit exclusions of section 16c): +**Role rules** (section 3 relationship-code amendment, with amendment +1c's code-88 convention and the two family-unit exclusions of section +16c): ==== ============================== ============== =================== Code Income role and spouse slot Annuity life Marital resolution @@ -69,14 +69,18 @@ 10 head / reference person head (is the head) 20 spouse slot, either sex legal spouse yes 22 spouse slot (cohabitor) no no -88 TO VERIFY: refuses (no declared substantive rule; birth support only) +88 OFUM (not the slot) no never 90 OFUM, 2013-2017 (not the slot) legal spouse yes 92 OFUM, 2017 only no never ==== ============================== ============== =================== This is the section 3 table (``u2-draft-3``'s), amended where Max's -d1090 ruling (2026-10-10, section 16c) differs from it. A 2015 family -unit that holds a code-20 person recorded male (blocker B1), and a +rulings differ from it. Under amendment 1c (d637, 2026-09-30, +section 16b; confirmed by d745 on 2026-10-12) code 88, the first-year +cohabitor, has the OFUM income role outside the spouse slot, is no +annuity life, never resolves a marital history by its code and keeps +its administrative birth support. Under d1090 (2026-10-10, section +16c) a 2015 family unit that holds a code-20 person recorded male (blocker B1), and a 2019-2023 family unit that holds a code-90 or code-92 person (blocker B2), supplies no observation to any row: each observation it would have supplied is counted under the named disposition @@ -87,7 +91,9 @@ 2013-2017 code 90 and 2017 code 92 (adjudication disposition D) and, by its d1090 commit, the same exclusions for 2015 code 20 and 2019-2023 codes 90 and 92, which the adjudication had refused (disposition F); the -registry gate follows the registry. +registry gate follows the registry. The registry leaves code 88 TO +VERIFY until amendment 1c's registry commit, so until then the +registry context refuses it. Any other in-family code keeps the inherited OFUM income role with no spouse status. Legacy identifiers ``wife``, ``wife_present`` and @@ -662,8 +668,8 @@ class SourceGate: ``registry``: refuse any entry with a blocker. ``invented_declared``: apply the declared interpretation and record what the registry gate would refuse (``would_refuse``). What the specification declares - nothing for refuses in the callers under both gates (code 88's - substantive role, documentary crosswalks as inputs). Frozen, so a + nothing for refuses in the callers under both gates (documentary + crosswalks as inputs). Frozen, so a gate's kind cannot change after the registry check at construction (the use records are appended in place). """ @@ -810,12 +816,6 @@ def as_dict(self) -> dict[str, Any]: marital_resolution=False, administrative_birth_support=False, ) -_CODE_88_REFUSAL = ( - "code 88 (first-year cohabitor): section 3 verifies its label and " - "keeps it in the inherited administrative birth-support selection; " - "any income slot, annuity or marital rule remains TO VERIFY and none " - "is declared, so an observation whose family holds one refuses" -) #: The two ratified exclusions as role-rule attachments (section 16c). @@ -826,10 +826,15 @@ def as_dict(self) -> dict[str, Any]: def declared_role_rules() -> dict[tuple[int, int], RoleRule]: """The declared rules for codes 10-92 in every support wave. - Section 3's table, amended where Max's d1090 ruling (2026-10-10, - section 16c) differs from it: 2015 code 20 keeps the spouse slot and - brings B1's exclusion when the person is recorded male, and 2019-2023 - codes 90 and 92 bring B2's exclusion and have no income role. + Section 3's table, amended where Max's rulings differ from it. Under + d1090 (2026-10-10, section 16c) 2015 code 20 keeps the spouse slot + and brings B1's exclusion when the person is recorded male, and + 2019-2023 codes 90 and 92 bring B2's exclusion and have no income + role. Under amendment 1c (d637, section 16b; confirmed by d745 on + 2026-10-12) code 88, the first-year cohabitor, has the OFUM income + role on the family basis outside the spouse slot, is no legal-spouse + annuity life, never resolves a marital history by its code and + keeps the inherited administrative birth support. """ out: dict[tuple[int, int], RoleRule] = {} @@ -854,7 +859,7 @@ def declared_role_rules() -> dict[tuple[int, int], RoleRule]: wave, 22, True, "wife", True, False, False, True ) out[(wave, 88)] = RoleRule( - wave, 88, True, None, False, False, False, True, _CODE_88_REFUSAL + wave, 88, True, "ofum", False, False, False, True ) b2 = _B2_EXCLUSION if wave in _B2_WAVES else None out[(wave, 90)] = RoleRule( @@ -1007,11 +1012,12 @@ class RoleContext: ``registry``: the committed roles registry; any code the loader's ``require_resolved`` refuses, refuses here (code 88 in every wave, - TO VERIFY, and code 92's absence in 2013 and 2015; until the d1090 - commit also 2015 code 20 and 2019-2023 codes 90 and 92, which now - carry section 16c's exclusions). ``invented_declared``: the declared rules, section 3's table - with section 16c's two family-unit exclusions (code 88 still - refuses); accepted only with invented inputs. + TO VERIFY until amendment 1c's registry commit, and code 92's + absence in 2013 and 2015; until the d1090 commit also 2015 code 20 + and 2019-2023 codes 90 and 92, which now carry section 16c's + exclusions). ``invented_declared``: the declared rules, section 3's + table with section 16c's two family-unit exclusions and amendment + 1c's code-88 convention; accepted only with invented inputs. The kind is a binding, not a label: construction refuses rules other than the kind's own (the committed registry's or the declared diff --git a/tests/track_u2/test_u2_roles.py b/tests/track_u2/test_u2_roles.py index 97fb73673..a59e1e1f3 100644 --- a/tests/track_u2/test_u2_roles.py +++ b/tests/track_u2/test_u2_roles.py @@ -2,8 +2,8 @@ Groups: labels (codes 10/20/22/88/90 in every support wave; code 92 absent in 2013/2015 and present 2017-2023; reported-birth-year anchors) -and roles (code 20 of either sex, code 90, code 92, code 88's -administrative-support distinction, cohabitor income slots, zero-weight +and roles (code 20 of either sex, code 90, code 92, code 88's OFUM +convention and administrative support, cohabitor income slots, zero-weight legal spouses, unresolved and ambiguous pairings), with the two family-unit exclusions Max ratified on 2026-10-10 (d1090; section 16c). INVENTED DATA - NOT A COMPARISON. @@ -150,7 +150,11 @@ def test_declared_rules_follow_the_section_3_table(): assert (ninety.income_role, ninety.spouse_slot) == (role, False) assert ninety.family_unit_exclusion == exclusion assert ninety.legal_spouse_annuity and ninety.marital_resolution - assert rules[(wave, 88)].refusal is not None + # Amendment 1c (d637, confirmed by d745): the first-year + # cohabitor's convention. + assert rules[(wave, 88)] == sources.RoleRule( + wave, 88, True, "ofum", False, False, False, True + ) assert rules[(wave, 88)].family_unit_exclusion is None assert rules[(wave, 22)].family_unit_exclusion is None assert rules[(wave, 10)].family_unit_exclusion is None @@ -292,22 +296,33 @@ def test_registry_context_refuses_refused_codes(): ) -def test_code_88_refuses_under_every_context(u2_inputs): +def test_code_88_takes_the_amendment_1c_convention(u2_inputs): + """Amendment 1c (d637, confirmed by d745): a code-88 first-year + cohabitor has the OFUM role outside the spouse slot, is no annuity + life and resolves no marital history, so adding one to a family that + supplies observations changes no observation or disposition. The + registry context refuses code 88 until the roles registry records + 1c.""" + variant = invented.with_code_88_cohabitor(u2_inputs) - with pytest.raises(sources.U2RoleRefusal, match="code 88"): - cohort.build_u2_cohort( - variant, - cohort.U2CohortSpec(row="U1"), - role_context=sources.RoleContext.declared(), - ) + spec = cohort.U2CohortSpec(row="U1") + declared = sources.RoleContext.declared() + built = cohort.build_u2_cohort(variant, spec, role_context=declared) + base = cohort.build_u2_cohort(u2_inputs, spec, role_context=declared) + # The cohabitor's family supplies observations, so its roles are read. + family = int(u2_inputs.family_income[2019]["interview"].iloc[0]) + obs = built.observations + assert (obs["wave"].eq(2019) & obs["interview"].eq(family)).any() + pd.testing.assert_frame_equal(built.observations, base.observations) + pd.testing.assert_frame_equal(built.dispositions, base.dispositions) + # The same family refuses where code 88 is still open, so the + # cohabitor's rule is read, not skipped. registry = sources.RoleContext.from_registry() for wave in sources.SUPPORT_WAVES: with pytest.raises(sources.U2RoleRefusal, match="relationship.88"): registry.rule(wave, 88) with pytest.raises(sources.U2RoleRefusal): - cohort.build_u2_cohort( - variant, cohort.U2CohortSpec(row="U1"), role_context=registry - ) + cohort.build_u2_cohort(variant, spec, role_context=registry) def test_declared_context_refuses_non_invented_inputs(u2_inputs, declared): @@ -715,20 +730,30 @@ def test_earlier_dispositions_keep_precedence( pd.testing.assert_frame_equal(built.observations, base.observations) -def test_code_88_in_an_excluded_unit_still_refuses(u2_inputs): - """A refusal is never hidden by an exclusion: a code-88 member in a - unit that B2 excludes refuses the build under both role contexts - (review of 07916bb, finding 3).""" +def test_code_88_in_an_excluded_unit_keeps_the_exclusion(u2_inputs): + """A code-88 member never hides or changes an exclusion: under + amendment 1c's convention a unit that B2 excludes stays excluded, + counted under its disposition, and nothing else moves; a role context + that leaves code 88 open refuses the build, never hidden by the + exclusion (review of 07916bb, finding 3).""" changed = invented.invented_variant( u2_inputs, "code_88_in_excluded_unit_2019" ) - for context in ( - sources.RoleContext.declared(), - sources.RoleContext.from_registry(), - ): - with pytest.raises(sources.U2RoleRefusal, match="88"): - cohort.build_u2_cohort(changed, role_context=context) + declared = sources.RoleContext.declared() + built = cohort.build_u2_cohort(changed, role_context=declared) + base = cohort.build_u2_cohort(u2_inputs, role_context=declared) + pd.testing.assert_frame_equal(built.observations, base.observations) + pd.testing.assert_frame_equal(built.dispositions, base.dispositions) + assert ( + built.dispositions["disposition"] + .eq(sources.UNCOOPERATIVE_SPOUSE_OR_PARTNER) + .any() + ) + with pytest.raises(sources.U2RoleRefusal, match="88"): + cohort.build_u2_cohort( + changed, role_context=sources.RoleContext.from_registry() + ) def test_structure_and_runner_disclose_each_exclusion(u1_cohort, u2_run): diff --git a/tests/track_u2/test_u2_scripts.py b/tests/track_u2/test_u2_scripts.py index 1c9d99d53..d052c4744 100644 --- a/tests/track_u2/test_u2_scripts.py +++ b/tests/track_u2/test_u2_scripts.py @@ -117,8 +117,27 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): exclusions = built["rows"][row]["family_unit_exclusions"] assert set(exclusions) == set(sources.FAMILY_UNIT_EXCLUSIONS) assert all(count > 0 for count in exclusions.values()), row + # Amendment 1c: the declared context builds a code-88 first-year + # cohabitor's family with every observation and disposition + # unchanged; the registry context refuses code 88 until the roles + # registry records 1c. assert roles["code_88_under_registry_context"]["refused"] - assert roles["code_88_under_declared_context"]["refused"] + declared_88 = roles["code_88_under_declared_context"] + assert not declared_88["refused"] + assert declared_88["observations_equal"] + assert declared_88["dispositions_equal"] + assert declared_88["rule"] == { + "wave": 2019, + "code": 88, + "present": True, + "income_role": "ofum", + "spouse_slot": False, + "legal_spouse_annuity": False, + "marital_resolution": False, + "administrative_birth_support": True, + "refusal": None, + "family_unit_exclusion": None, + } assert roles["declared_context_on_non_invented_inputs"]["refused"] assert roles["registry_rules_equal_declared_where_resolved"] refusals = roles["registry_rule_refusals"] @@ -166,10 +185,9 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): "spouse_slot_occupant_without_family_spouse_2019", ] assert all(entry["regenerated"] for entry in variants.values()) + # Amendment 1c: the two code-88 variants build under the declared + # context (the cohabitor has the OFUM role). build_refused = { - "code_88_first_year_cohabitor": "U2RoleRefusal", - # Section 16c: a code-88 refusal is never hidden by an exclusion. - "code_88_in_excluded_unit_2019": "U2RoleRefusal", "missing_family_record_2019": "U2CohortError", "duplicate_family_record_2019": "U2CohortError", "duplicate_person_record_2019": "U2CohortError", From 006aedfa23fabc716ae9ed2cf7a00b113ab358c6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 10:49:23 -0400 Subject: [PATCH 18/21] Apply d637 amendment 1c to the U2 roles registry Max's ruling d637 ratified u2-draft-4 amendment 1's parts 1a-1c as written, as he confirmed on 2026-10-12 (d745; section 16b). This is the 1c registry commit: the code-88 convention. U2 is a live blind target, and with no blocker left the structural pass would read real PSID records, so this commit also gates that pass on Max's recorded authorization. - roles.json: the six code-88 records (adjudication A, TO VERIFY with refuse_substantive_routing) are RESOLVED under the first-year cohabitor convention: action convention_per_d637_1c, income_role ofum, not the spouse slot, no legal-spouse annuity, no relationship-based marital resolution, the inherited administrative birth support; a resolution, the ruling (confirmed_by d745) and the question kept as superseded_question. The registry's code-88 role rules now equal the declared ones. - income.json: amendment 2's ten use_restriction texts said 1a-1c "await Max's confirmation, d745" and "code 88's slot status stays TO VERIFY"; they now say d637 ratified 1a-1c (d745) and a code-88 cohabitor does not occupy the slot (amendment 1c). - u2_source_registry: 1c's scope (entries, ruling, disposition A, fixed role fields, the action value only its entries may carry). - The loader's source preflight passes: 12 blockers to 0. Master record 1792 RESOLVED, 0 REFUSED, 0 TO VERIFY. - Structural-pass authorization (section 20 step 4): the pass needs Max's authorization, recorded as a chief-of-staff decision. scripts/track_u2_structure.py and track_u2_component_diagnostics.py refuse without --authorized-by and exit 2 before any PSID path is resolved or opened; build() refuses without a valid record or with one made at another git head; loader.load_u2_inputs refuses an unfrozen load without the record, before the source preflight. With the flag the output records the decision id, the git head and the UTC time. The flag only records the authorization; it does not grant it, and nothing reads the decision log. The registered run still refuses first (unratified specification, no pre-registration evidence). - Docstrings: sources, loader, the package, both structural scripts, the dry run and the registered run; the code-88 variant notes. - Section 17 item 2, section 19 and section 20 step 4 record it; the spec is re-pinned. Master record and exposure log follow. Tests: the d637 differential, text pins and clauses (1c and the reworded amendment 2), 14 new scope mutations and one text mutation; the preflight passes with no blocker; both role contexts build a code-88 family unchanged; each structural script, in a fresh interpreter with an audit hook on the staged and default PSID directories and a raising sentinel in place of psid._resolve_data_dir, exits 2 without a valid flag having resolved and opened nothing; with a decision id and an empty staged directory it reaches the cited-source rehash and still opens nothing; build() and the loader refuse without a record; the component pass records its authorization. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 20 +- data/external/track_u2/roles.json | 167 ++++++--- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 6 +- docs/design/u2_m1_source_adjudication.md | 43 +-- docs/design/u2_m1b_exposure.md | 1 + scripts/run_track_u2_registered.py | 13 +- scripts/track_u2_component_diagnostics.py | 58 ++- scripts/track_u2_dry_run.py | 33 +- scripts/track_u2_structure.py | 60 +++- .../data/u2_source_registry.py | 28 ++ .../uniform_cut_track_u2/__init__.py | 4 +- .../uniform_cut_track_u2/invented.py | 7 +- .../uniform_cut_track_u2/loader.py | 140 +++++++- .../uniform_cut_track_u2/sources.py | 24 +- tests/README-tiers.md | 8 +- tests/tier_counts.json | 6 +- tests/track_u2/test_adjudication_applied.py | 89 ++++- tests/track_u2/test_source_registries.py | 74 ++++ tests/track_u2/test_u2_loader.py | 38 +- tests/track_u2/test_u2_mappings.py | 112 ++++-- tests/track_u2/test_u2_roles.py | 97 ++--- tests/track_u2/test_u2_scripts.py | 336 ++++++++++++++---- 23 files changed, 1047 insertions(+), 319 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index 49851b436..546f617bb 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -4615,7 +4615,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -7546,7 +7546,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -9211,7 +9211,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -12158,7 +12158,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -13815,7 +13815,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -16754,7 +16754,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -18421,7 +18421,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -21435,7 +21435,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -23242,7 +23242,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", @@ -26256,7 +26256,7 @@ }, "status": "RESOLVED", "usage": "slot_metadata_only", - "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", + "use_restriction": "Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable.", "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question.", "ruling": { "decision": "d637", diff --git a/data/external/track_u2/roles.json b/data/external/track_u2/roles.json index 4b2f673f3..411589f4a 100644 --- a/data/external/track_u2/roles.json +++ b/data/external/track_u2/roles.json @@ -174,7 +174,6 @@ "variable": "ER34203", "present": true, "label": "First-year cohabitor of Head", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -197,13 +196,6 @@ "line": 102 } ], - "administrative_birth_support": true, - "action": "refuse_substantive_routing", - "income_role": null, - "spouse_slot": false, - "legal_spouse_annuity": false, - "marital_resolution": false, - "question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 102).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 102, @@ -218,7 +210,24 @@ "line": 198 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation." - } + }, + "status": "RESOLVED", + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": false, + "legal_spouse_annuity": false, + "marital_resolution": false, + "administrative_birth_support": true, + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2013 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1c", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" + }, + "superseded_question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 102)." }, { "id": "2013.relationship.90", @@ -480,7 +489,6 @@ "variable": "ER34303", "present": true, "label": "First-year cohabitor of Head", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -503,13 +511,6 @@ "line": 104 } ], - "administrative_birth_support": true, - "action": "refuse_substantive_routing", - "income_role": null, - "spouse_slot": false, - "legal_spouse_annuity": false, - "marital_resolution": false, - "question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 104).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 104, @@ -524,7 +525,24 @@ "line": 198 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation." - } + }, + "status": "RESOLVED", + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": false, + "legal_spouse_annuity": false, + "marital_resolution": false, + "administrative_birth_support": true, + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2015 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1c", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" + }, + "superseded_question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 104)." }, { "id": "2015.relationship.90", @@ -756,7 +774,6 @@ "variable": "ER34503", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -783,13 +800,6 @@ "line": 106 } ], - "administrative_birth_support": true, - "action": "refuse_substantive_routing", - "income_role": null, - "spouse_slot": false, - "legal_spouse_annuity": false, - "marital_resolution": false, - "question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 106).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 106, @@ -804,7 +814,24 @@ "line": 198 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation." - } + }, + "status": "RESOLVED", + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": false, + "legal_spouse_annuity": false, + "marital_resolution": false, + "administrative_birth_support": true, + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2017 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1c", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" + }, + "superseded_question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 106)." }, { "id": "2017.relationship.90", @@ -1067,7 +1094,6 @@ "variable": "ER34703", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -1094,13 +1120,6 @@ "line": 109 } ], - "administrative_birth_support": true, - "action": "refuse_substantive_routing", - "income_role": null, - "spouse_slot": false, - "legal_spouse_annuity": false, - "marital_resolution": false, - "question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 109).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 109, @@ -1115,7 +1134,24 @@ "line": 198 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation." - } + }, + "status": "RESOLVED", + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": false, + "legal_spouse_annuity": false, + "marital_resolution": false, + "administrative_birth_support": true, + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2019 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1c", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" + }, + "superseded_question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 109)." }, { "id": "2019.relationship.90", @@ -1392,7 +1428,6 @@ "variable": "ER34903", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -1419,13 +1454,6 @@ "line": 112 } ], - "administrative_birth_support": true, - "action": "refuse_substantive_routing", - "income_role": null, - "spouse_slot": false, - "legal_spouse_annuity": false, - "marital_resolution": false, - "question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 112).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 112, @@ -1440,7 +1468,24 @@ "line": 198 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation." - } + }, + "status": "RESOLVED", + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": false, + "legal_spouse_annuity": false, + "marital_resolution": false, + "administrative_birth_support": true, + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2021 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1c", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" + }, + "superseded_question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 112)." }, { "id": "2021.relationship.90", @@ -1717,7 +1762,6 @@ "variable": "ER35103", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -1744,13 +1788,6 @@ "line": 115 } ], - "administrative_birth_support": true, - "action": "refuse_substantive_routing", - "income_role": null, - "spouse_slot": false, - "legal_spouse_annuity": false, - "marital_resolution": false, - "question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 115).", "adjudication": { "source": "EV/phase2-20260927/out/u2-adjudicate.md", "line": 115, @@ -1765,7 +1802,24 @@ "line": 198 }, "reason": "docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation." - } + }, + "status": "RESOLVED", + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": false, + "legal_spouse_annuity": false, + "marital_resolution": false, + "administrative_birth_support": true, + "resolution": "Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2023 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question.", + "ruling": { + "decision": "d637", + "date": "2026-09-30", + "specification_section": "16b", + "amendment": "1c", + "confirmed_by": "d745", + "confirmed_on": "2026-10-12" + }, + "superseded_question": "Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 115)." }, { "id": "2023.relationship.90", @@ -1936,9 +1990,10 @@ "specification_section": "16b", "amendments": [ "1a", - "1b" + "1b", + "1c" ], - "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1; on 2026-10-12 Max confirmed that it ratified amendment 1's parts 1a-1c as written too (d745; section 16b). Applied here, each in its own reviewed commit: 1a records the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 OFUM routes, with the completeness disclosure registration states; 1b records the ruling on 2013's inherited code-90 meaning. Each ruled entry keeps its adjudication record, if any, and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." + "note": "Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendments 2-5 as written and amendment 6's option 1; on 2026-10-12 Max confirmed that it ratified amendment 1's parts 1a-1c as written too (d745; section 16b). Applied here, each in its own reviewed commit: 1a records the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 OFUM routes, with the completeness disclosure registration states; 1b records the ruling on 2013's inherited code-90 meaning; 1c resolves the six code-88 records under the first-year cohabitor convention. Each ruled entry keeps its adjudication record, if any, and gains a ruling; an open question or refusal the ruling answers is kept as superseded_question or superseded_refusal." } ] } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index e8b7918bd..0a24bce17 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "c9862af57c16af27ade448d5fb4e811dd18cdf5a55cb0a2f75c9580ad661ebcc", + "sha256": "ce6064ce76204454e1b54b986d1f361eb14a69db18e88d27a25ce870dfc92986", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index ae14f5b99..a6cf512d7 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1656,7 +1656,7 @@ Milestone 1 then built the documentary source registries (`data/external/track_u What remains before ratification: 1. Max rules on amendments 1–6 in §16a, choosing one option each for blocker B1 (2015 male code 20; Part B PARTIAL), B2 (codes 90 and 92 in 2019–2023; Part B PARTIAL) and B3 (construction of the revised 2017 weight; Part B PARTIAL). An option that lets registration proceed before documents resolve B1 or B2 takes effect only if Max also ratifies the amendment to draft-3 lines 152–160 or 1391 that the option names; otherwise that blocker's hold governs. B3's option 1 likewise takes effect only if Max ratifies its amendment to draft-3 line 134. **Ruled 2026-09-30 (d637; §16b):** B1 and B2 take option 3, the hold, with no amendment ratified; B3 takes option 1, with its amendment to draft-3 line 134 ratified; amendments 2–5 are ratified as written, and amendment 1's parts 1a–1c are read as ratified pending Max's confirmation (d745). **Ruled 2026-10-10 (d1090; §16c), after PSID answered:** B1 takes option 2, with its amendment to draft-3 line 160 ratified, and B2 takes option 1, with its amendment to draft-3 lines 152, 154 and 158 ratified for 2019–2023. **Confirmed 2026-10-12 (d745; §16b):** amendment 1's parts 1a–1c are ratified as written. This item is closed. -2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. Max confirmed 1a–1c on 2026-10-12 (d745), and the 1a and 1b commits recorded the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 routes and on 2013's inherited code-90 meaning. The 6 TO VERIFY records left are code 88's, open until amendment 1c's registry commit lands. +2. Each ruling is applied to the registries in its own reviewed commit. The d1090 commit (2026-10-10) replaced the eight B1 and B2 refusals with §16c's exclusions and released their dependents. The d637 commits, one per ratified amendment, have so far resolved amendment 2's ten later spouse age and sex records as slot metadata and amendment 3's six spouse-retirement crosswalks as non-executable metadata, recorded amendment 4's ruling on the nine corrected wealth accuracy flags, resolved the 2015 P62ACKPT checkpoint under 5b, released the 2017–2023 P64/P65 records under 5a and resolved the 2017 weight ER34651 under amendment 6's option 1; the §15 fragment itself is materialized under item 9. Max confirmed 1a–1c on 2026-10-12 (d745), and the 1a, 1b and 1c commits recorded the ruling on the documented 2015 code-90 and 2017 code-90 and code-92 routes and on 2013's inherited code-90 meaning, and resolved the six code-88 records under the first-year cohabitor convention. No TO VERIFY record remains, and the loader's source preflight passes; the structural pass still needs Max's authorization (item 8; §20 step 4). 3. The recommendations asked Max to send PSID the questions in `docs/design/u2_m1b_psid_research.md` §§1–3 now. He ruled to send all three as a held draft for him (d637), and they were sent on 2026-09-30 (§16b). PSID answered all three on 2026-10-07 (research record §§1–3), and Max ruled on B1 and B2 on 2026-10-10 (d1090; §16c). 4. Verify identification support, observation plans and refreshed design domains; weight documentation follows the amendment 6 ruling (option 1, ratified 2026-09-30; §16b). 5. Pin the SSI parameter file in §15. The capture exists and the adjudication reconfirmed all 106 source hashes. @@ -1868,6 +1868,8 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 1a: the three documented routes (`roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92`, adjudication D) record the ruling, confirmed by d745, and each gains the completeness disclosure 1a requires at registration: the OFUM route is documented, but not how completely the income of a person about whom answers were withheld was reported. No role rule, count or loader blocker moves. - Amendment 1b: `roles:2013.relationship.90` ('Legal husband of Head', resolved in milestone 1 with no adjudication record) records the ruling, confirmed by d745: 2013 keeps the inherited meanings, an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. No role rule, count or loader blocker moves. - Amendment 1c, builder: the declared role rules (invented data only) give code 88 amendment 1c's convention: the OFUM income role on the family basis outside the spouse slot, no legal-spouse annuity life, no relationship-based marital resolution and the inherited administrative birth support. A code-88 first-year cohabitor added to an invented family changes no observation or disposition. Until the 1c registry commit, the registry context still refuses code 88 and the loader still refuses on its 12 blockers. +- Amendment 1c: the six code-88 records (`roles:.relationship.88`, adjudication A) are RESOLVED under the first-year cohabitor convention and record the ruling, confirmed by d745; each keeps its question as `superseded_question`. The registry's code-88 role rules now equal the declared ones. Amendment 2's ten spouse age and sex records now say that a code-88 cohabitor does not occupy the slot, in place of "code 88's slot status stays TO VERIFY" and the clause that 1a–1c await d745. No TO VERIFY record remains, and the loader's source preflight passes: no blocker remains. +- The structural pass (§20 step 4) still needs Max's authorization, recorded as a chief-of-staff decision. `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` now refuse without `--authorized-by `, exiting non-zero before any PSID path is resolved or opened, and the loader refuses an unfrozen load without the record; the flag records the decision id, the git head and the UTC time, and does not grant the authorization. §17 item 2 and §20 step 4 say so. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record @@ -1877,7 +1879,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t 1. Resolve the proposed decisions and source-verification blockers. **Decisions: done (Max, d514, 2026-09-28, adopting every §16 default).** Source-verification blockers remain for steps 2–5. **`u2-draft-4` amendments 1–6 (§16a): ruled by Max on 2026-09-30 (d637; §16b).** B3 is resolved by option 1. B1 and B2 were held until PSID answered; **Max ruled on them on 2026-10-10 (d1090; §16c)**: B1 by option 2 and B2 by option 1, each an exclusion under a named disposition. Max confirmed the reading of amendment 1's parts 1a–1c in §16b on 2026-10-12 (d745), so this step is done. 2. Complete implementation, manifests and parameter capture. 3. Complete independent mapping review, invented tests, historical-isolation checks and exact U1 differential evidence. -4. Complete the authorized pre-registration structural, reconciliation and F17 component pass; resolve mapping failures. +4. Complete the authorized pre-registration structural, reconciliation and F17 component pass; resolve mapping failures. The pass reads real PSID records, so it needs Max's authorization, recorded as a chief-of-staff decision. `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` refuse to run without `--authorized-by `, exiting non-zero before any PSID path is resolved or opened, and the loader refuses an unfrozen load without the same record. The flag records the decision id, the git head and the UTC time in the pass's output; it only records the authorization and does not grant it. 5. Materialize the final parameter block and U2 rulings record; require exact code/specification agreement. 6. Ratify the specification by merge. 7. Obtain and independently check the fresh forecast. diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index abd6746b9..c7b1089d6 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,7 +2,7 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2026-09-28) to apply the independent adjudication `EV/phase2-20260927/out/u2-adjudicate.md` (SHA-256 `a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662`, verdict ERRORS FOUND). This record does not ratify, register or execute U2. No PSID records were read and no estimator was run. Milestone 1b writes the proposed revision `u2-draft-4` into the specification's new §16a; every line the registries cite keeps its draft-3 text and line number. -**1786 RESOLVED; 0 REFUSED; 6 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. The 6 TO VERIFY records are the adjudication's genuinely ambiguous items (disposition A) still open. Of its 24, Max's ruling d637 resolves the 18 under amendments 2, 3, 5 and 6, one registry commit per amendment (section below lists those applied), and the six code-88 records wait for amendment 1c's registry commit (Max confirmed the 1a–1c reading on 2026-10-12, d745). +**1792 RESOLVED; 0 REFUSED; 0 TO VERIFY** route/field records, including 26 SSI/Census records (milestone 1: 1744 RESOLVED; 48 TO VERIFY; milestone 1b: 1760 RESOLVED, 8 REFUSED, 24 TO VERIFY; after d1090: 1768 RESOLVED, 0 REFUSED, 24 TO VERIFY). Counts treat a documented mapping with an unresolved application dependency as RESOLVED metadata; the loader still refuses its application. A resolved record is a documentary finding, not execution authorization. **REFUSED** would mark a route the independent adjudication refused (disposition F) that no ruling has replaced. The adjudication refused eight; on 2026-10-10 Max's ruling d1090 (specification §16c) replaced all eight with ratified family-unit exclusions, so none is REFUSED now. Each keeps its adjudication record (disposition F) and, as `superseded_refusal`, its refusal text; its `ruling` names d1090, and `require_resolved()` returns it. No TO VERIFY record remains. Of the adjudication's 24 genuinely ambiguous items (disposition A), Max's ruling d637 resolved the 18 under amendments 2, 3, 5 and 6 and, as Max confirmed on 2026-10-12 (d745), the six code-88 records under amendment 1c, one registry commit per amendment (the section below lists them). | Registry | RESOLVED | REFUSED | TO VERIFY | |---|---:|---:|---:| @@ -10,7 +10,7 @@ Documentary source review for `u2-draft-3`, updated by milestone 1b (`u2-m1b`, 2 | wealth | 234 | 0 | 0 | | individual | 59 | 0 | 0 | | pension | 648 | 0 | 0 | -| roles | 30 | 0 | 6 | +| roles | 36 | 0 | 0 | | support | 16 | 0 | 0 | | weights | 7 | 0 | 0 | | design | 2 | 0 | 0 | @@ -27,10 +27,10 @@ The loader is `src/populace_dynamics/data/u2_source_registry.py`; `require_resol | 2015 male code 20 | RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | The adjudication refuses the male code-20 spouse-slot route: q2015 pp. 89 and 166 admit only CYAQRTH=202, 222 and ER60020 (p. 6) lists only female. Part B found that official documentation routes every documented kind of male legal spouse in 2015 away from code 20: through 2015 a husband is Head (UserGuide2017 PDF p. 13; FAQ question 73), a husband who is not Head is code 90 and 'was asked the same questions as an Ofum' (FAQ question 74), and same-sex partners are code 98 with the ER34304 flag (IND2023ER_codebook p. 978; UserGuide2015 PDF p. 33; UserGuide2017 PDF p. 35). The 2015 Section J qualifier classes values 102-222 as female (q2015 p. 190). No document says code 20 is never male in 2015, and FAQ question 70's general sentence allows a same-sex Spouse/Partner couple from 2015, so the documents do not settle how a male Head's legally married same-sex spouse was coded. PSID answered on 2026-10-07 that no male should have code 20 in 2015 (research record §1). Max ruled on 2026-10-10 (d1090; specification §16c, option 1d-2, amending draft-3 line 160): every 2015 family-unit observation that contains a person recorded male with code 20 is excluded from every row under `male code 20 in 2015 family unit`. | | Code 90, 2015–2023 | 2015 and 2017 RESOLVED (D; amendment 1a ratified by d637, confirmed by d745); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | 2015 and 2017 OFUM collection is documented (q2015 pp. 89, 104, 166; q2017 pp. 5, 95; FAQ question 74). From 2019 the Big OFUM gate admits only CYAQRTH=301-882 or 951-982, so 901/902 get no Section G income questions (q2019 p. 103; q2021 p. 205; q2023 p. 203); whether their income enters the aggregates is not documented online, and PSID's 2026-10-07 answer did not say (research record §2). Max ruled on 2026-10-10 (d1090; specification §16c, option 1e-1, amending draft-3 lines 152, 154 and 158 for 2019–2023): every 2019–2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under `uncooperative spouse or partner in family unit`. | | Code 92, 2017–2023 | 2017 RESOLVED (D; amendment 1a ratified by d637, confirmed by d745); 2019–2023 RESOLVED by Max's ruling d1090 (exclusion; adjudication F superseded); Part B PARTIAL | As for code 90, with 921/922; the same exclusion applies. | -| Code 88 substantive routing | TO VERIFY (adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. The legal-marital and annuity convention is amendment 1c's, ratified by d637 as Max confirmed on 2026-10-12 (d745); its registry commit applies it. | +| Code 88 substantive routing | RESOLVED by Max's ruling d637 (amendment 1c, confirmed by d745; adjudication A) | OFUM collection is documented in all six questionnaires, and FAQ question 75 says boyfriends and girlfriends 'are treated like other family members who are not Reference Person, Spouse or Partner'. Amendment 1c's convention applies in every wave: an OFUM income role on the family basis (its SSI in §8's OFUM unit), not the spouse slot, no legal-spouse annuity life, no relationship-based marital resolution, the recorded legal marital history and the inherited administrative birth support. | | 2015 pension P62A | RESOLVED by Max's ruling d637 (amendment 5b; adjudication A) | q2015 p. 164 conflicts internally and with FAM2015ER p. 692. Amendment 5b admits the documentary intersection (P46=5 reaches P64/P65; P46=7 reaches P48/P49) and refuses the disputed branch: an amount reached only through it is off-route under the named disposition `off_route`, never zero-filled. | | 2017–2023 pension P64/P65 contract | RESOLVED (D); applied by Max's ruling d637 (amendment 5a) | q2017 p. 152, q2019 p. 163, q2021 p. 304 and q2023 p. 302 route only P46=5 to P63–P65. Amendment 5a's wave-specific route supersedes the §15 contract's generic account/formula/dk previous route (§16b; §15's text is unchanged until §20 step 5 materializes the final parameter block), so the dependent P64/P65 entries are released. | -| Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot: the relationship rules in the roles registry do, and code 88's slot status stays TO VERIFY (amendment 1c, pending d745). | +| Later spouse age/sex universes | RESOLVED by Max's ruling d637 (amendment 2; adjudication A) | Amendment 2: annuitant sex and age come from the individual's recorded sex and derived age; the 2015–2023 family spouse age and sex are slot metadata only, and their inapplicability wording never decides who occupies the spouse slot: the relationship rules in the roles registry do, and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). | | Historical combined spouse retirement crosswalk | RESOLVED by Max's ruling d637 (amendment 3; adjudication A) | Amendment 3: the six crosswalk records are non-executable metadata and no scalar historical equivalence is asserted; spouse retirement components, spouse IRA income included, remain inside total family income and are never added again. | | Wealth accuracy-variable references | RESOLVED (D); amendment 4 ratified by Max's ruling d637 | Nine corrected targets are recorded with the contradictory codebook wording preserved; amendment 4 limits the flags to imputation diagnostics, and the corrections never change an amount or a sample-inclusion decision. | | Revised 2017 individual-weight construction | RESOLVED by Max's ruling d637 (amendment 6, option 1; adjudication A); Part B PARTIAL | The May 2019 release notes say 2017 Immigrant weights 'remain zero and will be updated in Release 3', while UserGuide2017 p. 62 dates the update to Release 2, the 2017 family file's own release series (FAM2017ER codebook p. 2). The April 2021 and June 2026 cross-sectional reports document the 2017 population control (ACS one-year PUMS, CPS not used, two named exclusions) and the calibration-variable selection approach used since 2017; the April 2021 report also documents some calibration controls; both build cross-sectional weights with a version of the fair-shares method; and the 2019 longitudinal-weights report documents the 2017 Immigrant sample's base weights as formed for the 2019 weights. PSID's later reports cite a 2019 version of the 1997-2017 report that the served report does not match. Which starting weights, and which integration of joint families, the revised 2017 weight used for that sample, and the complete 2017 raking specification, remain undocumented. Max took amendment 6's option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 is used as released, and registration discloses what PSID has and has not documented (`registration_disclosure`). | @@ -82,8 +82,9 @@ Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): ame | 6: the 2017 cross-sectional weight | 1 + 1 released | `weights:2017.cross_section_weight` is RESOLVED under option 1, ratified as an explicit amendment to draft-3 line 134: ER34651 as released, selected by positive weight, with no immigrant exclusion, reconstructed weight or recalibration, and a `registration_disclosure` listing what PSID has documented (its identity, the ACS one-year PUMS control and its two named exclusions, the calibration-variable selection approach, some calibration controls, the 2017 Immigrant sample's base weights as formed for the 2019 weights) and has not published (that sample's starting weights and joint-family integration in the revised 2017 weight, and the complete 2017 raking specification), with the coverage bound the control's exclusions set. `individual:2017.weight` is released | | 1a: documented routes (confirmed by d745) | 3 | `roles:2015.relationship.90`, `roles:2017.relationship.90` and `roles:2017.relationship.92` keep their documentary resolution (adjudication D) and record the ruling (`ruling` with `confirmed_by` d745) and a `ruling_effect`; each gains the `completeness_disclosure` registration states: the OFUM route is documented, but not how completely a person about whom answers were withheld had their income reported. No role rule, count or loader blocker moves | | 1b: 2013's inherited meanings (confirmed by d745) | 1 | `roles:2013.relationship.90` ('Legal husband of Head', resolved in milestone 1 with no adjudication record) records the ruling and a `ruling_effect`: 2013 keeps the inherited meanings, an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. No role rule, count or loader blocker moves | +| 1c: the code-88 convention (confirmed by d745) | 6 | `roles:{2013,2015,2017,2019,2021,2023}.relationship.88` are RESOLVED under the first-year cohabitor convention (action `convention_per_d637_1c`): an OFUM income role on the family basis with its SSI in §8's OFUM unit, not the spouse slot, no legal-spouse annuity life, no relationship-based marital resolution and the inherited administrative birth support; each keeps its question as `superseded_question`. The registry's code-88 role rules now equal the declared ones, and the 12 loader blockers are released. Amendment 2's ten spouse age/sex records now say that a code-88 cohabitor does not occupy the slot, in place of 'code 88's slot status stays TO VERIFY' | -The loader's source preflight now refuses on 12 blockers, all code 88's (amendment 1c, whose registry commit follows): each wave's code-88 role entry is TO VERIFY and refuses its substantive routing. +The loader's source preflight now passes: no blocker remains. The structural pass (specification §20 step 4) still needs Max's authorization, recorded as a chief-of-staff decision: its two scripts, and the loader's unfrozen load, refuse without it, and `--authorized-by ` records that decision without granting it. ## One row per source route or field @@ -217,7 +218,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2015.fu_size` | RESOLVED | ER60016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1912`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 5 | | `income:income.2015.head_age` | RESOLVED | ER60017: AGE OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1913`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `income:income.2015.head_sex` | RESOLVED | ER60018: SEX OF HEAD. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:16`; `PSID/family/2015/FAM2015ER.sps:1914`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | -| `income:income.2015.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1915`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:68` | +| `income:income.2015.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1915`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:68` | | `income:income.2015.n_children` | RESOLVED | ER60021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1917`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | | `income:income.2015.head_labor` | RESOLVED | ER65216: LABOR INCOME OF HEAD-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1749`; `PSID/family/2015/FAM2015ER.sps:7112`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1943 | | `income:income.2015.head_farm` | RESOLVED | ER65195: FARM INCOME OF HEAD AND SPOUSE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1742`; `PSID/family/2015/FAM2015ER.sps:7091`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1938 | @@ -300,7 +301,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2015.ofum_asset_acc` | RESOLVED | ER65320: ACCURACY OF OTR FU MEMBR ASSET Y-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1784`; `PSID/family/2015/FAM2015ER.sps:7216`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1969 | | `income:income.2015.wife_va_pension` | RESOLVED | ER65290: SPOUSE VA PENSION-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1774`; `PSID/family/2015/FAM2015ER.sps:7186`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1962 | | `income:income.2015.wife_alimony` | RESOLVED | ER65306: SPOUSE ALIMONY-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1779`; `PSID/family/2015/FAM2015ER.sps:7202`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1966 | -| `income:income.2015.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1916`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:69` | +| `income:income.2015.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Head is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2015/FAM2015ER.sps:17`; `PSID/family/2015/FAM2015ER.sps:1916`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:69` | | `income:income.2015.head_farm_acc` | RESOLVED | ER65196: ACC FARM INCOME OF HEAD AND SPOUSE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1742`; `PSID/family/2015/FAM2015ER.sps:7092`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1938 | | `income:income.2015.head_tanf_acc` | RESOLVED | ER65255: ACCURACY OF HEAD INCOME FROM TANF-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1762`; `PSID/family/2015/FAM2015ER.sps:7151`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1954 | | `income:income.2015.head_other_welfare_acc` | RESOLVED | ER65259: ACCURACY OF HEAD OTHER WELFARE-2014. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2015/FAM2015ER.sps:1763`; `PSID/family/2015/FAM2015ER.sps:7155`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1954; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 1955 | @@ -345,7 +346,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2017.fu_size` | RESOLVED | ER66016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1940`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_age` | RESOLVED | ER66017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1941`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | | `income:income.2017.head_sex` | RESOLVED | ER66018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:16`; `PSID/family/2017/FAM2017ER.sps:1942`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6 | -| `income:income.2017.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1943`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:71` | +| `income:income.2017.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1943`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:71` | | `income:income.2017.n_children` | RESOLVED | ER66021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1945`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | | `income:income.2017.head_labor` | RESOLVED | ER71293: LABOR INCOME OF REF PERSON-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1775`; `PSID/family/2017/FAM2017ER.sps:7217`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1996 | | `income:income.2017.head_farm` | RESOLVED | ER71272: FARM INCOME OF REF PERSN AND SPOUSE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1768`; `PSID/family/2017/FAM2017ER.sps:7196`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1990 | @@ -428,7 +429,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2017.ofum_asset_acc` | RESOLVED | ER71397: ACCURACY OF OTR FU MEMBR ASSET Y-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1809`; `PSID/family/2017/FAM2017ER.sps:7321`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2022 | | `income:income.2017.wife_va_pension` | RESOLVED | ER71367: SPOUSE VA PENSION-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1799`; `PSID/family/2017/FAM2017ER.sps:7291`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2014 | | `income:income.2017.wife_alimony` | RESOLVED | ER71383: SPOUSE ALIMONY-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1805`; `PSID/family/2017/FAM2017ER.sps:7307`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2018 | -| `income:income.2017.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1944`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:72` | +| `income:income.2017.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2017/FAM2017ER.sps:17`; `PSID/family/2017/FAM2017ER.sps:1944`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:72` | | `income:income.2017.head_farm_acc` | RESOLVED | ER71273: ACC FARM INCOME OF RP AND SPOUSE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1768`; `PSID/family/2017/FAM2017ER.sps:7197`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 1991 | | `income:income.2017.head_tanf_acc` | RESOLVED | ER71332: ACCURACY OF RP INCOME FROM TANF-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1788`; `PSID/family/2017/FAM2017ER.sps:7256`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2006 | | `income:income.2017.head_other_welfare_acc` | RESOLVED | ER71336: ACCURACY OF RP OTHER WELFARE-2016. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2017/FAM2017ER.sps:1789`; `PSID/family/2017/FAM2017ER.sps:7260`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 2007 | @@ -473,7 +474,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2019.fu_size` | RESOLVED | ER72016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1944`; `PSID/family/2019/fam2019er_codebook.pdf` p. 5 | | `income:income.2019.head_age` | RESOLVED | ER72017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1945`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | | `income:income.2019.head_sex` | RESOLVED | ER72018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:16`; `PSID/family/2019/FAM2019ER.sps:1946`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | -| `income:income.2019.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1947`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:74` | +| `income:income.2019.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1947`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:74` | | `income:income.2019.n_children` | RESOLVED | ER72021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1949`; `PSID/family/2019/fam2019er_codebook.pdf` p. 7 | | `income:income.2019.head_labor` | RESOLVED | ER77315: LABOR INCOME OF REF PERSON-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1782`; `PSID/family/2019/FAM2019ER.sps:7243`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1980 | | `income:income.2019.head_farm` | RESOLVED | ER77294: FARM INCOME OF REF PERSN AND SPOUSE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1775`; `PSID/family/2019/FAM2019ER.sps:7222`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1975 | @@ -556,7 +557,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2019.ofum_asset_acc` | RESOLVED | ER77419: ACCURACY OF OTR FU MEMBR ASSET Y-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1817`; `PSID/family/2019/FAM2019ER.sps:7347`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2007 | | `income:income.2019.wife_va_pension` | RESOLVED | ER77389: SPOUSE VA PENSION-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1807`; `PSID/family/2019/FAM2019ER.sps:7317`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1999 | | `income:income.2019.wife_alimony` | RESOLVED | ER77405: SPOUSE ALIMONY-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1812`; `PSID/family/2019/FAM2019ER.sps:7333`; `PSID/family/2019/fam2019er_codebook.pdf` p. 2003 | -| `income:income.2019.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1948`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:75` | +| `income:income.2019.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2019/FAM2019ER.sps:17`; `PSID/family/2019/FAM2019ER.sps:1948`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:75` | | `income:income.2019.head_farm_acc` | RESOLVED | ER77295: ACC FARM INCOME OF RP AND SPOUSE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1775`; `PSID/family/2019/FAM2019ER.sps:7223`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1975 | | `income:income.2019.head_tanf_acc` | RESOLVED | ER77354: ACCURACY OF RP INCOME FROM TANF-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1795`; `PSID/family/2019/FAM2019ER.sps:7282`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1991 | | `income:income.2019.head_other_welfare_acc` | RESOLVED | ER77358: ACCURACY OF RP OTHER WELFARE-2018. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2019/FAM2019ER.sps:1796`; `PSID/family/2019/FAM2019ER.sps:7286`; `PSID/family/2019/fam2019er_codebook.pdf` p. 1991; `PSID/family/2019/fam2019er_codebook.pdf` p. 1992 | @@ -601,7 +602,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2021.fu_size` | RESOLVED | ER78016: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1396`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:224` | | `income:income.2021.head_age` | RESOLVED | ER78017: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1397`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6 | | `income:income.2021.head_sex` | RESOLVED | ER78018: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:16`; `PSID/family/2021/FAM2021ER.sps:1398`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 6; `PSID/family/2021/FAM2021ER_formats.sps:248` | -| `income:income.2021.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1399`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:77` | +| `income:income.2021.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1399`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `EV/phase2-20260927/out/u2-adjudicate.md:77` | | `income:income.2021.n_children` | RESOLVED | ER78021: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1401`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:261` | | `income:income.2021.head_labor` | RESOLVED | ER81642: LABOR INCOME OF REF PERSON-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1224`; `PSID/family/2021/FAM2021ER.sps:5022`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1344 | | `income:income.2021.head_farm` | RESOLVED | ER81621: FARM INCOME OF REF PERSN AND SPOUSE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1217`; `PSID/family/2021/FAM2021ER.sps:5001`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1339 | @@ -684,7 +685,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2021.ofum_asset_acc` | RESOLVED | ER81746: ACCURACY OF OTR FU MEMBR ASSET Y-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1259`; `PSID/family/2021/FAM2021ER.sps:5126`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1371; `PSID/family/2021/FAM2021ER_formats.sps:37106` | | `income:income.2021.wife_va_pension` | RESOLVED | ER81716: SPOUSE VA PENSION-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1249`; `PSID/family/2021/FAM2021ER.sps:5096`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1363 | | `income:income.2021.wife_alimony` | RESOLVED | ER81732: SPOUSE ALIMONY-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1254`; `PSID/family/2021/FAM2021ER.sps:5112`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1367 | -| `income:income.2021.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1400`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:254`; `EV/phase2-20260927/out/u2-adjudicate.md:78` | +| `income:income.2021.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2021/FAM2021ER.sps:17`; `PSID/family/2021/FAM2021ER.sps:1400`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7; `PSID/family/2021/FAM2021ER_formats.sps:254`; `EV/phase2-20260927/out/u2-adjudicate.md:78` | | `income:income.2021.head_farm_acc` | RESOLVED | ER81622: ACC FARM INCOME OF RP AND SPOUSE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1218`; `PSID/family/2021/FAM2021ER.sps:5002`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1339; `PSID/family/2021/FAM2021ER_formats.sps:36643` | | `income:income.2021.head_tanf_acc` | RESOLVED | ER81681: ACCURACY OF RP INCOME FROM TANF-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1237`; `PSID/family/2021/FAM2021ER.sps:5061`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1355; `PSID/family/2021/FAM2021ER_formats.sps:36889` | | `income:income.2021.head_other_welfare_acc` | RESOLVED | ER81685: ACCURACY OF RP OTHER WELFARE-2020. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2021/FAM2021ER.sps:1239`; `PSID/family/2021/FAM2021ER.sps:5065`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1355; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 1356; `PSID/family/2021/FAM2021ER_formats.sps:36903` | @@ -729,7 +730,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2023.fu_size` | RESOLVED | ER82017: # IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1348`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:228` | | `income:income.2023.head_age` | RESOLVED | ER82018: AGE OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:16`; `PSID/family/2023/FAM2023ER.sps:1349`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6 | | `income:income.2023.head_sex` | RESOLVED | ER82019: SEX OF REFERENCE PERSON. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1350`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `PSID/family/2023/FAM2023ER_formats.sps:252` | -| `income:income.2023.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1351`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:80` | +| `income:income.2023.wife_age` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse age and its inapplicability wording ('Reference Person is female or single male') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1351`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 6; `EV/phase2-20260927/out/u2-adjudicate.md:80` | | `income:income.2023.n_children` | RESOLVED | ER82022: # CHILDREN IN FU. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:18`; `PSID/family/2023/FAM2023ER.sps:1353`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:265` | | `income:income.2023.head_labor` | RESOLVED | ER85496: LABOR INCOME OF REF PERSON-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1176`; `PSID/family/2023/FAM2023ER.sps:4827`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1262 | | `income:income.2023.head_farm` | RESOLVED | ER85475: FARM INCOME OF REF PERSN AND SPOUSE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1169`; `PSID/family/2023/FAM2023ER.sps:4806`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1257 | @@ -812,7 +813,7 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `income:income.2023.ofum_asset_acc` | RESOLVED | ER85600: ACCURACY OF OTR FU MEMBR ASSET Y-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1210`; `PSID/family/2023/FAM2023ER.sps:4931`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1289; `PSID/family/2023/FAM2023ER_formats.sps:34690` | | `income:income.2023.wife_va_pension` | RESOLVED | ER85570: SPOUSE VA PENSION-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1200`; `PSID/family/2023/FAM2023ER.sps:4901`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1281 | | `income:income.2023.wife_alimony` | RESOLVED | ER85586: SPOUSE ALIMONY-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1206`; `PSID/family/2023/FAM2023ER.sps:4917`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1285 | -| `income:income.2023.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c await Max's confirmation, d745), and code 88's slot status stays TO VERIFY. Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1352`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:258`; `EV/phase2-20260927/out/u2-adjudicate.md:81` | +| `income:income.2023.wife_sex` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified u2-draft-4 amendment 2 (annuitant input precedence) as written: the family-file spouse age and sex variables are slot metadata only, under the no-slot-inference rule in use_restriction. The layout and documented domain stand as recorded. The rule answers the adjudication's question without reference to any observed record; the question is kept as superseded_question. Slot metadata only (u2-draft-4 amendment 2). The family-file spouse sex and its inapplicability wording ('Reference Person is single male or female') never decide whether a person occupies the spouse slot: slot occupancy comes from the relationship rules, as the roles registry's spouse_slot field records them (section 3; section 16a amendment 1, whose parts 1a-1c d637 ratified, as Max confirmed on 2026-10-12, d745), and a code-88 first-year cohabitor does not occupy the slot (amendment 1c). Annuity lives and every age- or sex-dependent rule use each person's recorded sex (ER32000) and derived income-year age (section 3, Annuitant ages), never this variable. | `PSID/family/2023/FAM2023ER.sps:17`; `PSID/family/2023/FAM2023ER.sps:1352`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7; `PSID/family/2023/FAM2023ER_formats.sps:258`; `EV/phase2-20260927/out/u2-adjudicate.md:81` | | `income:income.2023.head_farm_acc` | RESOLVED | ER85476: ACC FARM INCOME OF RP AND SPOUSE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1169`; `PSID/family/2023/FAM2023ER.sps:4807`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1257; `PSID/family/2023/FAM2023ER_formats.sps:34227` | | `income:income.2023.head_tanf_acc` | RESOLVED | ER85535: ACCURACY OF RP INCOME FROM TANF-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1189`; `PSID/family/2023/FAM2023ER.sps:4866`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1273; `PSID/family/2023/FAM2023ER_formats.sps:34473` | | `income:income.2023.head_other_welfare_acc` | RESOLVED | ER85539: ACCURACY OF RP OTHER WELFARE-2022. Exact layout, codebook domain and universe are retained in the JSON registry. | `PSID/family/2023/FAM2023ER.sps:1190`; `PSID/family/2023/FAM2023ER.sps:4870`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1273; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 1274; `PSID/family/2023/FAM2023ER_formats.sps:34487` | @@ -1797,37 +1798,37 @@ The loader's source preflight now refuses on 12 blockers, all code 88's (amendme | `roles:2013.relationship.10` | RESOLVED | Head in 2013; 2011 Head who was mover-out nonresponse by the time of the 2013 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:21918`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2013.relationship.20` | RESOLVED | Legal Wife in 2013; 2011 Wife who was mover-out nonresponse by the time of the 2013 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:21919`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 6 | | `roles:2013.relationship.22` | RESOLVED | "Wife"--female cohabitor who has lived with Head for 12 months or more; 2011 "Wife" who was mover-out nonresponse...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:21920`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2013/FAM2013ER_codebook.pdf` p. 6 | -| `roles:2013.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 102). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:21945`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:102` | +| `roles:2013.relationship.88` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2013 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question. Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:21945`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:102` | | `roles:2013.relationship.90` | RESOLVED | Legal husband of Head; declared income role ofum; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. Citation corrected (amendment 7, EV/phase2-20260927/out/u2-adjudicate.md line 59): docs/design/boomers2004_1946_55_comparison.md:154 concerns the later-wave code-90 definition; lines 147, 150, 156 and 175 state the code-90 role, the 2013 meanings, the annuity pairing and the 2013 label test. The 2013 label remains 'Legal husband of Head' (IND2023ER_codebook.pdf p. 916). Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1b 2013 keeps the inherited meanings: code 90 is 'Legal husband of Head' (IND2023ER codebook p. 916), with the rules of section 3 lines 147, 150, 156 and 175: an OFUM income role outside the spouse slot, the co-resident legal-spouse annuity life and relationship-based marital resolution. | `PSID/ind2023er/IND2023ER_formats.sps:21946`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:147`; `docs/design/boomers2004_1946_55_comparison.md:150`; `docs/design/boomers2004_1946_55_comparison.md:156`; `docs/design/boomers2004_1946_55_comparison.md:175`; `EV/phase2-20260927/out/u2-adjudicate.md:59` | | `roles:2013.relationship.92` | RESOLVED | Code absent from complete wave format block; refuse if supplied. | `PSID/ind2023er/IND2023ER_formats.sps:21918`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 915; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 916; `docs/design/boomers2004_1946_55_comparison.md:152` | | `roles:2015.relationship.10` | RESOLVED | Head in 2015; 2013 Head who was mover-out nonresponse by the time of the 2015 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:22969`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2015.relationship.20` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1d-2, ratified as an explicit amendment to draft-3 line 160: 2015 code 20 occupies the spouse slot as documented, and every 2015 family-unit observation that contains a person recorded male (ER32000 = 1) with 2015 relationship code 20 is excluded from every row under the named disposition male code 20 in 2015 family unit. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. This replaces the adjudication's refusal, kept as superseded_refusal; family_unit_exclusion.condition records that the exclusion concerns sex=1. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md section 1): official documentation routes every documented kind of male legal spouse in 2015 away from code 20. Through 2015 the husband of a heterosexual couple is Head (UserGuide2017 PDF p. 13; FAQ question 73), a husband who is not Head is 'Husband of Head' code 90 and 'was asked the same questions as an Ofum' (FAQ question 74), and 2015 same-sex partners are code 98 'Other nonrelatives (includes same-sex partners ...)' flagged by ER34304 (IND2023ER_codebook p. 978; UserGuide2015 PDF p. 33; UserGuide2017 PDF p. 35). The 2015 Section J qualifier classes values 102-222 as female, where the 2017 qualifier classes 101, 201 and 221 as male (research record section 1, item 11; q2015 p. 190; q2017 p. 184). No document states that code 20 is never male in 2015, and FAQ question 70 says generally that from 2015 a Spouse/Partner couple 'can consist of heterosexual or same sex couples', so the documents do not settle how a male Head's legally married same-sex spouse was coded. The 2015 'RTH Lookup List' named for CYAQRTH (q2015 p. 5) is absent from the staged documentation. PSID answered on 2026-10-07 that no male should have code 20 in 2015 (research record section 1), and Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:22970`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6; `PSID/documentation/capture1/q2015.pdf` p. 89; `PSID/documentation/capture1/q2015.pdf` p. 166; `EV/phase2-20260927/out/u2-adjudicate.md:103` | | `roles:2015.relationship.22` | RESOLVED | Partner--female cohabitor who has lived with Head for 12 months or more; 2013 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:22971`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2015/FAM2015ER_codebook.pdf` p. 6 | -| `roles:2015.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 104). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:22996`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:104` | +| `roles:2015.relationship.88` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2015 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question. Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:22996`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:104` | | `roles:2015.relationship.90` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. I p. 978: 'Uncooperative legal spouse of Head'. The 2015 spouse income and pension loops admit only the Wife/'Wife' codes (q2015 p. 89 G49CKPT and p. 166 P70CKPT: 'CYFUHU=1, 4 & CYAQRTH=202, 222'), while the Big OFUM income series admits 'FUP1YEAR[I].AQRTH.ORD>222' (q2015 p. 104). Code-90 income is collected in the OFUM series and excluded from the standard spouse loop. The declared legal-spouse annuity and marital-resolution conventions are unchanged. Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 90 is an OFUM income role outside the spouse slot in 2015, with the annuity and marital rules recorded here, and registration states the completeness_disclosure. The 2015 code-90 OFUM route is documented; how completely the uncooperative legal spouse's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2015ER pp. 1968-1975), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a). | `PSID/ind2023er/IND2023ER_formats.sps:22997`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2015.pdf` p. 89; `PSID/documentation/capture1/q2015.pdf` p. 104; `PSID/documentation/capture1/q2015.pdf` p. 166; `tests/data/track_u2/psid_docs/FAQ_20260813.html:833`; `EV/phase2-20260927/out/u2-adjudicate.md:105` | | `roles:2015.relationship.92` | RESOLVED | Code absent from complete wave format block; refuse if supplied. | `PSID/ind2023er/IND2023ER_formats.sps:22969`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 977; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 978; `docs/design/boomers2004_1946_55_comparison.md:152` | | `roles:2017.relationship.10` | RESOLVED | Reference Person in 2017; 2015 Reference Person who was mover-out nonresponse by the time of the 2017 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:24449`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2017.relationship.20` | RESOLVED | Legal Spouse in 2017; 2015 Spouse who was mover-out nonresponse by the time of the 2017 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:24450`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | | `roles:2017.relationship.22` | RESOLVED | Partner--cohabitor who has lived with Reference Person for 12 months or more; 2015 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:24451`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2017/FAM2017ER_codebook.pdf` p. 7 | -| `roles:2017.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 106). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:24476`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:106` | +| `roles:2017.relationship.88` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2017 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question. Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:24476`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:106` | | `roles:2017.relationship.90` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. q2017 p. 5 lists '901. Uncooperative Male Spouse' and '902. Uncooperative Female Spouse'; p. 95 routes the spouse income repeat to 'Spouse/Partner (CYAQRTH=201-222, CYFUHU=FU, FUMI)' and the Big OFUM series to 'AQRTH>222'. Codes 901/902 fall in the OFUM gate and outside the spouse loop. The declared legal-spouse annuity and marital-resolution conventions are unchanged. Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 90 is an OFUM income role outside the spouse slot in 2017, with the annuity and marital rules recorded here, and registration states the completeness_disclosure. The 2017 code-90 OFUM route is documented; how completely the uncooperative legal spouse's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2017ER pp. 2021-2028), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a). | `PSID/ind2023er/IND2023ER_formats.sps:24477`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2017.pdf` p. 5; `PSID/documentation/capture1/q2017.pdf` p. 95; `EV/phase2-20260927/out/u2-adjudicate.md:107` | | `roles:2017.relationship.92` | RESOLVED | Independent adjudication D (amendment 1): OFUM income collection and exclusion from the standard spouse loop are documented. q2017 p. 5 lists '921. Uncooperative Male Partner' and '922. Uncooperative Female Partner'; p. 95's spouse gate (CYAQRTH=201-222) excludes them and its OFUM gate (AQRTH>222) includes them. I p. 1097: 'Uncooperative partner of Reference Person'. The declared legal-spouse annuity and marital-resolution conventions are unchanged. Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1a this documented route governs: code 92 is an OFUM income role outside the spouse slot in 2017, with the annuity and marital rules recorded here, and registration states the completeness_disclosure. The 2017 code-92 OFUM route is documented; how completely the uncooperative partner's income was reported is not. FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. By deduction, missing OFUM amounts fall under PSID's documented processing of missing data ('All missing data were assigned', FAM2017ER pp. 2021-2028), but no document says how a member about whom all information was withheld was treated. Registration discloses this (section 16a amendment 1a). | `PSID/ind2023er/IND2023ER_formats.sps:24478`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1095; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1096; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1097; `docs/design/boomers2004_1946_55_comparison.md:152`; `PSID/documentation/capture1/q2017.pdf` p. 5; `PSID/documentation/capture1/q2017.pdf` p. 95; `EV/phase2-20260927/out/u2-adjudicate.md:108` | | `roles:2019.relationship.10` | RESOLVED | Reference Person in 2019; 2017 Reference Person who was mover-out nonresponse by the time of the 2019 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:25976`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2019.relationship.20` | RESOLVED | Legal Spouse in 2019; 2017 Spouse who was mover-out nonresponse by the time of the 2019 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:25977`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | | `roles:2019.relationship.22` | RESOLVED | Partner--cohabitor who has lived with Reference Person for 12 months or more; 2017 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:25978`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2019/fam2019er_codebook.pdf` p. 6 | -| `roles:2019.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 109). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:26003`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:109` | +| `roles:2019.relationship.88` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2019 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question. Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:26003`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:109` | | `roles:2019.relationship.90` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1e-1, ratified as an explicit amendment to draft-3 lines 152, 154 and 158 for 2019-2023: every 2019 family-unit observation that contains a code-90 or code-92 person is excluded from every row under the named disposition uncooperative spouse or partner in family unit, whether the target person is the reference person, the uncooperative spouse or partner, or another member. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. No income, Social Security or SSI is assigned to the code-90 uncooperative legal spouse and no OFUM routing is asserted for them (income_role excluded). This replaces the adjudication's refusal, kept as superseded_refusal. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md): the instrument asks 901/902 no Section G income questions in 2019 (spouse series CYAQRTH=201-222; Big and Little OFUM series 301-882 or 951-982), and PSID FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. No online document states whether any of the person's income enters the family-file aggregates. PSID's 2026-10-07 answer confirmed that these persons are skipped out of the Section G income questions and did not say whether their income is edited or imputed into the family-file aggregates (research record section 2); Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:26004`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2019.pdf` p. 103; `EV/phase2-20260927/out/u2-adjudicate.md:110` | | `roles:2019.relationship.92` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1e-1, ratified as an explicit amendment to draft-3 lines 152, 154 and 158 for 2019-2023: every 2019 family-unit observation that contains a code-90 or code-92 person is excluded from every row under the named disposition uncooperative spouse or partner in family unit, whether the target person is the reference person, the uncooperative spouse or partner, or another member. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. No income, Social Security or SSI is assigned to the code-92 uncooperative partner and no OFUM routing is asserted for them (income_role excluded). This replaces the adjudication's refusal, kept as superseded_refusal. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md): the instrument asks 921/922 no Section G income questions in 2019 (spouse series CYAQRTH=201-222; Big and Little OFUM series 301-882 or 951-982), and PSID FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. No online document states whether any of the person's income enters the family-file aggregates. PSID's 2026-10-07 answer confirmed that these persons are skipped out of the Section G income questions and did not say whether their income is edited or imputed into the family-file aggregates (research record section 2); Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:26005`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1225; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1226; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1227; `docs/design/boomers2004_1946_55_comparison.md:152`; `PSID/documentation/capture1/q2019.pdf` p. 103; `EV/phase2-20260927/out/u2-adjudicate.md:111` | | `roles:2021.relationship.10` | RESOLVED | Reference Person in 2021; 2019 Reference Person who was mover-out nonresponse by the time of the 20121 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:27558`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2021.relationship.20` | RESOLVED | Legal Spouse in 2021; 2019 Spouse who was mover-out nonresponse by the time of the 2021 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:27559`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7 | | `roles:2021.relationship.22` | RESOLVED | Partner--cohabitor who has lived with Reference Person for 12 months or more; 2019 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:27560`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2021/FAM2021ER_codebook.pdf` p. 7 | -| `roles:2021.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 112). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:27585`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:112` | +| `roles:2021.relationship.88` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2021 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question. Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:27585`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:112` | | `roles:2021.relationship.90` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1e-1, ratified as an explicit amendment to draft-3 lines 152, 154 and 158 for 2019-2023: every 2021 family-unit observation that contains a code-90 or code-92 person is excluded from every row under the named disposition uncooperative spouse or partner in family unit, whether the target person is the reference person, the uncooperative spouse or partner, or another member. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. No income, Social Security or SSI is assigned to the code-90 uncooperative legal spouse and no OFUM routing is asserted for them (income_role excluded). This replaces the adjudication's refusal, kept as superseded_refusal. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md): the instrument asks 901/902 no Section G income questions in 2021 (spouse series CYAQRTH=201-222; Big and Little OFUM series 301-882 or 951-982), and PSID FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. No online document states whether any of the person's income enters the family-file aggregates. PSID's 2026-10-07 answer confirmed that these persons are skipped out of the Section G income questions and did not say whether their income is edited or imputed into the family-file aggregates (research record section 2); Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:27586`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2021.pdf` p. 205; `EV/phase2-20260927/out/u2-adjudicate.md:113` | | `roles:2021.relationship.92` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1e-1, ratified as an explicit amendment to draft-3 lines 152, 154 and 158 for 2019-2023: every 2021 family-unit observation that contains a code-90 or code-92 person is excluded from every row under the named disposition uncooperative spouse or partner in family unit, whether the target person is the reference person, the uncooperative spouse or partner, or another member. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. No income, Social Security or SSI is assigned to the code-92 uncooperative partner and no OFUM routing is asserted for them (income_role excluded). This replaces the adjudication's refusal, kept as superseded_refusal. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md): the instrument asks 921/922 no Section G income questions in 2021 (spouse series CYAQRTH=201-222; Big and Little OFUM series 301-882 or 951-982), and PSID FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. No online document states whether any of the person's income enters the family-file aggregates. PSID's 2026-10-07 answer confirmed that these persons are skipped out of the Section G income questions and did not say whether their income is edited or imputed into the family-file aggregates (research record section 2); Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:27587`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1346; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1347; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1348; `docs/design/boomers2004_1946_55_comparison.md:152`; `PSID/documentation/capture1/q2021.pdf` p. 205; `EV/phase2-20260927/out/u2-adjudicate.md:114` | | `roles:2023.relationship.10` | RESOLVED | Reference Person in 2023; 2021 Reference Person who was mover-out nonresponse by the time of the 20121 interview; declared income role head; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:29154`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:144` | | `roles:2023.relationship.20` | RESOLVED | Legal Spouse in 2023; 2021 Spouse who was mover-out nonresponse by the time of the 2023 interview; declared income role spouse; legal-spouse annuity True; relationship-based marital resolution True. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:29155`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:145`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7 | | `roles:2023.relationship.22` | RESOLVED | Partner--cohabitor who has lived with Reference Person for 12 months or more; 2021 Partner who was mover-out...; declared income role spouse; legal-spouse annuity False; relationship-based marital resolution False. Administrative support is separate. | `PSID/ind2023er/IND2023ER_formats.sps:29156`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:146`; `PSID/family/2023/FAM2023ER_codebook.pdf` p. 7 | -| `roles:2023.relationship.88` | TO VERIFY | Only administrative birth support is inherited. Which family income/SSI slot and substantive annuity or marital rule, if any, is authorized for first-year cohabitors? No legal-spouse status follows from the label. Independent adjudication: A (genuinely ambiguous); needs u2-draft-4 amendment 1, pending Max's ruling (EV/phase2-20260927/out/u2-adjudicate.md line 115). Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:29181`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:115` | +| `roles:2023.relationship.88` | RESOLVED | Max's ruling d637 (2026-09-30; specification section 16b) ratified amendment 1's parts 1a-1c as written, as Max confirmed on 2026-10-12 (d745; section 16b). Under 1c a first-year cohabitor (2023 code 88) has an OFUM income role on the family basis, and its SSI falls in section 8's OFUM unit; does not occupy the spouse slot; is not a legal-spouse annuity life; never resolves the head's or reference person's marital history as married by relationship code alone; keeps its recorded legal marital history; and keeps the inherited administrative birth support. The OFUM income gates include codes 881/882 in every wave, and FAQ question 75 says boyfriends and girlfriends are treated like other family members who are not Reference Person, Spouse or Partner (section 16a amendment 1c). The adjudication's question is kept as superseded_question. Citation corrected (found by the u2-m1b builder, outside the adjudication's 18): docs/design/boomers2004_1946_55_comparison.md:198 is the 2013 row of the staged-materials table; line 190 states code 88's inherited administrative birth support and its open substantive routing. The adjudication checked only RESOLVED entries, so it did not review this TO VERIFY citation. | `PSID/ind2023er/IND2023ER_formats.sps:29181`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:190`; `EV/phase2-20260927/out/u2-adjudicate.md:115` | | `roles:2023.relationship.90` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1e-1, ratified as an explicit amendment to draft-3 lines 152, 154 and 158 for 2019-2023: every 2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under the named disposition uncooperative spouse or partner in family unit, whether the target person is the reference person, the uncooperative spouse or partner, or another member. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. No income, Social Security or SSI is assigned to the code-90 uncooperative legal spouse and no OFUM routing is asserted for them (income_role excluded). This replaces the adjudication's refusal, kept as superseded_refusal. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md): the instrument asks 901/902 no Section G income questions in 2023 (spouse series CYAQRTH=201-222; Big and Little OFUM series 301-882 or 951-982), and PSID FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. No online document states whether any of the person's income enters the family-file aggregates. PSID's 2026-10-07 answer confirmed that these persons are skipped out of the Section G income questions and did not say whether their income is edited or imputed into the family-file aggregates (research record section 2); Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:29182`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:154`; `PSID/documentation/capture1/q2023.pdf` p. 203; `EV/phase2-20260927/out/u2-adjudicate.md:116` | | `roles:2023.relationship.92` | RESOLVED | Max's ruling d1090 (2026-10-10; specification section 16c), section 16a option 1e-1, ratified as an explicit amendment to draft-3 lines 152, 154 and 158 for 2019-2023: every 2023 family-unit observation that contains a code-90 or code-92 person is excluded from every row under the named disposition uncooperative spouse or partner in family unit, whether the target person is the reference person, the uncooperative spouse or partner, or another member. The rule is fixed before any count and is vacuous if no such unit supplies an observation; the count is disclosed after the authorized structural pass and decides nothing. No income, Social Security or SSI is assigned to the code-92 uncooperative partner and no OFUM routing is asserted for them (income_role excluded). This replaces the adjudication's refusal, kept as superseded_refusal. Part B: PARTIAL (u2-m1b Part B, docs/design/u2_m1b_psid_research.md): the instrument asks 921/922 no Section G income questions in 2023 (spouse series CYAQRTH=201-222; Big and Little OFUM series 301-882 or 951-982), and PSID FAQ question 74 says the code is used when one half of the couple withholds information about the other or refuses to have their information included. No online document states whether any of the person's income enters the family-file aggregates. PSID's 2026-10-07 answer confirmed that these persons are skipped out of the Section G income questions and did not say whether their income is edited or imputed into the family-file aggregates (research record section 2); Max ruled on 2026-10-10 (d1090; specification section 16c). | `PSID/ind2023er/IND2023ER_formats.sps:29183`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1467; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1468; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 1469; `docs/design/boomers2004_1946_55_comparison.md:152`; `PSID/documentation/capture1/q2023.pdf` p. 203; `EV/phase2-20260927/out/u2-adjudicate.md:117` | | `support:1946.2013` | RESOLVED | Documentary calendar: birth 1946, age 66, income year 2012, wave 2013, multiplier 0.5; staged anchors documented; no empirical availability claim. Citation corrected (amendment 7, EV/phase2-20260927/out/u2-adjudicate.md line 44): docs/design/boomers2004_1946_55_comparison.md:67 is blank; line 88 is the specification's calendar row for birth year 1946. | `docs/design/boomers2004_1946_55_comparison.md:88`; `PSID/ind2023er/IND2023ER.sps:641`; `PSID/ind2023er/IND2023ER.sps:2849`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 918; `PSID/ind2023er/IND2023ER.sps:672`; `PSID/ind2023er/IND2023ER.sps:2943`; `PSID/ind2023er/IND2023ER_codebook.pdf` p. 976; `EV/phase2-20260927/out/u2-adjudicate.md:44` | diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 9805fc78c..197e27bb1 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -411,6 +411,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 1a: `roles.json` (the three documented routes and a d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the code-90 and code-92 blocker rows, the d637 section and three rows); `u2_source_registry.py` (1a's scope) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 1b: `roles.json` (2013's code-90 record and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the 2013 code-90 row and the d637 section); `u2_source_registry.py` (1b's scope, the first with no adjudication disposition) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 1c, builder: `sources.py` (the declared code-88 rule and its docstrings), `cohort.py` and `estimator.py` (docstrings), `invented.py` (the two code-88 variant notes) and `scripts/track_u2_dry_run.py` (the code-88 checks); `u1_identity.json`; the specification's §19; `tests/track_u2/test_u2_roles.py` and `test_u2_scripts.py`; and this section. +- Amendment 1c: `roles.json` (the six code-88 records and the d637 record), `income.json` (amendment 2's ten `use_restriction` sentences) and `u1_identity.json`; the specification's §17 item 2, §19 and §20 step 4; the master record (the summary, the code-88 and spouse age/sex blocker rows, the d637 section and sixteen per-route rows); `u2_source_registry.py` (1c's scope), `sources.py` (the roles and income byte pins and docstrings), `loader.py` (the structural-pass authorization), `invented.py` (two variant notes), `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` (`--authorized-by`), `scripts/track_u2_dry_run.py` and `scripts/run_track_u2_registered.py` (checks and docstrings); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py`, `test_u2_scripts.py` and `test_u2_loader.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/scripts/run_track_u2_registered.py b/scripts/run_track_u2_registered.py index 52da3f60a..bc128b70e 100644 --- a/scripts/run_track_u2_registered.py +++ b/scripts/run_track_u2_registered.py @@ -2,10 +2,11 @@ Specification sections 14 and 20. This is the only entry point that would compute the U2 statistic on real data. It is built but not -authorized: the specification is ``u2-draft-4`` (unratified; section -16a's amendments await Max's ruling) and the registries hold open and -refused routes, so its preflight refuses before any PSID file is -opened. :func:`preflight` verifies, in order: +authorized: the specification is ``u2-draft-4`` (not ratified; Max has +ruled on section 16a's amendments, d637 and d1090) and the +pre-registration evidence record does not exist, so its preflight +refuses (items 4 and 8) before any PSID file is opened. :func:`preflight` +verifies, in order: 1. the registration pointer is an issue #42 comment URL; 2. ``--registered-commit`` is a full SHA equal to ``HEAD`` and the @@ -41,8 +42,8 @@ ``--binding-sha256`` the #42 registration quotes; 10. the independent mapping review is complete: every registry entry a U2 run applies is RESOLVED with no blocker (the loader's source - preflight, which rechecks the plan), which refuses at the adjudicated - registries. + preflight, which rechecks the plan); since amendment 1c's registry + commit it passes at the committed registries. Only then are PSID files read (:func:`populace_dynamics. uniform_cut_track_u2.loader.load_u2_inputs` with the evidence record: it diff --git a/scripts/track_u2_component_diagnostics.py b/scripts/track_u2_component_diagnostics.py index 470af2f94..2bcc5807a 100644 --- a/scripts/track_u2_component_diagnostics.py +++ b/scripts/track_u2_component_diagnostics.py @@ -1,7 +1,7 @@ """F17 component diagnostics of the U2 population (no statistic; built). Specification section 9 ("Permitted before registration") and section 20 -step 4, which is separately authorized and has **not** been run. When +step 4, which needs Max's authorization and has **not** been run. When authorized it builds row U0's observations from the staged PSID (U2 loader, committed-registry role context) and writes ``track-u2-component-diagnostics.json``: Social Security, SSI and WEALTH1 @@ -11,13 +11,26 @@ It computes **no** income concept, annuity, threshold assignment, poverty status or poverty rate, and never imports the income concept, the tabulation, the runner, the parameter bundle or the invented -generator (:data:`FORBIDDEN_MODULES`, checked before and after). Until -every required route is resolved, the loader refuses before opening any -PSID file. +generator (:data:`FORBIDDEN_MODULES`, checked before and after). Since +amendment 1c's registry commit every required route is resolved, so the +loader's source preflight passes: the authorization is what stands +before the PSID records are read. + +**Authorization.** The pass reads real PSID records, so section 20 +step 4 needs Max's authorization, recorded as a chief-of-staff decision. +``--authorized-by `` names that decision, and the output +records it with the git head and the UTC time (``authorization``). The +flag only records the authorization; it does not grant one, and nothing +here reads the decision log. Without the flag, or with anything but a +decision id, the script exits non-zero before any PSID path is resolved +or opened; :func:`build` and the loader refuse the same way +(:func:`populace_dynamics.uniform_cut_track_u2.loader. +check_structural_pass_authorization`). Usage:: - python scripts/track_u2_component_diagnostics.py --output-dir DIR + python scripts/track_u2_component_diagnostics.py \\ + --authorized-by DECISION --output-dir DIR """ from __future__ import annotations @@ -26,6 +39,7 @@ import json import subprocess import sys +from collections.abc import Mapping from pathlib import Path from typing import Any @@ -61,11 +75,22 @@ def _git(*args: str) -> str: ).stdout.strip() -def build(data_dir: Path | None = None) -> dict[str, Any]: +def build( + data_dir: Path | None = None, + *, + authorization: Mapping[str, str] | None = None, +) -> dict[str, Any]: + authorization = loader.check_structural_pass_authorization(authorization) + if authorization["git_head"] != _git("rev-parse", "HEAD"): + raise loader.U2AuthorizationRefusal( + "the authorization was recorded at another git head" + ) leaked = _leaked() if leaked: raise RuntimeError(f"forbidden modules already imported: {leaked}") - inputs = loader.load_u2_inputs(data_dir=data_dir) + inputs = loader.load_u2_inputs( + data_dir=data_dir, authorization=authorization + ) built = cohort.build_u2_cohort( inputs, cohort.U2CohortSpec(), @@ -86,6 +111,7 @@ def build(data_dir: Path | None = None) -> dict[str, Any]: "component aggregates only: no income concept, threshold or " "poverty rate", ], + "authorization": authorization, "code_commit": _git("rev-parse", "HEAD"), "code_clean": _git("status", "--porcelain") == "", "diagnostics": result, @@ -97,8 +123,24 @@ def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--output-dir", type=Path, required=True) parser.add_argument("--data-dir", type=Path, default=None) + parser.add_argument( + "--authorized-by", + metavar="DECISION", + default=None, + help=( + "the chief-of-staff decision recording Max's authorization of " + "the section 20 step-4 pass (e.g. d1234); recorded in the " + "output, it does not grant the authorization" + ), + ) args = parser.parse_args(argv) - result = build(args.data_dir) + try: + authorization = loader.structural_pass_authorization( + args.authorized_by, git_head=_git("rev-parse", "HEAD") + ) + except loader.U2AuthorizationRefusal as refusal: + parser.exit(2, f"{parser.prog}: refused: {refusal}\n") + result = build(args.data_dir, authorization=authorization) args.output_dir.mkdir(parents=True, exist_ok=True) path = args.output_dir / "track-u2-component-diagnostics.json" path.write_text( diff --git a/scripts/track_u2_dry_run.py b/scripts/track_u2_dry_run.py index 410f8bb8e..7f473191a 100644 --- a/scripts/track_u2_dry_run.py +++ b/scripts/track_u2_dry_run.py @@ -18,13 +18,13 @@ * every family-record mapping round-trips through the registry layouts (invented fixed-width records: losses, sentinels, top codes, the seven/eight-asset WEALTH1 identities, every employer-DC route); -* under the committed registries, the source gate refuses each wave the - milestone-1 record leaves open, the registry role context refuses the - TO VERIFY relationship codes (code 88 until amendment 1c's registry - commit), the declared context builds a code-88 first-year cohabitor's - family under amendment 1c's OFUM convention with every observation - unchanged, and the loader's preflight refuses before any PSID file is - opened; +* under the committed registries, the source gate applies every wave's + family records, the registry role context refuses only code 92 where + it is absent (2013 and 2015), both role contexts build a code-88 + first-year cohabitor's family under amendment 1c's OFUM convention + with every observation unchanged, the loader's source preflight passes + (no blocker remains), and an unfrozen load without Max's recorded + authorization refuses before any PSID path is resolved; * the registered runner refuses these invented inputs and parameters; * each tested route from other data to the invented declared rules and routes refuses: relabelled or hand-copied invented provenance, frames @@ -99,6 +99,8 @@ tabulation, ) +#: A data directory that does not exist, for the unauthorized-load check. +_SENTINEL_PSID_DIR = Path("/nonexistent/track-u2-dry-run-psid") #: A syntactically valid pointer used only to show that the registered #: path refuses invented inputs (no such comment is implied). _GUARD_POINTER = ( @@ -883,8 +885,8 @@ def checks( ), # Amendment 1c (d637, confirmed by d745): a code-88 # first-year cohabitor has the OFUM role outside the spouse - # slot, so adding one changes no observation or disposition; - # a context that leaves code 88 open refuses. + # slot, so adding one changes no observation or disposition + # under either context. "code_88_under_registry_context": _code_88_check( inputs, code88, registry_roles ), @@ -930,6 +932,12 @@ def checks( ), "invented_variants": _variants(inputs, params), "loader_preflight": _refusal(loader.source_preflight), + # Section 20 step 4 needs Max's recorded authorization: without + # it an unfrozen load refuses before any PSID path is resolved + # (the sentinel directory does not exist). + "unauthorized_unfrozen_load": _refusal( + lambda: loader.load_u2_inputs(data_dir=_SENTINEL_PSID_DIR) + ), "registered_guard_refuses_invented_inputs": _refusal( lambda: runner.run_track_u2( inputs, @@ -1178,8 +1186,11 @@ def results_markdown(result: dict[str, Any]) -> str: f"{role['code_88_under_declared_context'].get('observations_equal')}" "). Refusals: declared context on non-invented inputs " f"{role['declared_context_on_non_invented_inputs']['refused']}.", - "- Loader preflight refuses before any PSID file is opened: " - f"{check['loader_preflight']['refused']}.", + "- Loader source preflight passes at the committed registries " + f"(no blocker): {not check['loader_preflight']['refused']}; an " + "unfrozen load without Max's recorded authorization refuses before " + "any PSID path is resolved: " + f"{check['unauthorized_unfrozen_load']['refused']}.", "- Tested routes from other data to the invented declared rules " "and routes, each refused: " + ", ".join( diff --git a/scripts/track_u2_structure.py b/scripts/track_u2_structure.py index 2e070cf22..4e319f20b 100644 --- a/scripts/track_u2_structure.py +++ b/scripts/track_u2_structure.py @@ -1,8 +1,8 @@ """Structural counts of the U2 age-67 population (counts only; built). Specification sections 4 and 14: the pre-registration structural, -join and reconciliation pass (section 20 step 4), which is separately -authorized and has **not** been run. When authorized it builds the U0 +join and reconciliation pass (section 20 step 4), which needs Max's +authorization and has **not** been run. When authorized it builds the U0 and U1 observations from the staged PSID through the U2 loader and the committed-registry role context and writes ``track-u2-structure.json``: dispositions, observations, roles, pairings, design strata, the family @@ -21,13 +21,26 @@ It computes **no** income concept, annuity, threshold, poverty status or poverty rate and never imports the income concept or the tabulation -(:data:`FORBIDDEN_MODULES`, checked before and after). Until every -required route is resolved, the loader refuses before opening any PSID -file, so running it at the adjudicated registries writes nothing. +(:data:`FORBIDDEN_MODULES`, checked before and after). Since amendment +1c's registry commit every required route is resolved, so the loader's +source preflight passes: the authorization is what stands before the +PSID records are read. + +**Authorization.** The pass reads real PSID records, so section 20 +step 4 needs Max's authorization, recorded as a chief-of-staff decision. +``--authorized-by `` names that decision, and the output +records it with the git head and the UTC time (``authorization``). The +flag only records the authorization; it does not grant one, and nothing +here reads the decision log. Without the flag, or with anything but a +decision id, the script exits non-zero before any PSID path is resolved +or opened; :func:`build` and the loader refuse the same way +(:func:`populace_dynamics.uniform_cut_track_u2.loader. +check_structural_pass_authorization`). Usage:: - python scripts/track_u2_structure.py --output-dir DIR + python scripts/track_u2_structure.py --authorized-by DECISION \\ + --output-dir DIR """ from __future__ import annotations @@ -36,6 +49,7 @@ import json import subprocess import sys +from collections.abc import Mapping from pathlib import Path from typing import Any @@ -71,11 +85,22 @@ def _git(*args: str) -> str: ).stdout.strip() -def build(data_dir: Path | None = None) -> dict[str, Any]: +def build( + data_dir: Path | None = None, + *, + authorization: Mapping[str, str] | None = None, +) -> dict[str, Any]: + authorization = loader.check_structural_pass_authorization(authorization) + if authorization["git_head"] != _git("rev-parse", "HEAD"): + raise loader.U2AuthorizationRefusal( + "the authorization was recorded at another git head" + ) leaked = _leaked() if leaked: raise RuntimeError(f"forbidden modules already imported: {leaked}") - inputs = loader.load_u2_inputs(data_dir=data_dir) + inputs = loader.load_u2_inputs( + data_dir=data_dir, authorization=authorization + ) registries = sources.RegistrySet.committed() roles = sources.RoleContext.from_registry(registries) births = cohort.derive_u2_births(inputs) @@ -112,6 +137,7 @@ def build(data_dir: Path | None = None) -> dict[str, Any]: "was computed." ), "labels": ["PSID-realized outcomes (not a projection)", "counts only"], + "authorization": authorization, "code_commit": _git("rev-parse", "HEAD"), "provenance": { k: v @@ -131,8 +157,24 @@ def main(argv: list[str] | None = None) -> int: parser = argparse.ArgumentParser(description=__doc__.splitlines()[0]) parser.add_argument("--output-dir", type=Path, required=True) parser.add_argument("--data-dir", type=Path, default=None) + parser.add_argument( + "--authorized-by", + metavar="DECISION", + default=None, + help=( + "the chief-of-staff decision recording Max's authorization of " + "the section 20 step-4 pass (e.g. d1234); recorded in the " + "output, it does not grant the authorization" + ), + ) args = parser.parse_args(argv) - result = build(args.data_dir) + try: + authorization = loader.structural_pass_authorization( + args.authorized_by, git_head=_git("rev-parse", "HEAD") + ) + except loader.U2AuthorizationRefusal as refusal: + parser.exit(2, f"{parser.prog}: refused: {refusal}\n") + result = build(args.data_dir, authorization=authorization) args.output_dir.mkdir(parents=True, exist_ok=True) path = args.output_dir / "track-u2-structure.json" path.write_text( diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index 43635e8b6..cbf548802 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -147,6 +147,34 @@ "required": ("ruling_effect",), "markers": (), }, + # Code 88's six records (adjudication A), TO VERIFY until the ruling, + # resolve under the first-year cohabitor convention. + "1c": { + "registry": "roles", + "entries": tuple( + f"{wave}.relationship.88" for wave in (2013, *_LATER_WAVES) + ), + "ruling": {**_D637, "amendment": "1c", **_D745}, + "disposition": "A", + "fields": { + "status": "RESOLVED", + "present": True, + "action": "convention_per_d637_1c", + "income_role": "ofum", + "spouse_slot": False, + "legal_spouse_annuity": False, + "marital_resolution": False, + "administrative_birth_support": True, + }, + "absent": ( + "refusal", + "question", + "blocking_dependencies", + "family_unit_exclusion", + ), + "required": ("resolution", "superseded_question"), + "markers": ("action",), + }, "2": { "registry": "income", "entries": tuple( diff --git a/src/populace_dynamics/uniform_cut_track_u2/__init__.py b/src/populace_dynamics/uniform_cut_track_u2/__init__.py index 8f612e042..e79f47d89 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/__init__.py +++ b/src/populace_dynamics/uniform_cut_track_u2/__init__.py @@ -38,7 +38,9 @@ * :mod:`.memo` -- the comparison-memo classification rules of section 10a (no comparator value is held anywhere in the repository); * :mod:`.loader` -- the registered real-data loader, which refuses before - opening any PSID record while a required route is unresolved. + opening any PSID record while a required route is unresolved, and + refuses an unfrozen load (the section 20 step-4 pass) without Max's + recorded authorization. Every output is a *PSID-realized outcome (not a projection)* computed with a *Python income concept (not Axiom)* under *mechanical incidence*. diff --git a/src/populace_dynamics/uniform_cut_track_u2/invented.py b/src/populace_dynamics/uniform_cut_track_u2/invented.py index 6a3bd1301..444663383 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/invented.py +++ b/src/populace_dynamics/uniform_cut_track_u2/invented.py @@ -1551,8 +1551,7 @@ def _sex_unknown_target_in_excluded_unit_2019( "under amendment 1c's convention (d637, confirmed by d745) it " "has the OFUM income role outside the spouse slot, is no annuity " "life and resolves no marital history, so every observation and " - "disposition is unchanged; a role context that leaves code 88 " - "open refuses" + "disposition is unchanged under either role context" ), "missing_family_record_2019": ( "the first 2019 family-file income record is dropped; an " @@ -1594,8 +1593,8 @@ def _sex_unknown_target_in_excluded_unit_2019( "a code-88 first-year cohabitor joins the 2019 family of U0's " "first observation that the section 16c B2 exclusion removes: " "under amendment 1c it has the OFUM role, and the unit stays " - "excluded, counted under its disposition; a role context that " - "leaves code 88 open refuses, never hidden by the exclusion" + "excluded, counted under its disposition, under either role " + "context" ), "zero_weight_target_in_excluded_unit_2015": ( "the target of U0's first 2015 observation that the B1 exclusion " diff --git a/src/populace_dynamics/uniform_cut_track_u2/loader.py b/src/populace_dynamics/uniform_cut_track_u2/loader.py index 029c04560..c9fdce98d 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/loader.py +++ b/src/populace_dynamics/uniform_cut_track_u2/loader.py @@ -3,7 +3,9 @@ Specification sections 4 and 14 ("Registered-run preflight: before reading raw PSID records, verify ... completed independent mapping review"). :func:`load_u2_inputs` runs in three stages and never reaches -a later stage when an earlier one refuses: +a later stage when an earlier one refuses. Before them, an unfrozen load +(the section 20 step-4 pass) refuses without Max's recorded authorization +(**Authorization** below). 1. **Source preflight** (:func:`source_preflight`), before any file under the PSID directory is opened: every registry entry a U2 run @@ -11,17 +13,17 @@ each support wave, the anchors, weights, design and sex variables, the support plan and the role of every amended relationship code -- must be RESOLVED with no blocking dependency, under the committed, - pinned registries. It refuses at the committed registries: code 88 - is TO VERIFY in every wave (amendment 1c, pending Max's confirmation, - d745). The d1090 - registry commit (section 16c) replaced the refused 2015 code-20 and - 2019-2023 code-90/92 routes with the ratified family-unit exclusions, - which released the income and pension entries that depended on them; - the d637 registry commits (section 16b) resolve the ratified - amendments one by one: amendment 2's spouse age/sex slot metadata, - amendment 3's crosswalks, amendment 4's wealth flags, 5b's 2015 P62A - checkpoint, 5a's 2017-2023 P64/P65 route and amendment 6's 2017 - weight. + pinned registries. Since amendment 1c's registry commit it passes at + the committed registries: no blocker remains. The d1090 registry + commit (section 16c) replaced the refused 2015 code-20 and 2019-2023 + code-90/92 routes with the ratified family-unit exclusions, which + released the income and pension entries that depended on them; the + d637 registry commits (section 16b) resolved the ratified amendments + one by one: amendment 1's parts 1a-1c (as Max confirmed on + 2026-10-12, d745; 1c resolves code 88 in every wave), amendment 2's + spouse age/sex slot metadata, amendment 3's crosswalks, amendment 4's + wealth flags, 5b's 2015 P62A checkpoint, 5a's 2017-2023 P64/P65 route + and amendment 6's 2017 weight. 2. **Source identity**: each staged setup file and codebook the registries cite is rehashed against the registry's recorded SHA-256, and every registry layout -- the family files' and the individual @@ -40,6 +42,17 @@ (:class:`U2UndocumentedValue`), as the family-file parse refuses out-of-domain values (``sources._check_domain``). +**Authorization.** The section 20 step-4 pass reads real PSID records, +so it needs Max's authorization, recorded as a chief-of-staff decision. +:func:`structural_pass_authorization` records the decision id the pass +names (``--authorized-by`` in ``scripts/track_u2_structure.py`` and +``scripts/track_u2_component_diagnostics.py``), the git head and the UTC +time; an unfrozen :func:`load_u2_inputs` refuses without that record, +before the source preflight and before any PSID path is resolved +(:class:`U2AuthorizationRefusal`). The record only records the +authorization; it does not grant one. The registered run's load is +frozen instead: its evidence binds every file and frame digest. + **Frozen identities** (section 14: "changed source bytes, changed frame digests ... refuse execution. Pre-registration mapping checks resolve these before the one-shot; execution rechecks their frozen @@ -56,16 +69,19 @@ hash, which a registered run requires. This module computes no income concept, threshold, annuity or poverty -status. No real PSID record is read in development: the tests run -stage 1 as committed (it refuses) and stages 2 and 3 on an invented -staged directory, with the registry blockers, the cited-source rehash -and the two inherited readers patched +status. No real PSID record is read in development: the tests run the +authorization check and stage 1 as committed (an unauthorized unfrozen +load refuses before any PSID path is resolved; stage 1 passes and opens +no PSID file) and stages 2 and 3 on an invented staged directory, with +the registry blockers, the cited-source rehash and the two inherited +readers patched (``tests/track_u2/test_u2_loader.py``). """ from __future__ import annotations import dataclasses +import datetime import hashlib import json import re @@ -84,6 +100,7 @@ "ANCHOR_CONCEPTS", "PREREGISTRATION_EVIDENCE_PATH", "PREREGISTRATION_SCHEMA", + "U2AuthorizationRefusal", "U2FrozenIdentityMismatch", "U2LoaderRefusal", "U2UndocumentedValue", @@ -92,6 +109,7 @@ "check_layouts_against_setup", "check_preregistration_evidence", "check_source_hashes", + "check_structural_pass_authorization", "family_record_specs", "files_bundle_sha256", "individual_specs", @@ -102,6 +120,7 @@ "read_preregistration_evidence", "required_entries", "source_preflight", + "structural_pass_authorization", ] #: The individual-file anchors of each support wave (section 3 table). @@ -172,6 +191,22 @@ "input_frames_sha256", ) _HEX64 = re.compile(r"[0-9a-f]{64}") +#: A chief-of-staff decision id: ``d`` and its number (e.g. ``d745``). +_DECISION_ID = re.compile(r"d[1-9][0-9]*") +_HEX40 = re.compile(r"[0-9a-f]{40}") +_UTC_SECONDS = "%Y-%m-%dT%H:%M:%SZ" +#: The fields of a structural-pass authorization record. +_AUTHORIZATION_KEYS: tuple[str, ...] = ( + "decision", + "git_head", + "recorded_at_utc", +) +_UNAUTHORIZED = ( + "the section 20 step-4 structural pass reads real PSID records, so it " + "needs Max's authorization, recorded as a chief-of-staff decision; " + "name that decision with --authorized-by (e.g. d1234). " + "The flag only records the authorization; it does not grant one" +) class U2LoaderRefusal(sources.U2SourceRefusal): @@ -189,6 +224,65 @@ class U2FrozenIdentityMismatch(U2LoaderRefusal): frozen pre-registration evidence (section 14).""" +class U2AuthorizationRefusal(U2LoaderRefusal): + """An unfrozen load (the section 20 step-4 pass) without Max's + recorded authorization: it refuses before any PSID path is + resolved.""" + + +def structural_pass_authorization( + decision: str | None, *, git_head: str +) -> dict[str, str]: + """The authorization record of a section 20 step-4 pass, or refuse. + + The pass reads real PSID records, so it needs Max's authorization, + recorded as a chief-of-staff decision. ``decision`` names that + decision (the structural scripts' ``--authorized-by``); the record + holds it, the git head the pass runs at and the UTC time it was + recorded. Naming a decision records the authorization; it does not + grant one, and nothing here reads the decision log. Without a + decision id this refuses (:class:`U2AuthorizationRefusal`). + """ + + return check_structural_pass_authorization( + { + "decision": decision, + "git_head": git_head, + "recorded_at_utc": datetime.datetime.now( + datetime.timezone.utc + ).strftime(_UTC_SECONDS), + } + ) + + +def check_structural_pass_authorization(record: Any) -> dict[str, str]: + """Return ``record`` checked, or refuse: exactly a decision id, the + full git head and a UTC timestamp (``None`` refuses).""" + + if not isinstance(record, Mapping) or set(record) != set( + _AUTHORIZATION_KEYS + ): + raise U2AuthorizationRefusal(_UNAUTHORIZED) + decision = record["decision"] + if not isinstance(decision, str) or not _DECISION_ID.fullmatch(decision): + raise U2AuthorizationRefusal(f"{_UNAUTHORIZED} (got {decision!r})") + head = record["git_head"] + if not isinstance(head, str) or not _HEX40.fullmatch(head): + raise U2AuthorizationRefusal( + "an authorization record names the full git head the pass " + f"runs at (got {head!r})" + ) + stamp = record["recorded_at_utc"] + try: + datetime.datetime.strptime(stamp, _UTC_SECONDS) + except (TypeError, ValueError): + raise U2AuthorizationRefusal( + "an authorization record holds the UTC time it was recorded " + f"as YYYY-MM-DDTHH:MM:SSZ (got {stamp!r})" + ) from None + return {key: record[key] for key in _AUTHORIZATION_KEYS} + + def required_entries( registries: sources.RegistrySet, ) -> list[tuple[str, str]]: @@ -443,11 +537,19 @@ def load_u2_inputs( *, data_dir: Path | None = None, evidence: Mapping[str, Any] | None = None, + authorization: Mapping[str, Any] | None = None, ) -> cohort.U2Inputs: """Read every U2 input from the staged PSID, or refuse first. - Stage 1 (:func:`source_preflight`) refuses at the adjudicated - registries, before any PSID file is opened. + An unfrozen load (no ``evidence``: the section 20 step-4 pass) reads + real PSID records with nothing frozen to check them against, so it + needs ``authorization``, the record of Max's authorization + (:func:`structural_pass_authorization`); without it the load refuses + before the source preflight and before any PSID path is resolved + (:class:`U2AuthorizationRefusal`). Stage 1 (:func:`source_preflight`) + then refuses on any registry blocker, before any PSID file is opened; + since amendment 1c's registry commit it passes at the committed + registries. ``evidence`` is the frozen pre-registration record (:func:`check_preregistration_evidence`). The registered run passes @@ -458,6 +560,8 @@ def load_u2_inputs( (:class:`U2FrozenIdentityMismatch`). """ + if evidence is None: + check_structural_pass_authorization(authorization) frozen = ( None if evidence is None else check_preregistration_evidence(evidence) ) diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index 963be597f..cec12ab46 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -91,9 +91,9 @@ 2013-2017 code 90 and 2017 code 92 (adjudication disposition D) and, by its d1090 commit, the same exclusions for 2015 code 20 and 2019-2023 codes 90 and 92, which the adjudication had refused (disposition F); the -registry gate follows the registry. The registry leaves code 88 TO -VERIFY until amendment 1c's registry commit, so until then the -registry context refuses it. +registry gate follows the registry. The roles registry records the +same convention for code 88 in every wave (amendment 1c's registry +commit), so the registry context applies it too. Any other in-family code keeps the inherited OFUM income role with no spouse status. Legacy identifiers ``wife``, ``wife_present`` and @@ -260,10 +260,13 @@ #: individual registry records amendment 6's release. Max's d745 #: confirmation (2026-10-12) of amendment 1a-1c moved the roles bytes: #: 1a records the ruling on the documented code-90/92 routes, 1b on 2013's -#: inherited code-90 meaning. +#: inherited code-90 meaning, and 1c resolves the six code-88 records +#: under the first-year cohabitor convention (no blocker remains); the 1c +#: commit also moved the income bytes, where amendment 2's ten records say +#: a code-88 cohabitor does not occupy the spouse slot. REGISTRY_SHA256: dict[str, str] = { "income": ( - "ddf49639c5678c35d525438b595393d7c55cba00956c23e42f68515ac56446d5" + "9598798b6a5924e71f15f0c3cff6ca445e096540d46f33e87c6d07ff016916e5" ), "wealth": ( "4fa58c6621738f071fbbc3e0e4ec3b2f6f0db9bcb6a942b43684137aa829a716" @@ -275,7 +278,7 @@ "bd22e07a8576ea9239818ca40377839c5c3151fffb403f3e5f1ef1549da546cf" ), "roles": ( - "3a2924122555a612d976f9dbd3e4d764f133fede0467a65b2c9eb67932e60c51" + "014b4402f65d5cda2ecbc2c038c6b6dc408d2e994ebcc5001de926537d103674" ), "support": ( "533c39187214fef6626e565362b2d97ff393b8d1f0034ffe365a7c87dadada6d" @@ -1011,11 +1014,10 @@ class RoleContext: """The role rules a U2 build applies, and whether it may run on data. ``registry``: the committed roles registry; any code the loader's - ``require_resolved`` refuses, refuses here (code 88 in every wave, - TO VERIFY until amendment 1c's registry commit, and code 92's - absence in 2013 and 2015; until the d1090 commit also 2015 code 20 - and 2019-2023 codes 90 and 92, which now carry section 16c's - exclusions). ``invented_declared``: the declared rules, section 3's + ``require_resolved`` refuses, refuses here (code 92's absence in 2013 + and 2015; until amendment 1c's registry commit also code 88 in every + wave, and until the d1090 commit 2015 code 20 and 2019-2023 codes 90 + and 92, which now carry section 16c's exclusions). ``invented_declared``: the declared rules, section 3's table with section 16c's two family-unit exclusions and amendment 1c's code-88 convention; accepted only with invented inputs. diff --git a/tests/README-tiers.md b/tests/README-tiers.md index be57615ea..196b7465c 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,498 | -| `artifact` | 4,153 | -| `integration_psid` | 1,400 | +| `unit` | 7,499 | +| `artifact` | 4,186 | +| `integration_psid` | 1,401 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,879** | +| **Total** | **13,914** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 07830b191..c18185f47 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,9 +1,9 @@ { "schema_version": 1, "counts": { - "unit": 7498, - "artifact": 4153, - "integration_psid": 1400, + "unit": 7499, + "artifact": 4186, + "integration_psid": 1401, "reproduction_legacy": 520, "oracle_policyengine": 308 } diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index 139932702..e2c3712e1 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -59,14 +59,13 @@ def quoted_spans(markdown): # by a ruling, TO VERIFY). Max's d1090 ruling (2026-10-10; spec section # 16c) replaced all eight F refusals (roles 7, pension 1). The d637 # registry commits (2026-09-30 ruling; spec section 16b) resolve the A -# items of amendments 2, 3, 5 and 6: so far amendment 2's ten income -# records and amendment 3's six. +# items of amendments 1c, 2, 3, 5 and 6, every one now. EXPECTED_COUNTS = { "income": (764, 0, 0), "wealth": (234, 0, 0), "individual": (59, 0, 0), "pension": (648, 0, 0), - "roles": (30, 0, 6), + "roles": (36, 0, 0), "support": (16, 0, 0), "weights": (7, 0, 0), "design": (2, 0, 0), @@ -74,14 +73,15 @@ def quoted_spans(markdown): SSI_AND_CENSUS_RESOLVED = 26 #: TO VERIFY records left: the adjudication's 24 A items less those the #: d637 commits resolved (amendment 2: 10; amendment 3: 6; 5b: 1; 5a's -#: four entries were D items, RESOLVED already; amendment 6: 1). The six -#: left are code 88's (amendment 1c), pending Max's confirmation (d745). -OPEN_ITEMS = 6 +#: four entries were D items, RESOLVED already; amendment 6: 1; and, as +#: Max confirmed on 2026-10-12 (d745), amendment 1c: code 88's 6). +OPEN_ITEMS = 0 #: Each d637 amendment a registry commit has applied (section 16b), with #: the adjudication disposition its entries carry. D637_APPLIED = { "1a": "D", "1b": None, + "1c": "A", "2": "A", "3": "A", "4": "D", @@ -557,9 +557,9 @@ def test_d637_amendment_2_spouse_metadata_never_decides_the_slot(documents): """Amendment 2 (section 16a; ratified by d637): the later family-file spouse age and sex are slot metadata only. Each record quotes its own inapplicability wording as wording that decides no slot, defers slot - occupancy to the roles registry (code 88's left open, pending d745), - and names the recorded sex and derived age as what age- and - sex-dependent rules use.""" + occupancy to the roles registry (under amendment 1c a code-88 + cohabitor does not occupy the slot), and names the recorded sex and + derived age as what age- and sex-dependent rules use.""" entries = {e["id"]: e for e in documents["income"]["entries"]} for key in registry.RATIFIED_AMENDMENTS["2"]["entries"]: entry = entries[key] @@ -572,13 +572,17 @@ def test_d637_amendment_2_spouse_metadata_never_decides_the_slot(documents): f"('{wording.group(1)}') never decide whether a person occupies " "the spouse slot" in rule ), key - # Occupancy is the role rules', not a closed list of codes, so the - # rule decides nothing about code 88 (amendment 1c, pending d745). + # Occupancy is the role rules', not a closed list of codes; code 88 + # is outside the slot by amendment 1c (d637, confirmed by d745). assert ( "slot occupancy comes from the relationship rules, as the roles " "registry's spouse_slot field records them" in rule ) - assert "code 88's slot status stays TO VERIFY" in rule + assert ( + "a code-88 first-year cohabitor does not occupy the slot " + "(amendment 1c)" in rule + ) + assert "TO VERIFY" not in rule and "await" not in rule assert "code-20 or code-22 person" not in rule assert "recorded sex (ER32000) and derived income-year age" in rule # 2013 is U1's wave: its spouse age and sex keep U1's mapping. @@ -641,7 +645,8 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): D637_TEXT_SHA256 = { "1a": ("2f4bca660f85b99cac371c970bb02fe3ae3978e5e52c12d661dd3fdd5690b88d"), "1b": ("ca251770719feb29812705e2ef7d3868ec6f95a925806e40792f1fda63c69be8"), - "2": ("64a67aae69d9d0bd6c6743328e8169304708003845395830ad628a6ee708887d"), + "1c": ("23767199b72112d6e686ad7fd99bcbcf77a7225f511ef5257817d509edef87cb"), + "2": ("6f6df518e66bd3588b6bed230c0c21bd6acc07db7586c4b38fa65c91719a9099"), "3": ("8d93d501819d981c6605b02fde3ff89d05488738146757bd179a84c02e131598"), "4": ("0b79677a5d1b41a76b17709152bec0d3789d118c75859b9716ea505c3413cd9c"), "5b": ("ca59c757b3b2b3f9402643c51ae910c16f9dd5927848d03fed327172cb38b513"), @@ -679,14 +684,49 @@ def test_d637_amendment_6_uses_er34651_as_released(documents): ("ruling_effect", "the co-resident legal-spouse annuity life"), ("ruling_effect", "relationship-based marital resolution"), ], + "1c": [ + ( + "resolution", + "ratified amendment 1's parts 1a-1c as written, as Max confirmed " + "on 2026-10-12 (d745; section 16b)", + ), + ( + "resolution", + "Under 1c a first-year cohabitor ({wave} code 88) has an OFUM " + "income role on the family basis", + ), + ("resolution", "its SSI falls in section 8's OFUM unit"), + ("resolution", "does not occupy the spouse slot"), + ("resolution", "is not a legal-spouse annuity life"), + ( + "resolution", + "never resolves the head's or reference person's marital " + "history as married by relationship code alone", + ), + ("resolution", "keeps its recorded legal marital history"), + ("resolution", "keeps the inherited administrative birth support"), + ( + "superseded_question", + "Which family income/SSI slot and substantive annuity or " + "marital rule, if any, is authorized for first-year cohabitors?", + ), + ], "2": [ ("use_restriction", "Slot metadata only (u2-draft-4 amendment 2)."), ( "use_restriction", "never decide whether a person occupies the spouse slot", ), - ("use_restriction", "code 88's slot status stays TO VERIFY"), - ("use_restriction", "whose parts 1a-1c await Max's confirmation"), + ( + "use_restriction", + "a code-88 first-year cohabitor does not occupy the slot " + "(amendment 1c)", + ), + ( + "use_restriction", + "whose parts 1a-1c d637 ratified, as Max confirmed on " + "2026-10-12, d745", + ), ("use_restriction", "never this variable."), ("resolution", "ratified u2-draft-4 amendment 2"), ], @@ -789,11 +829,18 @@ def test_d637_texts_are_pinned(documents): #: (registry, entry, field, old, new): each changes what a d637 entry #: says and must fail ``check_d637_texts``. D637_TEXT_MUTATIONS = [ + ( + "roles", + "2019.relationship.88", + "resolution", + "; does not occupy the spouse slot", + "", + ), ( "income", "income.2019.wife_age", "use_restriction", - ", and code 88's slot status stays TO VERIFY", + ", and a code-88 first-year cohabitor does not occupy the slot", "", ), ( @@ -1456,10 +1503,14 @@ def test_section_16a_recommendations_and_options(): "records under 5a and resolved the 2017 weight ER34651 under " "amendment 6's option 1; the \N{SECTION SIGN}15 fragment itself is " "materialized under item 9. Max confirmed 1a\N{EN DASH}1c on " - "2026-10-12 (d745), and the 1a and 1b commits recorded the ruling on " + "2026-10-12 (d745), and the 1a, 1b and 1c commits recorded the " + "ruling on " "the documented 2015 code-90 and 2017 code-90 and code-92 routes and " - "on 2013's inherited code-90 meaning. The 6 TO VERIFY records left " - "are code 88's, open until amendment 1c's registry commit lands.", + "on 2013's inherited code-90 meaning, and resolved the six code-88 " + "records under the first-year cohabitor convention. No TO VERIFY " + "record remains, and the loader's source preflight passes; the " + "structural pass still needs Max's authorization (item 8; " + "\N{SECTION SIGN}20 step 4).", "8. Complete the pre-registration structural, reconciliation and " "F17 component pass, including only the counts that the ratified B1 " "and B2 options name (under the 2026-10-10 ruling, the counts " diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 985dd8320..42ce345ca 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -1052,6 +1052,80 @@ def _copy_ruling(document, source, target): d, "2013.relationship.90", "2013.relationship.20" ), ), + "a1c_reopened": ( + "roles", + lambda d: _entry(d, "2019.relationship.88").update(status="TO VERIFY"), + ), + "a1c_into_the_slot": ( + "roles", + lambda d: _entry(d, "2021.relationship.88").update(spouse_slot=True), + ), + "a1c_legal_spouse_annuity": ( + "roles", + lambda d: _entry(d, "2013.relationship.88").update( + legal_spouse_annuity=True + ), + ), + "a1c_marital_resolution": ( + "roles", + lambda d: _entry(d, "2017.relationship.88").update( + marital_resolution=True + ), + ), + "a1c_no_income_role": ( + "roles", + lambda d: _entry(d, "2015.relationship.88").update(income_role="head"), + ), + "a1c_birth_support_dropped": ( + "roles", + lambda d: _entry(d, "2023.relationship.88").update( + administrative_birth_support=False + ), + ), + "a1c_refusal_restored": ( + "roles", + lambda d: _entry(d, "2019.relationship.88").update( + action="refuse_substantive_routing" + ), + ), + "a1c_question_kept_open": ( + "roles", + lambda d: _entry(d, "2019.relationship.88").update( + question=_entry(d, "2019.relationship.88")["superseded_question"] + ), + ), + "a1c_question_dropped": ( + "roles", + lambda d: _entry(d, "2013.relationship.88").pop("superseded_question"), + ), + "a1c_ruling_dropped": ( + "roles", + lambda d: _entry(d, "2015.relationship.88").pop("ruling"), + ), + "a1c_ruling_on_code_22": ( + "roles", + lambda d: _copy_ruling( + d, "2019.relationship.88", "2019.relationship.22" + ), + ), + "a1c_convention_on_code_22": ( + "roles", + lambda d: _entry(d, "2019.relationship.22").update( + action="convention_per_d637_1c" + ), + ), + "a1c_confirmation_dropped": ( + "roles", + lambda d: _entry(d, "2021.relationship.88")["ruling"].pop( + "confirmed_by" + ), + ), + "a1c_adjudication_changed": ( + "roles", + lambda d: _entry(d, "2023.relationship.88")["adjudication"].update( + disposition="D" + ), + ), "a2_blocked_again": ( "income", lambda d: _entry(d, "income.2019.wife_sex").update( diff --git a/tests/track_u2/test_u2_loader.py b/tests/track_u2/test_u2_loader.py index ccb9c9332..1ec0f8a08 100644 --- a/tests/track_u2/test_u2_loader.py +++ b/tests/track_u2/test_u2_loader.py @@ -1,18 +1,21 @@ """The registered U2 loader's stages 2 and 3 on an invented staged directory (section 14; review finding 6 of the u2m2 review). -INVENTED DATA - NOT A COMPARISON. At the adjudicated registries the -loader refuses in stage 1, before any PSID file is opened -(``test_u2_mappings.py``). To execute the rest of it before any real -record is read, this module stages the invented population as PSID-shaped -fixed-width files -- one family file and setup per support wave, written -at the registry layouts, and an individual file and setup -- in a -temporary directory and runs :func:`loader.load_u2_inputs` against it -with four patches, each named where it is applied: +INVENTED DATA - NOT A COMPARISON. An unfrozen load without Max's +recorded authorization refuses before any PSID path is resolved, and +since amendment 1c's registry commit stage 1 passes at the committed +registries, opening no PSID file (``test_u2_mappings.py``). To execute +the rest of the loader before any real record is read, this module +stages the invented population as PSID-shaped fixed-width files -- one +family file and setup per support wave, written at the registry layouts, +and an individual file and setup -- in a temporary directory and runs +:func:`loader.load_u2_inputs` against it, an unfrozen load with an +invented authorization record (:data:`TEST_AUTHORIZATION`, naming no +real decision), with four patches, each named where it is applied: * ``sources.blockers`` returns no blocker, so stage 1 and the registry - gate pass (the registry's own routes, not the declared ones, are - applied); + gate pass whatever the committed registries hold (the registry's own + routes, not the declared ones, are applied); * ``loader.check_source_hashes`` returns nothing, because the registries cite the real PSID setup files and codebooks, which are not staged here (the rehash itself is tested in ``test_u2_mappings.py``); @@ -159,6 +162,17 @@ def _stage(root, inputs: cohort.U2Inputs, registries) -> set[str]: return staged +#: An invented authorization record for the unfrozen loads here, on the +#: invented staged directory only: the section 20 step-4 pass needs Max's +#: recorded authorization (a chief-of-staff decision), and this +#: placeholder id is not one. +TEST_AUTHORIZATION = { + "decision": "d999999999", + "git_head": "0" * 40, + "recorded_at_utc": "2026-10-12T00:00:00Z", +} + + def _load( root, inputs: cohort.U2Inputs, @@ -193,7 +207,9 @@ def _load( ) _GUARD["active"], _GUARD["opened"] = True, [] if evidence is None: - return loader.load_u2_inputs(data_dir=root) + return loader.load_u2_inputs( + data_dir=root, authorization=TEST_AUTHORIZATION + ) return loader.load_u2_inputs(data_dir=root, evidence=evidence) finally: _GUARD["active"] = False diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index fe530f2ed..1e9a9bfd2 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -10,6 +10,7 @@ from __future__ import annotations +import datetime import functools import hashlib import json @@ -283,6 +284,15 @@ def test_d1090_moved_only_the_ruled_gate_fields(): # Amendment 1a's three D items keep their documentary resolution. "1a": {"ruling_effect", "completeness_disclosure", "ruling"}, "1b": {"ruling_effect", "ruling"}, + # Code 88's six A items resolve under the convention. + "1c": { + "status", + "action", + "income_role", + "resolution", + "ruling", + "superseded_question", + }, "2": { "status", "usage", @@ -383,7 +393,7 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "pension": ["5b", "5a"], "weights": ["6"], "individual": ["6"], - "roles": ["1a", "1b"], + "roles": ["1a", "1b", "1c"], } @@ -907,29 +917,87 @@ def test_declared_gate_records_what_the_registry_gate_refuses( } -def test_source_preflight_refuses_before_opening_any_psid_file( +def test_an_unauthorized_load_refuses_before_any_psid_path( tmp_path, monkeypatch ): - opened = [] + """Section 20 step 4 needs Max's recorded authorization: an unfrozen + load without it refuses before the source preflight and before any + PSID path is resolved, and a malformed record refuses the same way. + With a record, the load passes the source preflight (no blocker) and + refuses at the cited-source rehash, the staged directory being + empty; nothing under it is opened.""" + + import sys + + from populace_dynamics.data import psid + + opened, resolved = [], [] def spy(event, args): if event == "open" and args and str(args[0]).startswith(str(tmp_path)): opened.append(args[0]) - import sys - sys.addaudithook(spy) - with pytest.raises(loader.U2LoaderRefusal, match="registry blockers"): - loader.load_u2_inputs(data_dir=tmp_path) + original = psid._resolve_data_dir + monkeypatch.setattr( + psid, + "_resolve_data_dir", + lambda data_dir: resolved.append(data_dir) or original(data_dir), + ) + head = "0" * 40 + for authorization in ( + None, + {}, + {"decision": "d745"}, + {"decision": "yes", "git_head": head, "recorded_at_utc": "x"}, + { + "decision": "d745", + "git_head": "HEAD", + "recorded_at_utc": "2026-10-12T00:00:00Z", + }, + { + "decision": "d745", + "git_head": head, + "recorded_at_utc": "2026-10-12 00:00", + }, + ): + with pytest.raises(loader.U2AuthorizationRefusal): + loader.load_u2_inputs( + data_dir=tmp_path, authorization=authorization + ) + assert resolved == [] and opened == [] + record = loader.structural_pass_authorization("d1", git_head=head) + with pytest.raises(loader.U2LoaderRefusal, match="is not staged"): + loader.load_u2_inputs(data_dir=tmp_path, authorization=record) + assert resolved == [tmp_path] assert opened == [] -def test_source_preflight_lists_the_open_entries(committed_registries): - with pytest.raises(loader.U2LoaderRefusal) as error: - loader.source_preflight(committed_registries) - # Since the d637 registry commits the only blockers are code 88's - # (amendment 1c, pending d745): TO VERIFY and its refusal, each wave. - assert str(error.value).startswith("12 registry blockers refuse") +def test_structural_pass_authorization_records_and_refuses(): + head = "a" * 40 + record = loader.structural_pass_authorization("d1234", git_head=head) + assert set(record) == {"decision", "git_head", "recorded_at_utc"} + assert (record["decision"], record["git_head"]) == ("d1234", head) + stamp = datetime.datetime.strptime( + record["recorded_at_utc"], "%Y-%m-%dT%H:%M:%SZ" + ).replace(tzinfo=datetime.timezone.utc) + now = datetime.datetime.now(datetime.timezone.utc) + assert abs((now - stamp).total_seconds()) < 120 + assert loader.check_structural_pass_authorization(record) == record + for decision in (None, "", "d", "d0", "D745", "d745 ", "745", "yes"): + with pytest.raises(loader.U2AuthorizationRefusal): + loader.structural_pass_authorization(decision, git_head=head) + with pytest.raises(loader.U2AuthorizationRefusal): + loader.check_structural_pass_authorization({**record, "granted": True}) + + +def test_source_preflight_passes_at_the_committed_registries( + committed_registries, +): + # Since amendment 1c's registry commit (d637, confirmed by d745) no + # blocker remains: the preflight passes, opening no PSID file. + review = loader.source_preflight(committed_registries) + assert review["all_resolved"] is True # The preflight's own rule: an absent code (code 92 in 2013 and 2015) # passes when it is RESOLVED; every other entry counts its blockers. refused = [] @@ -939,14 +1007,12 @@ def test_source_preflight_lists_the_open_entries(committed_registries): assert entry["status"] == "RESOLVED", entry_id continue refused += sources.blockers(entry, name) - assert len(refused) == 12 - assert sorted({reason.split(": ")[0] for reason in refused}) == sorted( - f"roles:{wave}.relationship.88" for wave in sources.SUPPORT_WAVES - ) + assert refused == [] for wave in sources.SUPPORT_WAVES: - key = f"roles:{wave}.relationship.88" - assert f"{key}: refuse_substantive_routing" in refused - assert any(r.startswith(f"{key}: TO VERIFY: ") for r in refused) + assert ( + "roles", + f"{wave}.relationship.88", + ) in loader.required_entries(committed_registries) entries = loader.required_entries(committed_registries) assert ("weights", "2017.cross_section_weight") in entries assert ("roles", "2019.relationship.92") in entries @@ -1105,8 +1171,7 @@ def test_registry_documents_are_read_only(committed_registries): ) # The frozen documents still serialize to the pinned canonical form. assert json.loads(json.dumps(committed_registries.documents["design"])) - with pytest.raises(loader.U2LoaderRefusal, match="registry blockers"): - loader.source_preflight(committed_registries) + assert loader.source_preflight(committed_registries)["all_resolved"] # --------------------------------------------------------------------------- @@ -1139,8 +1204,7 @@ def test_registry_set_copies_and_pickles_keep_their_binding( restored.entry("pension", "2021.route.previous_dc_only")[ "status" ] = "RESOLVED" - with pytest.raises(loader.U2LoaderRefusal, match="registry blockers"): - loader.source_preflight(restored) + assert loader.source_preflight(restored)["all_resolved"] def test_a_committed_set_built_from_plain_documents_is_frozen(): diff --git a/tests/track_u2/test_u2_roles.py b/tests/track_u2/test_u2_roles.py index a59e1e1f3..ce3e29c52 100644 --- a/tests/track_u2/test_u2_roles.py +++ b/tests/track_u2/test_u2_roles.py @@ -182,15 +182,14 @@ def test_administrative_birth_support_is_administrative_only(): assert not rules[(wave, 92)].administrative_birth_support -#: The role entries the committed registries refuse: code 88 everywhere -#: (TO VERIFY) and code 92's absence in 2013 and 2015. 2013 and -#: 2015-2017 code 90 and 2017 code 92 are documented rules (disposition -#: D). 2015 code 20 and 2019-2023 codes 90 and 92, which the -#: adjudication refused (disposition F), carry section 16c's ratified -#: exclusions since the d1090 registry commit (:data:`D1090_RULED`). -REFUSED_ROLES = frozenset( - {(wave, 88) for wave in sources.SUPPORT_WAVES} | {(2013, 92), (2015, 92)} -) +#: The role entries the committed registries refuse: code 92's absence +#: in 2013 and 2015. 2013 and 2015-2017 code 90 and 2017 code 92 are +#: documented rules (disposition D). 2015 code 20 and 2019-2023 codes 90 +#: and 92, which the adjudication refused (disposition F), carry section +#: 16c's ratified exclusions since the d1090 registry commit +#: (:data:`D1090_RULED`). Code 88, TO VERIFY until then, takes amendment +#: 1c's convention in every wave since its d637 registry commit. +REFUSED_ROLES = frozenset({(2013, 92), (2015, 92)}) D1090_RULED = frozenset( {(2015, 20)} | {(wave, code) for wave in B2_WAVES for code in (90, 92)} ) @@ -284,9 +283,11 @@ def test_registry_context_refuses_refused_codes(): registry = sources.RoleContext.from_registry() with pytest.raises(sources.U2RoleRefusal, match="code 92 absent in 2015"): registry.rule(2015, 92) + # Amendment 1c (d637, confirmed by d745): code 88 resolves to the + # declared convention in every wave. + declared = sources.RoleContext.declared() for wave in sources.SUPPORT_WAVES: - with pytest.raises(sources.U2RoleRefusal, match="TO VERIFY"): - registry.rule(wave, 88) + assert registry.rule(wave, 88) == declared.rule(wave, 88) # The ruled codes resolve, each with its exclusion (section 16c). for wave, code in sorted(D1090_RULED): rule = registry.rule(wave, code) @@ -296,13 +297,12 @@ def test_registry_context_refuses_refused_codes(): ) -def test_code_88_takes_the_amendment_1c_convention(u2_inputs): +def test_code_88_takes_the_amendment_1c_convention(u2_inputs, monkeypatch): """Amendment 1c (d637, confirmed by d745): a code-88 first-year cohabitor has the OFUM role outside the spouse slot, is no annuity life and resolves no marital history, so adding one to a family that - supplies observations changes no observation or disposition. The - registry context refuses code 88 until the roles registry records - 1c.""" + supplies observations changes no observation or disposition, under + the declared and the registry role context alike.""" variant = invented.with_code_88_cohabitor(u2_inputs) spec = cohort.U2CohortSpec(row="U1") @@ -315,14 +315,33 @@ def test_code_88_takes_the_amendment_1c_convention(u2_inputs): assert (obs["wave"].eq(2019) & obs["interview"].eq(family)).any() pd.testing.assert_frame_equal(built.observations, base.observations) pd.testing.assert_frame_equal(built.dispositions, base.dispositions) - # The same family refuses where code 88 is still open, so the - # cohabitor's rule is read, not skipped. registry = sources.RoleContext.from_registry() - for wave in sources.SUPPORT_WAVES: - with pytest.raises(sources.U2RoleRefusal, match="relationship.88"): - registry.rule(wave, 88) - with pytest.raises(sources.U2RoleRefusal): - cohort.build_u2_cohort(variant, spec, role_context=registry) + by_registry = cohort.build_u2_cohort(variant, spec, role_context=registry) + base_registry = cohort.build_u2_cohort( + u2_inputs, spec, role_context=registry + ) + pd.testing.assert_frame_equal( + by_registry.observations, base_registry.observations + ) + pd.testing.assert_frame_equal( + by_registry.dispositions, base_registry.dispositions + ) + # The cohabitor's rule is read, not skipped: were code 88 to refuse, + # as its declared rule did before amendment 1c, this family would. + original = sources.declared_role_rules + + def refusing(): + rules = original() + rules[(2019, 88)] = dataclasses.replace( + rules[(2019, 88)], income_role=None, refusal="refused (test)" + ) + return rules + + monkeypatch.setattr(sources, "declared_role_rules", refusing) + with pytest.raises(sources.U2RoleRefusal, match="code 88: refused"): + cohort.build_u2_cohort( + variant, spec, role_context=sources.RoleContext.declared() + ) def test_declared_context_refuses_non_invented_inputs(u2_inputs, declared): @@ -733,26 +752,24 @@ def test_earlier_dispositions_keep_precedence( def test_code_88_in_an_excluded_unit_keeps_the_exclusion(u2_inputs): """A code-88 member never hides or changes an exclusion: under amendment 1c's convention a unit that B2 excludes stays excluded, - counted under its disposition, and nothing else moves; a role context - that leaves code 88 open refuses the build, never hidden by the - exclusion (review of 07916bb, finding 3).""" + counted under its disposition, and nothing else moves, under either + role context (review of 07916bb, finding 3).""" changed = invented.invented_variant( u2_inputs, "code_88_in_excluded_unit_2019" ) - declared = sources.RoleContext.declared() - built = cohort.build_u2_cohort(changed, role_context=declared) - base = cohort.build_u2_cohort(u2_inputs, role_context=declared) - pd.testing.assert_frame_equal(built.observations, base.observations) - pd.testing.assert_frame_equal(built.dispositions, base.dispositions) - assert ( - built.dispositions["disposition"] - .eq(sources.UNCOOPERATIVE_SPOUSE_OR_PARTNER) - .any() - ) - with pytest.raises(sources.U2RoleRefusal, match="88"): - cohort.build_u2_cohort( - changed, role_context=sources.RoleContext.from_registry() + for context in ( + sources.RoleContext.declared(), + sources.RoleContext.from_registry(), + ): + built = cohort.build_u2_cohort(changed, role_context=context) + base = cohort.build_u2_cohort(u2_inputs, role_context=context) + pd.testing.assert_frame_equal(built.observations, base.observations) + pd.testing.assert_frame_equal(built.dispositions, base.dispositions) + assert ( + built.dispositions["disposition"] + .eq(sources.UNCOOPERATIVE_SPOUSE_OR_PARTNER) + .any() ) @@ -941,7 +958,7 @@ def test_a_registry_context_cannot_carry_the_declared_rules(): with pytest.raises(sources.U2SourceRefusal, match="declared table"): sources.RoleContext(sources.INVENTED_DECLARED, registry.rules) # Any one rule changed is refused, e.g. 2015 code 20 without its - # section 16c exclusion, or code 88 made applicable. + # section 16c exclusion, or code 88 given the spouse slot. for key, changed in ( ( (2015, 20), @@ -951,7 +968,7 @@ def test_a_registry_context_cannot_carry_the_declared_rules(): ), ( (2019, 88), - dataclasses.replace(registry.rules[(2019, 88)], refusal=None), + dataclasses.replace(registry.rules[(2019, 88)], spouse_slot=True), ), ): rules = dict(registry.rules) diff --git a/tests/track_u2/test_u2_scripts.py b/tests/track_u2/test_u2_scripts.py index d052c4744..daf837810 100644 --- a/tests/track_u2/test_u2_scripts.py +++ b/tests/track_u2/test_u2_scripts.py @@ -7,7 +7,8 @@ * ``scripts/track_u2_structure.py`` and ``scripts/track_u2_component_diagnostics.py`` cannot reach the income concept or the tabulation (static transitive import graph, package - initializers included). + initializers included), and refuse without Max's recorded + authorization (``--authorized-by``) before any PSID path is resolved. * ``scripts/run_track_u2_registered.py`` refuses every other state before reading PSID, with a fake ``git`` and hand-built specification blocks. Its checks run in the script's own order (pointer, commit, @@ -117,43 +118,42 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): exclusions = built["rows"][row]["family_unit_exclusions"] assert set(exclusions) == set(sources.FAMILY_UNIT_EXCLUSIONS) assert all(count > 0 for count in exclusions.values()), row - # Amendment 1c: the declared context builds a code-88 first-year - # cohabitor's family with every observation and disposition - # unchanged; the registry context refuses code 88 until the roles - # registry records 1c. - assert roles["code_88_under_registry_context"]["refused"] - declared_88 = roles["code_88_under_declared_context"] - assert not declared_88["refused"] - assert declared_88["observations_equal"] - assert declared_88["dispositions_equal"] - assert declared_88["rule"] == { - "wave": 2019, - "code": 88, - "present": True, - "income_role": "ofum", - "spouse_slot": False, - "legal_spouse_annuity": False, - "marital_resolution": False, - "administrative_birth_support": True, - "refusal": None, - "family_unit_exclusion": None, - } + # Amendment 1c (d637, confirmed by d745): both role contexts build a + # code-88 first-year cohabitor's family with every observation and + # disposition unchanged. + for context in ("registry", "declared"): + entry = roles[f"code_88_under_{context}_context"] + assert not entry["refused"], context + assert entry["observations_equal"], context + assert entry["dispositions_equal"], context + assert entry["rule"] == { + "wave": 2019, + "code": 88, + "present": True, + "income_role": "ofum", + "spouse_slot": False, + "legal_spouse_annuity": False, + "marital_resolution": False, + "administrative_birth_support": True, + "refusal": None, + "family_unit_exclusion": None, + }, context assert roles["declared_context_on_non_invented_inputs"]["refused"] assert roles["registry_rules_equal_declared_where_resolved"] refusals = roles["registry_rule_refusals"] assert refusals and all(entry["refused"] for entry in refusals.values()) - # Only code 88 (TO VERIFY) and code 92's absence before 2017 refuse. - assert set(refusals) == { - *(f"{wave}.88" for wave in sources.SUPPORT_WAVES), - "2013.92", - "2015.92", - } - members = roles["invented_members_with_refused_codes"] - # The invented population holds a member of every refused code - # except code 88, which the separate code-88 variant adds. - for key, count in members.items(): - assert (count > 0) == (not key.endswith(".88")), key - assert checks["loader_preflight"]["refused"] + # Only code 92's absence before 2017 refuses: code 88 takes amendment + # 1c's convention since its registry commit. + assert set(refusals) == {"2013.92", "2015.92"} + # Both are absences, so no present relationship code refuses: the + # count of invented members holding one is over no code. + assert roles["invented_members_with_refused_codes"] == {} + # No blocker remains, and an unfrozen load without Max's recorded + # authorization refuses before any PSID path is resolved. + assert not checks["loader_preflight"]["refused"] + unauthorized = checks["unauthorized_unfrozen_load"] + assert unauthorized["error"] == "U2AuthorizationRefusal" + assert "needs Max's authorization" in unauthorized["message"] gate_refusals = checks["invented_gate_refusals"] assert set(gate_refusals) == { "relabel_as_invented", @@ -280,7 +280,14 @@ def test_dry_run_markdown_names_the_refusals(dry_run): text = (dry_run / "RESULTS.md").read_text() assert "INVENTED" in text assert "not a comparison" in text.lower() - assert "Loader preflight refuses" in text + assert ( + "Loader source preflight passes at the committed registries " + "(no blocker): True" in text + ) + assert ( + "an unfrozen load without Max's recorded authorization refuses " + "before any PSID path is resolved: True" in text + ) assert "Round-1 review refusals (invented;" in text assert "Round-2 review refusals (invented;" in text assert "routes from other data to the invented declared rules" in text @@ -347,35 +354,53 @@ def _reachable(root: str) -> set[str]: return seen -@pytest.mark.parametrize( - "script", ["track_u2_structure", "track_u2_component_diagnostics"] -) -def test_pre_registration_scripts_refuse_before_opening_psid(script, tmp_path): - """Section 20 step 4 is not authorized and the registries hold open - routes: each structural entry point, run in a fresh interpreter, - refuses in the loader's source preflight before any file under the - PSID directory is opened, with no poverty module loaded, and writes - nothing.""" +#: The two section 20 step-4 entry points. +STRUCTURAL_SCRIPTS = ["track_u2_structure", "track_u2_component_diagnostics"] + +def _probe(script, argv, tmp_path, *, resolve): + """Run ``script`` with ``argv`` in a fresh interpreter: an audit hook + records every open under the staged and the default PSID directory + (the default pointed at a sentinel directory), and + ``psid._resolve_data_dir`` records each call and, unless ``resolve``, + raises. Returns the exit code, stderr, the refusal, the opens and + resolutions, and any poverty module loaded.""" + + import os import subprocess import sys + from populace_dynamics.data import psid + data_dir = tmp_path / "psid" - data_dir.mkdir() - output_dir = tmp_path / "out" + data_dir.mkdir(exist_ok=True) + default = tmp_path / "default-psid" + default.mkdir(exist_ok=True) + watched = [str(data_dir), str(default), str(psid._DEFAULT_DATA_DIR)] path = ROOT / "scripts" / f"{script}.py" probe = f""" import json, runpy, sys -opened = [] +opened, resolved = [], [] def spy(event, args): - if event == "open" and args and str(args[0]).startswith({str(data_dir)!r}): + if event == "open" and args and any( + str(args[0]).startswith(w) for w in {watched!r} + ): opened.append(str(args[0])) sys.addaudithook(spy) -sys.argv = [{str(path)!r}, "--output-dir", {str(output_dir)!r}, - "--data-dir", {str(data_dir)!r}] -refusal = None +from populace_dynamics.data import psid +original = psid._resolve_data_dir +def sentinel(data_dir): + resolved.append(None if data_dir is None else str(data_dir)) + if not {resolve!r}: + raise RuntimeError("a PSID path was resolved") + return original(data_dir) +psid._resolve_data_dir = sentinel +sys.argv = [{str(path)!r}, *{argv!r}] +code, refusal = None, None try: runpy.run_path({str(path)!r}, run_name="__main__") +except SystemExit as error: + code = error.code except BaseException as error: refusal = [type(error).__name__, str(error)] forbidden = [ @@ -386,8 +411,8 @@ def spy(event, args): ) if name in sys.modules ] -print(json.dumps({{"refusal": refusal, "opened": opened, - "forbidden": forbidden}})) +print(json.dumps({{"code": code, "refusal": refusal, "opened": opened, + "resolved": resolved, "forbidden": forbidden}})) """ done = subprocess.run( [sys.executable, "-c", probe], @@ -395,15 +420,185 @@ def spy(event, args): capture_output=True, text=True, check=True, + env={**os.environ, psid._DEFAULT_DATA_DIR_ENV: str(default)}, ) record = json.loads(done.stdout.strip().splitlines()[-1]) + record["stderr"] = done.stderr + record["data_dir"] = str(data_dir) + return record + + +@pytest.mark.parametrize("script", STRUCTURAL_SCRIPTS) +@pytest.mark.parametrize( + "flags", + [ + [], + ["--data-dir", "{data_dir}"], + ["--authorized-by", "yes"], + ["--authorized-by", "D745"], + ["--authorized-by", ""], + ["--authorized-by", "d0", "--data-dir", "{data_dir}"], + ], +) +def test_structural_pass_refuses_without_max_s_authorization( + script, flags, tmp_path +): + """Section 20 step 4 reads real PSID records, so it needs Max's + authorization, recorded as a chief-of-staff decision: without + ``--authorized-by `` (or with anything but a decision + id) each structural entry point, run in a fresh interpreter, exits + non-zero before any PSID path is resolved or any file under the + staged or default PSID directory is opened, with no poverty module + loaded, and writes nothing.""" + + output_dir = tmp_path / "out" + data_dir = str(tmp_path / "psid") + argv = ["--output-dir", str(output_dir)] + [ + flag.format(data_dir=data_dir) for flag in flags + ] + record = _probe(script, argv, tmp_path, resolve=False) + assert record["code"] == 2, record + assert record["refusal"] is None, record + assert "needs Max's authorization" in record["stderr"] + assert "does not grant" in record["stderr"] + assert record["resolved"] == [] and record["opened"] == [] + assert record["forbidden"] == [] + assert not output_dir.exists() + + +@pytest.mark.parametrize("script", STRUCTURAL_SCRIPTS) +def test_an_authorized_structural_pass_reaches_the_loader(script, tmp_path): + """With a decision id the pass gets past the authorization check and + the source preflight (no blocker remains) and reaches the staged + directory, where an empty one refuses at the cited-source rehash: + nothing under it is opened and nothing is written. The flag records + the authorization; it grants nothing a decision id does not name.""" + + output_dir = tmp_path / "out" + data_dir = str(tmp_path / "psid") + argv = [ + "--output-dir", + str(output_dir), + "--data-dir", + data_dir, + "--authorized-by", + "d999999999", + ] + record = _probe(script, argv, tmp_path, resolve=True) assert record["refusal"][0] == "U2LoaderRefusal", record - assert "registry blockers" in record["refusal"][1] + assert "is not staged" in record["refusal"][1] + assert record["resolved"] == [data_dir] assert record["opened"] == [] assert record["forbidden"] == [] assert not output_dir.exists() +@pytest.mark.parametrize("script", STRUCTURAL_SCRIPTS) +def test_structural_build_refuses_without_an_authorization_record( + script, monkeypatch +): + """``build`` itself refuses without a valid record, or with one made + at another git head, before the loader is called.""" + + from populace_dynamics.uniform_cut_track_u2 import loader + + module = _script(script) + calls = [] + monkeypatch.setattr( + module.loader, "load_u2_inputs", lambda **kwargs: calls.append(kwargs) + ) + for authorization in (None, {}, {"decision": "d745"}): + with pytest.raises(loader.U2AuthorizationRefusal): + module.build(authorization=authorization) + elsewhere = loader.structural_pass_authorization("d745", git_head="f" * 40) + with pytest.raises(loader.U2AuthorizationRefusal, match="another git"): + module.build(authorization=elsewhere) + assert calls == [] + + +def test_the_component_pass_records_its_authorization(u2_inputs, monkeypatch): + """The output records the decision id, the git head and the UTC time + (here on invented inputs, with the import guard and the loader + patched: no PSID path is touched).""" + + from populace_dynamics.uniform_cut_track_u2 import loader + + module = _script("track_u2_component_diagnostics") + monkeypatch.setattr(module, "_leaked", lambda: []) + monkeypatch.setattr( + module.loader, "load_u2_inputs", lambda **kwargs: u2_inputs + ) + record = loader.structural_pass_authorization( + "d1234", git_head=module._git("rev-parse", "HEAD") + ) + result = module.build(authorization=record) + assert result["authorization"] == record + + +@pytest.mark.parametrize("script", STRUCTURAL_SCRIPTS) +def test_main_hands_build_the_recorded_authorization( + script, tmp_path, monkeypatch +): + module = _script(script) + seen = {} + + def build(data_dir, *, authorization): + seen.update(authorization) + return {"authorization": authorization, "preregistration_evidence": {}} + + monkeypatch.setattr(module, "build", build) + module.main( + [ + "--output-dir", + str(tmp_path), + "--authorized-by", + "d1234", + ] + ) + assert seen["decision"] == "d1234" + assert seen["git_head"] == module._git("rev-parse", "HEAD") + name = { + "track_u2_structure": "track-u2-structure.json", + "track_u2_component_diagnostics": ( + "track-u2-component-diagnostics.json" + ), + }[script] + written = json.loads((tmp_path / name).read_text()) + assert written["authorization"] == seen + + +@pytest.mark.parametrize("script", STRUCTURAL_SCRIPTS) +def test_build_returns_the_authorization_in_its_output(script): + """Statically: ``build`` puts the checked record in what it returns, + under ``authorization`` (the structure pass cannot run to its end on + invented inputs, whose seal its loader evidence refuses).""" + + tree = ast.parse((ROOT / "scripts" / f"{script}.py").read_text()) + (build,) = [ + node + for node in tree.body + if isinstance(node, ast.FunctionDef) and node.name == "build" + ] + returned = [ + node.value + for node in ast.walk(build) + if isinstance(node, ast.Return) and isinstance(node.value, ast.Dict) + ] + (output,) = returned + pairs = { + key.value: value + for key, value in zip(output.keys, output.values, strict=True) + if isinstance(key, ast.Constant) + } + assert isinstance(pairs["authorization"], ast.Name) + assert pairs["authorization"].id == "authorization" + first = build.body[0] + assert ast.unparse(first) == ( + "authorization = " + "loader.check_structural_pass_authorization(authorization)" + ) + + @pytest.mark.parametrize( "script", ["track_u2_structure", "track_u2_component_diagnostics"] ) @@ -556,13 +751,33 @@ def test_preflight_refuses_incomplete_specifications( _preflight(registered, specification=block) -def test_preflight_refuses_a_wrong_binding_then_the_open_mapping(registered): +def test_preflight_refuses_a_wrong_binding_then_checks_the_mapping( + registered, monkeypatch +): with pytest.raises(ValueError, match="binds"): _preflight(registered) binding = registered.binding_sha256("b" * 64, _invented_evidence()) - from populace_dynamics.uniform_cut_track_u2 import loader + from populace_dynamics.uniform_cut_track_u2 import loader, sources - with pytest.raises(loader.U2LoaderRefusal, match="registry blockers"): + # Since amendment 1c's registry commit the mapping review passes at + # the committed registries (with the invented specification and + # evidence here; the committed ones refuse first, items 4 and 8) ... + state = _preflight(registered, binding=binding) + assert state["mapping_review"]["all_resolved"] is True + assert state["binding_sha256"] == binding + # ... and a blocker on any required entry refuses it. + sources.RoleContext.from_registry() + original = sources.blockers + monkeypatch.setattr( + sources, + "blockers", + lambda entry, name: ( + ["reblocked (test)"] + if entry.get("id") == "2019.relationship.88" + else original(entry, name) + ), + ) + with pytest.raises(loader.U2LoaderRefusal, match="^1 registry blockers"): _preflight(registered, binding=binding) @@ -583,7 +798,7 @@ def test_preflight_refuses_a_plan_the_support_registry_does_not_hold( def test_preflight_checks_the_plan_before_the_mapping_review( registered, monkeypatch ): - from populace_dynamics.uniform_cut_track_u2 import cohort, loader + from populace_dynamics.uniform_cut_track_u2 import cohort calls = [] original = cohort.check_plan_against_support_registry @@ -594,8 +809,9 @@ def spy(registries): monkeypatch.setattr(cohort, "check_plan_against_support_registry", spy) binding = registered.binding_sha256("b" * 64, _invented_evidence()) - with pytest.raises(loader.U2LoaderRefusal, match="registry blockers"): - _preflight(registered, binding=binding) + assert _preflight(registered, binding=binding)["mapping_review"][ + "all_resolved" + ] # Once in the preflight, once again in the loader's source preflight. assert calls == ["committed", "committed"] From eccf3ea0c0454e6f993c80d96db535d7f4d44292 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 10:56:13 -0400 Subject: [PATCH 19/21] Record d745 and the 1c commit in the U2 adjudication notes Max confirmed on 2026-10-12 that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 records, so the wording that still said otherwise follows. - The six registries that record the independent adjudication (income, wealth, pension, roles, support, weights) ended their note with "The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." Each now says Max confirmed 1a-1c and the 1c commit resolved them; nothing else in the notes moves. sources: six byte pins. - The package docstring names 1a-1c among d637's ratified amendments in place of "await his confirmation (d745) and change nothing here". - sources: three docstring and comment paragraphs over 79 columns are rewrapped (two from this branch's builder and 1c commits, one from the amendment-2 answers); no wording changes in them. - Section 19 records it; the spec is re-pinned. Exposure log follows. Tests: the pinned adjudication-note ending follows. No count or loader blocker moves (0). Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/income.json | 2 +- data/external/track_u2/pension.json | 2 +- data/external/track_u2/roles.json | 2 +- data/external/track_u2/support.json | 2 +- data/external/track_u2/u1_identity.json | 2 +- data/external/track_u2/wealth.json | 2 +- data/external/track_u2/weights.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1b_exposure.md | 1 + .../uniform_cut_track_u2/__init__.py | 16 ++--- .../uniform_cut_track_u2/sources.py | 64 ++++++++++--------- tests/track_u2/test_u2_mappings.py | 5 +- 12 files changed, 53 insertions(+), 48 deletions(-) diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index 546f617bb..72b1e67a6 100644 --- a/data/external/track_u2/income.json +++ b/data/external/track_u2/income.json @@ -28015,7 +28015,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 role records; no A item stays TO VERIFY." }, "rulings_applied": [ { diff --git a/data/external/track_u2/pension.json b/data/external/track_u2/pension.json index 54ab4f495..6bf5970cf 100644 --- a/data/external/track_u2/pension.json +++ b/data/external/track_u2/pension.json @@ -26536,7 +26536,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 role records; no A item stays TO VERIFY." }, "rulings_applied": [ { diff --git a/data/external/track_u2/roles.json b/data/external/track_u2/roles.json index 411589f4a..c3f3348dc 100644 --- a/data/external/track_u2/roles.json +++ b/data/external/track_u2/roles.json @@ -1975,7 +1975,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 role records; no A item stays TO VERIFY." }, "rulings_applied": [ { diff --git a/data/external/track_u2/support.json b/data/external/track_u2/support.json index 6ec342548..354c164d3 100644 --- a/data/external/track_u2/support.json +++ b/data/external/track_u2/support.json @@ -966,6 +966,6 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 role records; no A item stays TO VERIFY." } } diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index 0a24bce17..dd8156af5 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "ce6064ce76204454e1b54b986d1f361eb14a69db18e88d27a25ce870dfc92986", + "sha256": "76dcd91ab461a84c680d804d148be8e7188e8da17e365a2908782892ab54e410", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/data/external/track_u2/wealth.json b/data/external/track_u2/wealth.json index cbab8aea4..4a53549ff 100644 --- a/data/external/track_u2/wealth.json +++ b/data/external/track_u2/wealth.json @@ -9063,7 +9063,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 role records; no A item stays TO VERIFY." }, "rulings_applied": [ { diff --git a/data/external/track_u2/weights.json b/data/external/track_u2/weights.json index c28323496..1762bbcc2 100644 --- a/data/external/track_u2/weights.json +++ b/data/external/track_u2/weights.json @@ -553,7 +553,7 @@ "sha256": "a891762bf9be39e05509e59c5b7cd3fdab0eb34feb9ad5b9bb4a8ec3afc39662", "bytes": 26564, "applied_by": "u2-m1b", - "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. The six code-88 role records (amendment 1c) stay TO VERIFY pending Max's confirmation of the 1a-1c reading (d745)." + "note": "Independent adjudication of milestone 1 (VERDICT: ERRORS FOUND). Amendment 7 citation corrections, D resolutions and F refusals are applied here; A items stayed TO VERIFY pending u2-draft-4 amendments 1-6 and Max's ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on 2026-10-10 (d1090); a registry's rulings_applied, where present, records what each ruling changed in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's parts 1a-1c as written (d745), and the 1c registry commit resolved the six code-88 role records; no A item stays TO VERIFY." }, "rulings_applied": [ { diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index a6cf512d7..da6da8977 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1870,6 +1870,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 1c, builder: the declared role rules (invented data only) give code 88 amendment 1c's convention: the OFUM income role on the family basis outside the spouse slot, no legal-spouse annuity life, no relationship-based marital resolution and the inherited administrative birth support. A code-88 first-year cohabitor added to an invented family changes no observation or disposition. Until the 1c registry commit, the registry context still refuses code 88 and the loader still refuses on its 12 blockers. - Amendment 1c: the six code-88 records (`roles:.relationship.88`, adjudication A) are RESOLVED under the first-year cohabitor convention and record the ruling, confirmed by d745; each keeps its question as `superseded_question`. The registry's code-88 role rules now equal the declared ones. Amendment 2's ten spouse age and sex records now say that a code-88 cohabitor does not occupy the slot, in place of "code 88's slot status stays TO VERIFY" and the clause that 1a–1c await d745. No TO VERIFY record remains, and the loader's source preflight passes: no blocker remains. - The structural pass (§20 step 4) still needs Max's authorization, recorded as a chief-of-staff decision. `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` now refuse without `--authorized-by `, exiting non-zero before any PSID path is resolved or opened, and the loader refuses an unfrozen load without the record; the flag records the decision id, the git head and the UTC time, and does not grant the authorization. §17 item 2 and §20 step 4 say so. +- Wording: the six registries that record the independent adjudication no longer end their note with the six code-88 records staying TO VERIFY pending d745; each says Max confirmed 1a–1c on 2026-10-12 and that the 1c commit resolved them. The package docstring names 1a–1c among d637's ratified amendments. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 197e27bb1..3edfb8070 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -412,6 +412,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 1b: `roles.json` (2013's code-90 record and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the 2013 code-90 row and the d637 section); `u2_source_registry.py` (1b's scope, the first with no adjudication disposition) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 1c, builder: `sources.py` (the declared code-88 rule and its docstrings), `cohort.py` and `estimator.py` (docstrings), `invented.py` (the two code-88 variant notes) and `scripts/track_u2_dry_run.py` (the code-88 checks); `u1_identity.json`; the specification's §19; `tests/track_u2/test_u2_roles.py` and `test_u2_scripts.py`; and this section. - Amendment 1c: `roles.json` (the six code-88 records and the d637 record), `income.json` (amendment 2's ten `use_restriction` sentences) and `u1_identity.json`; the specification's §17 item 2, §19 and §20 step 4; the master record (the summary, the code-88 and spouse age/sex blocker rows, the d637 section and sixteen per-route rows); `u2_source_registry.py` (1c's scope), `sources.py` (the roles and income byte pins and docstrings), `loader.py` (the structural-pass authorization), `invented.py` (two variant notes), `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` (`--authorized-by`), `scripts/track_u2_dry_run.py` and `scripts/run_track_u2_registered.py` (checks and docstrings); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py`, `test_u2_scripts.py` and `test_u2_loader.py`; and this section. +- Wording after d745: the six `independent_adjudication` notes (`income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json`, `weights.json`) and `u1_identity.json`; the specification's §19; `sources.py` (six byte pins and three docstring paragraphs rewrapped) and the package docstring; `tests/track_u2/test_u2_mappings.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/src/populace_dynamics/uniform_cut_track_u2/__init__.py b/src/populace_dynamics/uniform_cut_track_u2/__init__.py index e79f47d89..54857360f 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/__init__.py +++ b/src/populace_dynamics/uniform_cut_track_u2/__init__.py @@ -2,14 +2,14 @@ Specification ``docs/design/boomers2004_1946_55_comparison.md`` (``u2-draft-4``), sections 3-15, with section 16a's amendments as Max -ruled on them: d637 (section 16b) ratified amendments 2-5 and amendment -6's option 1, and d1090 (section 16c) ruled on blockers B1 and B2. Each -ruling reaches this package through the source registries, one reviewed -commit per amendment, and through the declared rules where the declared -interpretation changes; amendment 1's parts 1a-1c await his -confirmation (d745) and change nothing here. U2 extends U1's static -measurement (:mod:`populace_dynamics.uniform_cut_track_u`) to the -1946-55 birth cohort on the 2013-2023 PSID waves. It never edits a U1 module: U1's +ruled on them: d637 (section 16b) ratified amendments 2-5, amendment 6's +option 1 and, as Max confirmed on 2026-10-12 (d745), amendment 1's parts +1a-1c; d1090 (section 16c) ruled on blockers B1 and B2. Each ruling +reaches this package through the source registries, one reviewed commit +per amendment, and through the declared rules where the declared +interpretation changes. U2 extends U1's static measurement +(:mod:`populace_dynamics.uniform_cut_track_u`) to the 1946-55 birth +cohort on the 2013-2023 PSID waves. It never edits a U1 module: U1's constants, defaults, registered behaviour, parameter captures and artifacts stay byte-identical, and every U2 identity (target, column, rows, rulings, parameters, artifact path) is separate and refuses its U1 diff --git a/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index cec12ab46..3e0b15468 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/sources.py +++ b/src/populace_dynamics/uniform_cut_track_u2/sources.py @@ -75,15 +75,15 @@ ==== ============================== ============== =================== This is the section 3 table (``u2-draft-3``'s), amended where Max's -rulings differ from it. Under amendment 1c (d637, 2026-09-30, -section 16b; confirmed by d745 on 2026-10-12) code 88, the first-year -cohabitor, has the OFUM income role outside the spouse slot, is no -annuity life, never resolves a marital history by its code and keeps -its administrative birth support. Under d1090 (2026-10-10, section -16c) a 2015 family unit that holds a code-20 person recorded male (blocker B1), and a -2019-2023 family unit that holds a code-90 or code-92 person (blocker -B2), supplies no observation to any row: each observation it would have -supplied is counted under the named disposition +rulings differ from it. Under amendment 1c (d637, 2026-09-30, section +16b; confirmed by d745 on 2026-10-12) code 88, the first-year cohabitor, +has the OFUM income role outside the spouse slot, is no annuity life, +never resolves a marital history by its code and keeps its +administrative birth support. Under d1090 (2026-10-10, section 16c) a +2015 family unit that holds a code-20 person recorded male (blocker B1), +and a 2019-2023 family unit that holds a code-90 or code-92 person +(blocker B2), supplies no observation to any row: each observation it +would have supplied is counted under the named disposition (:data:`MALE_CODE_20_2015`, :data:`UNCOOPERATIVE_SPOUSE_OR_PARTNER`; the rule's :class:`FamilyUnitExclusion`, applied by :func:`family_unit_exclusion`), so 2019-2023 codes 90 and 92 have no @@ -91,9 +91,9 @@ 2013-2017 code 90 and 2017 code 92 (adjudication disposition D) and, by its d1090 commit, the same exclusions for 2015 code 20 and 2019-2023 codes 90 and 92, which the adjudication had refused (disposition F); the -registry gate follows the registry. The roles registry records the -same convention for code 88 in every wave (amendment 1c's registry -commit), so the registry context applies it too. +registry gate follows the registry. The roles registry records the same +convention for code 88 in every wave (amendment 1c's registry commit), +so the registry context applies it too. Any other in-family code keeps the inherited OFUM income role with no spouse status. Legacy identifiers ``wife``, ``wife_present`` and @@ -263,42 +263,43 @@ #: inherited code-90 meaning, and 1c resolves the six code-88 records #: under the first-year cohabitor convention (no blocker remains); the 1c #: commit also moved the income bytes, where amendment 2's ten records say -#: a code-88 cohabitor does not occupy the spouse slot. +#: a code-88 cohabitor does not occupy the spouse slot. The six +#: adjudication notes then record d745 and the 1c commit (six byte pins). REGISTRY_SHA256: dict[str, str] = { "income": ( - "9598798b6a5924e71f15f0c3cff6ca445e096540d46f33e87c6d07ff016916e5" + "79b8f953eda2111389b762608cea1025fb45d96ceb0468169eaf9ed405b0d4eb" ), "wealth": ( - "4fa58c6621738f071fbbc3e0e4ec3b2f6f0db9bcb6a942b43684137aa829a716" + "c5c8fa1ddf283c07ac4dcac4490afb9754d336ef3618a159e028ce2b39f874a1" ), "individual": ( "283c65c46ccfbc6b3a5b700ec7d0d1783e61c931352455b842828cc0186cedda" ), "pension": ( - "bd22e07a8576ea9239818ca40377839c5c3151fffb403f3e5f1ef1549da546cf" + "7cc17ff1f6f4a5896b86cafa5edc59028102d28bfcdfa42840d55b628074f3dd" ), "roles": ( - "014b4402f65d5cda2ecbc2c038c6b6dc408d2e994ebcc5001de926537d103674" + "f10c4a7dff38f74a29291fcec4a96c8c7b31d58561798a10c53094d6dcf393b7" ), "support": ( - "533c39187214fef6626e565362b2d97ff393b8d1f0034ffe365a7c87dadada6d" + "279515f5dab832155583829aec312078547a28d7152bcd07e90a2cf9bb863b8a" ), "weights": ( - "afefd67942e57076775d262849a3edbd292b1158a1a741c0b3811153d87ae738" + "47c9f2d1b4b95f8259107d589aadc370b835dc807d45fa5268cf88abd72cc3be" ), "design": ( "56181372d412811af6d06f5c0d8d1e071049f7fb73c34bd394aaff8f5e0c2fd6" ), } -#: The family-file income concepts the U2 income rows need (the union -#: over the ten rows; section 4). ``wife_age`` gives the family file's -#: spouse-slot flag (not 0: "no Spouse/Partner in FU"), which rows U3 (the -#: couple convention for new enrollment) and U4 (the unit size) read once -#: :func:`.cohort.income_rows` has held it equal to the roster's unique -#: spouse-slot occupant (the person whose role rule has ``spouse_slot``). The spouse age and sex are slot metadata only -#: (amendment 2, ratified by d637; section 16b): they never decide slot -#: occupancy, and a disagreement refuses. +#: The family-file income concepts the U2 income rows need (the union over the +#: ten rows; section 4). ``wife_age`` gives the family file's spouse-slot flag +#: (not 0: "no Spouse/Partner in FU"), which rows U3 (the couple convention for +#: new enrollment) and U4 (the unit size) read once :func:`.cohort.income_rows` +#: has held it equal to the roster's unique spouse-slot occupant (the person +#: whose role rule has ``spouse_slot``). The spouse age and sex are slot +#: metadata only (amendment 2, ratified by d637; section 16b): they never +#: decide slot occupancy, and a disagreement refuses. INCOME_CONCEPTS: tuple[str, ...] = ( "interview", "fu_size", @@ -1016,10 +1017,11 @@ class RoleContext: ``registry``: the committed roles registry; any code the loader's ``require_resolved`` refuses, refuses here (code 92's absence in 2013 and 2015; until amendment 1c's registry commit also code 88 in every - wave, and until the d1090 commit 2015 code 20 and 2019-2023 codes 90 - and 92, which now carry section 16c's exclusions). ``invented_declared``: the declared rules, section 3's - table with section 16c's two family-unit exclusions and amendment - 1c's code-88 convention; accepted only with invented inputs. + wave, and until the d1090 commit 2015 code 20 and 2019-2023 codes 90 and + 92, which now carry section 16c's exclusions). ``invented_declared``: + the declared rules, section 3's table with section 16c's two family-unit + exclusions and amendment 1c's code-88 convention; accepted only with + invented inputs. The kind is a binding, not a label: construction refuses rules other than the kind's own (the committed registry's or the declared diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 1e9a9bfd2..7bb62cc92 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -383,8 +383,9 @@ def test_d1090_moved_only_the_ruled_gate_fields(): "ruling. Max ruled on 2026-09-30 (d637) and, for blockers B1 and B2, on " "2026-10-10 (d1090); a registry's rulings_applied, where present, " "records what each ruling changed " - "in it. The six code-88 role records (amendment 1c) stay TO VERIFY " - "pending Max's confirmation of the 1a-1c reading (d745)." + "in it. On 2026-10-12 Max confirmed that d637 ratified amendment 1's " + "parts 1a-1c as written (d745), and the 1c registry commit resolved the " + "six code-88 role records; no A item stays TO VERIFY." ) #: The amendments each registry's ``rulings_applied`` d637 record lists. D637_RECORDED = { From 75354b897c1753df1c189a1828ae5fdccab789be Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Tue, 13 Oct 2026 18:41:46 -0400 Subject: [PATCH 20/21] Answer the reviews of the d745 and 1a-1c commits GPT-6.1 Sol reviewed 73d295f9 (d745 record), e74a5546 (1a), 7950065b (1b), 1bcb8765 (1c builder), 630a68c3 (1c) and 6bfa7612 (wording): 1b APPROVE, the rest REQUEST CHANGES, 1c with one blocking finding. - 1c, blocking: any well-formed evidence record let a frozen load skip the authorization an unfrozen load needs, so a fabricated record could reach raw-family parsing. A frozen load must now pass the committed pre-registration record (read_preregistration_evidence); an absent or different one refuses before any PSID path is resolved. While a frozen load reads, a loader audit hook refuses to open any file under the PSID directory that the record does not freeze, before it is read. - 1c, 1a: every required prose field of a ruled entry must be a nonblank string (resolution, ruling_effect, completeness_disclosure, superseded_question and the like); lists and dicts keep their types. - 1a: the scope pins present and each code's conventions through governs: code 90 keeps the legal-spouse annuity, marital resolution and birth support, code 92 none of them. 1b refuses any adjudication record, null included, and pins present and birth support. - 1a (inherited nit): the d637 differential counts a key added as null, or a value whose JSON type changes, as a change. - 1b nit: the recording test asserts 1b carries no adjudication record. - 1c builder: a builder that gave code 88 the spouse slot's role passed every test. A new invented variant (appended) puts a code-88 cohabitor born 1951, divorced in 1986, in the first 2019 family with OFUM Social Security and SSI: a U0 target whose own observation has the OFUM role and its recorded marriage under both role contexts and, in U4, the family income basis and the OFUM SSI cap. - Wording: rows.py no longer says section 16a awaits Max's ruling; the dry run's forged-registry docstring; the master record's d637 introduction; the exposure log. Section 19 records the answers; the spec is re-pinned. Tests: 1440 passed, 10 skipped (U2 set). New: fabricated and narrowed evidence refusals with zero resolutions and opens, 15 scope mutations, the code-88 cohort target, the differential's null case. Nothing reads PSID records. Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1_source_adjudication.md | 2 +- docs/design/u2_m1b_exposure.md | 3 +- scripts/track_u2_dry_run.py | 6 +- .../data/u2_source_registry.py | 47 +++++++- .../uniform_cut_track_u2/invented.py | 64 +++++++++++ .../uniform_cut_track_u2/loader.py | 105 ++++++++++++++++-- .../uniform_cut_track_u2/rows.py | 6 +- tests/README-tiers.md | 6 +- tests/tier_counts.json | 4 +- tests/track_u2/test_adjudication_applied.py | 11 +- tests/track_u2/test_source_registries.py | 84 ++++++++++++++ tests/track_u2/test_u2_loader.py | 88 ++++++++++++++- tests/track_u2/test_u2_mappings.py | 21 +++- tests/track_u2/test_u2_roles.py | 65 +++++++++++ tests/track_u2/test_u2_scripts.py | 1 + 17 files changed, 480 insertions(+), 36 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index dd8156af5..ea85c298d 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "76dcd91ab461a84c680d804d148be8e7188e8da17e365a2908782892ab54e410", + "sha256": "cdfef5ebc9f3208410de3d52220d34e27c3c713f4acbe986136c4d75c61d37eb", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index da6da8977..e8a50b635 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1871,6 +1871,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - Amendment 1c: the six code-88 records (`roles:.relationship.88`, adjudication A) are RESOLVED under the first-year cohabitor convention and record the ruling, confirmed by d745; each keeps its question as `superseded_question`. The registry's code-88 role rules now equal the declared ones. Amendment 2's ten spouse age and sex records now say that a code-88 cohabitor does not occupy the slot, in place of "code 88's slot status stays TO VERIFY" and the clause that 1a–1c await d745. No TO VERIFY record remains, and the loader's source preflight passes: no blocker remains. - The structural pass (§20 step 4) still needs Max's authorization, recorded as a chief-of-staff decision. `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` now refuse without `--authorized-by `, exiting non-zero before any PSID path is resolved or opened, and the loader refuses an unfrozen load without the record; the flag records the decision id, the git head and the UTC time, and does not grant the authorization. §17 item 2 and §20 step 4 say so. - Wording: the six registries that record the independent adjudication no longer end their note with the six code-88 records staying TO VERIFY pending d745; each says Max confirmed 1a–1c on 2026-10-12 and that the 1c commit resolved them. The package docstring names 1a–1c among d637's ratified amendments. +- After the reviews of the d745, 1a, 1b, 1c-builder, 1c and wording commits (GPT-6.1 Sol, Subfleet jobs `20261012-101531-d637-review-d745`, `-review-a1a`, `20261012-101532-d637-review-a1b`, `20261012-102316-d637-review-b1c`, `20261012-104946-d637-review-a1c` and `20261012-105640-d637-review-w745`; 1b APPROVE, the rest REQUEST CHANGES, the 1c review with one blocking finding): a frozen load must pass the committed pre-registration record, and any other refuses before any PSID path is resolved, so a fabricated record cannot stand in for the authorization an unfrozen load needs; while a frozen load reads, opening a file under the PSID directory that the record does not freeze refuses before the file is read. Every required prose field of a ruled entry must be a nonblank string; 1a pins each code's annuity, marital and birth-support conventions; 1b refuses any adjudication record, null included; the d637 differential counts a field added as null as a change. A new invented variant puts a code-88 cohabitor who is itself a U0 target (born 1951, observed in 2019) in a family with OFUM Social Security and SSI: it keeps the OFUM role and its recorded marriage and, in U4, the family income basis and the OFUM SSI cap. Stale wording in `rows.py`, the dry run, the master record and the exposure log is corrected. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1_source_adjudication.md b/docs/design/u2_m1_source_adjudication.md index c7b1089d6..b0b6e17ff 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -70,7 +70,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu ## Max's ruling d637 applied (registry commits from 2026-10-11) -Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): amendments 2–5 ratified as written and amendment 6's option 1 ratified as an explicit amendment to draft-3 line 134. Each amendment's registry effect is its own reviewed commit, and `RATIFIED_AMENDMENTS` in `src/populace_dynamics/data/u2_source_registry.py` pins each applied amendment's entries, ruling and gate fields. Each ruled entry keeps its adjudication record and gains a `ruling`; an open question or refusal the ruling answers is kept as `superseded_question` or `superseded_refusal`. Max confirmed on 2026-10-12 that d637 ratified amendment 1's parts 1a–1c as written (d745), and their registry commits are listed here too. +Max ruled on the §16a amendments on 2026-09-30 (d637; specification §16b): amendments 2–5 ratified as written and amendment 6's option 1 ratified as an explicit amendment to draft-3 line 134. Each amendment's registry effect is its own reviewed commit, and `RATIFIED_AMENDMENTS` in `src/populace_dynamics/data/u2_source_registry.py` pins each applied amendment's entries, ruling and gate fields. Each ruled entry keeps its adjudication record and gains a `ruling`; an open question or refusal the ruling answers is kept as `superseded_question` or `superseded_refusal`. Max confirmed on 2026-10-12 that d637 ratified amendment 1's parts 1a–1c as written (d745); the 1a, 1b and 1c rows below record their registry commits. | Amendment | Records | Registry effect | |---|---:|---| diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 3edfb8070..8012a46d8 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -412,7 +412,8 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 1b: `roles.json` (2013's code-90 record and the d637 record) and `u1_identity.json`; the specification's §17 item 2 and §19; the master record (the 2013 code-90 row and the d637 section); `u2_source_registry.py` (1b's scope, the first with no adjudication disposition) and `sources.py` (the roles byte pin); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py` and `test_u2_mappings.py`; and this section. - Amendment 1c, builder: `sources.py` (the declared code-88 rule and its docstrings), `cohort.py` and `estimator.py` (docstrings), `invented.py` (the two code-88 variant notes) and `scripts/track_u2_dry_run.py` (the code-88 checks); `u1_identity.json`; the specification's §19; `tests/track_u2/test_u2_roles.py` and `test_u2_scripts.py`; and this section. - Amendment 1c: `roles.json` (the six code-88 records and the d637 record), `income.json` (amendment 2's ten `use_restriction` sentences) and `u1_identity.json`; the specification's §17 item 2, §19 and §20 step 4; the master record (the summary, the code-88 and spouse age/sex blocker rows, the d637 section and sixteen per-route rows); `u2_source_registry.py` (1c's scope), `sources.py` (the roles and income byte pins and docstrings), `loader.py` (the structural-pass authorization), `invented.py` (two variant notes), `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` (`--authorized-by`), `scripts/track_u2_dry_run.py` and `scripts/run_track_u2_registered.py` (checks and docstrings); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py`, `test_u2_scripts.py` and `test_u2_loader.py`; and this section. -- Wording after d745: the six `independent_adjudication` notes (`income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json`, `weights.json`) and `u1_identity.json`; the specification's §19; `sources.py` (six byte pins and three docstring paragraphs rewrapped) and the package docstring; `tests/track_u2/test_u2_mappings.py`; and this section. +- Wording after d745: the six `independent_adjudication` notes (`income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json`, `weights.json`) and `u1_identity.json`; the specification's §19; `sources.py` (six byte pins and three docstring and comment paragraphs rewrapped) and the package docstring; `tests/track_u2/test_u2_mappings.py`; and this section. +- Answers to the d745 and 1a–1c reviews: `loader.py` (the committed-record check and the unfrozen-open guard), `u2_source_registry.py` (typed prose fields, 1a's governed role conventions, 1b's absent adjudication), `invented.py` (one variant appended), `rows.py` and `scripts/track_u2_dry_run.py` (docstrings), and `u1_identity.json`; the specification's §19; the master record (the d637 section's introduction); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_loader.py`, `test_u2_mappings.py`, `test_u2_roles.py` and `test_u2_scripts.py`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/scripts/track_u2_dry_run.py b/scripts/track_u2_dry_run.py index 7f473191a..7f8f14ba2 100644 --- a/scripts/track_u2_dry_run.py +++ b/scripts/track_u2_dry_run.py @@ -564,9 +564,9 @@ def estimate(spec: estimator.U2IncomeSpec) -> pd.DataFrame: def _forged_committed(registries: sources.RegistrySet) -> None: - """Registries labelled committed whose 2015 code-88 role entry (TO - VERIFY) has been edited to apply: refused (the label is bound to the - pins).""" + """Registries labelled committed whose RESOLVED 2015 code-88 role + entry has its ratified action removed: refused (the label is bound to + the pins).""" documents = copy.deepcopy(dict(registries.documents)) for entry in documents["roles"]["entries"]: diff --git a/src/populace_dynamics/data/u2_source_registry.py b/src/populace_dynamics/data/u2_source_registry.py index cbf548802..3082c199f 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -109,6 +109,7 @@ "disposition": "D", "fields": { "status": "RESOLVED", + "present": True, "action": "documented_rule", "income_role": "ofum", "spouse_slot": False, @@ -121,6 +122,31 @@ ), "required": ("resolution", "ruling_effect", "completeness_disclosure"), "markers": ("completeness_disclosure",), + # Section 16a 1a: code 90 keeps the co-resident legal-spouse + # annuity life and relationship-based marital resolution, code 92 + # section 3's nonspouse conventions; only code 90 keeps the + # inherited administrative birth support (section 3). + "governs": { + **{ + key: ( + { + "legal_spouse_annuity": True, + "marital_resolution": True, + "administrative_birth_support": True, + }, + (), + ) + for key in ("2015.relationship.90", "2017.relationship.90") + }, + "2017.relationship.92": ( + { + "legal_spouse_annuity": False, + "marital_resolution": False, + "administrative_birth_support": False, + }, + (), + ), + }, }, # 2013's inherited code-90 meaning was RESOLVED in milestone 1 and # carries no adjudication record (disposition None). @@ -131,14 +157,17 @@ "disposition": None, "fields": { "status": "RESOLVED", + "present": True, "label": "Legal husband of Head", "action": "documented_rule", "income_role": "ofum", "spouse_slot": False, "legal_spouse_annuity": True, "marital_resolution": True, + "administrative_birth_support": True, }, "absent": ( + "adjudication", "refusal", "question", "blocking_dependencies", @@ -415,6 +444,21 @@ "markers": ("registration_disclosure",), }, } +#: The prose fields an amendment's ``required`` may name: each must be a +#: nonblank string (review of 630a68c3, finding 2); the other required +#: fields keep their own types (a list of variables, a dict). +_RULED_PROSE = frozenset( + { + "completeness_disclosure", + "condition", + "required_amendment", + "resolution", + "ruling_effect", + "superseded_question", + "superseded_refusal", + "use_restriction", + } +) #: The rulings a registry entry may record. _RULED_DECISIONS = ("d637", "d1090") #: The d637 amendment each ruled entry belongs to, by (registry, id). @@ -526,13 +570,14 @@ def _ratified_amendment(entry: dict[str, Any], name: str) -> None: raise SourceAdjudicationError( f"d637 amendment {amendment} needs {field} for {entry['id']}" ) + _fields(entry, {f: str for f in scope["required"] if f in _RULED_PROSE}) if entry.get("blocking_dependencies"): raise SourceAdjudicationError( f"A d637 ruling applies only unblocked, for {entry['id']}" ) record = entry.get("adjudication") if scope["disposition"] is None: - if record is not None: + if "adjudication" in entry: raise SourceAdjudicationError( f"Unexpected adjudication record for {entry['id']}" ) diff --git a/src/populace_dynamics/uniform_cut_track_u2/invented.py b/src/populace_dynamics/uniform_cut_track_u2/invented.py index 444663383..8f422c08b 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/invented.py +++ b/src/populace_dynamics/uniform_cut_track_u2/invented.py @@ -1524,6 +1524,60 @@ def _sex_unknown_target_in_excluded_unit_2019( return dataclasses.replace(base, persons=persons) +#: The code-88 cohort target's recorded (ended) marriage: amendment 1c +#: keeps a first-year cohabitor's own legal marital history. +_CODE_88_TARGET_MARRIAGE = { + "marriage_order": 1, + "start_year": 1972, + "end_year": 1986, + "how_ended": "divorce", + "last_known_status": "divorced", + "most_recent_report_year": 2023, + "n_marriages": 1, + "n_records": 1, + "is_marriage": True, +} + + +def _code_88_target_with_ofum_income_2019( + base: cohort.U2Inputs, +) -> cohort.U2Inputs: + income = base.family_income[2019] + family = int( + income.loc[ + income["ofum_ss"].gt(0) & income["ofum_ssi"].gt(0), "interview" + ].iloc[0] + ) + anchor = base.anchors[2019] + sequence = int( + anchor.loc[anchor["interview"].eq(family), "sequence"].max() + ) + person = _PERSON_BASE - 4 + changed = _added_person( + base, + person, + wave=2019, + interview=family, + sequence=sequence + 1, + relationship=88, + age=68, + weight=500.0, + sex="female", + ) + history = base.marriage_history + record = {column: pd.NA for column in history.columns} + record.update( + person_id=person, + sex="female", + birth_year=1951, + **_CODE_88_TARGET_MARRIAGE, + ) + history = pd.concat( + [history, pd.DataFrame([record])], ignore_index=True + ).astype(history.dtypes.to_dict()) + return dataclasses.replace(changed, marriage_history=history) + + # Appended in order, so every earlier variant is unchanged. _VARIANT_BUILDERS = { "code_88_first_year_cohabitor": _code_88_first_year_cohabitor, @@ -1544,6 +1598,9 @@ def _sex_unknown_target_in_excluded_unit_2019( "spouse_slot_occupant_without_family_spouse_2019": ( _spouse_slot_occupant_without_family_spouse_2019 ), + "code_88_target_with_ofum_income_2019": ( + _code_88_target_with_ofum_income_2019 + ), } _VARIANT_NOTES: dict[str, str] = { "code_88_first_year_cohabitor": ( @@ -1614,6 +1671,13 @@ def _sex_unknown_target_in_excluded_unit_2019( "(amendment 2, ratified by d637), so the slot flag and the roster " "disagree and the income rows refuse (refusal case)" ), + "code_88_target_with_ofum_income_2019": ( + "a code-88 first-year cohabitor born 1951, divorced in 1986, joins " + "the first 2019 family with OFUM Social Security and SSI: a U0 " + "target whose own observation has the OFUM role under amendment " + "1c (the family income basis in U4 and the OFUM SSI cap), keeps " + "its recorded marital history and is no spouse-slot occupant" + ), } #: The generator's named variants (see :data:`_VARIANT_NOTES`). INVENTED_VARIANTS: tuple[str, ...] = tuple(_VARIANT_BUILDERS) diff --git a/src/populace_dynamics/uniform_cut_track_u2/loader.py b/src/populace_dynamics/uniform_cut_track_u2/loader.py index c9fdce98d..bc27c5a0c 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/loader.py +++ b/src/populace_dynamics/uniform_cut_track_u2/loader.py @@ -51,7 +51,8 @@ before the source preflight and before any PSID path is resolved (:class:`U2AuthorizationRefusal`). The record only records the authorization; it does not grant one. The registered run's load is -frozen instead: its evidence binds every file and frame digest. +frozen instead: its evidence, which must be the committed record, binds +every file and frame digest. **Frozen identities** (section 14: "changed source bytes, changed frame digests ... refuse execution. Pre-registration mapping checks resolve @@ -62,9 +63,12 @@ written by ``scripts/track_u2_structure.py``); the committed record lives at :data:`PREREGISTRATION_EVIDENCE_PATH`, and the registered run binds its hash, file hashes and frame digest into the registration binding. Given -that record, :func:`load_u2_inputs` rehashes every frozen file before any -record is parsed and refuses a changed or missing one, then refuses a -read file set or frame digest other than the frozen ones +that record -- the committed one: any other, however well-formed, +refuses before any PSID path is resolved -- :func:`load_u2_inputs` +rehashes every frozen file before any record is parsed and refuses a +changed or missing one, refuses to open any file under the PSID +directory that the record does not freeze while it reads, then refuses +a read file set or frame digest other than the frozen ones (:class:`U2FrozenIdentityMismatch`); the seal then records the evidence hash, which a registered run requires. @@ -80,12 +84,16 @@ from __future__ import annotations +import contextlib +import contextvars import dataclasses import datetime import hashlib import json +import os import re -from collections.abc import Iterable, Mapping, Sequence +import sys +from collections.abc import Iterable, Iterator, Mapping, Sequence from pathlib import Path, PurePosixPath from types import MappingProxyType from typing import Any @@ -553,18 +561,32 @@ def load_u2_inputs( ``evidence`` is the frozen pre-registration record (:func:`check_preregistration_evidence`). The registered run passes - it; the section 20 step-4 pass, which writes it, passes nothing. With - it, every frozen file is rehashed before any record is parsed - (:func:`check_frozen_files`), and after reading, the files read and + it; the section 20 step-4 pass, which writes it, passes nothing. It + must be the committed, reviewed record + (:func:`read_preregistration_evidence`), not just any well-formed + one: a fabricated record would otherwise stand in for the + authorization an unfrozen load needs. An absent or different record + refuses before any PSID path is resolved. With it, every frozen file + is rehashed before any record is parsed (:func:`check_frozen_files`); + while records are read, opening any file under the PSID directory + that the record does not freeze refuses before the file is read + (:func:`_only_frozen_files`); and after reading, the files read and the frame digest must equal the frozen ones, or the load refuses (:class:`U2FrozenIdentityMismatch`). """ if evidence is None: check_structural_pass_authorization(authorization) - frozen = ( - None if evidence is None else check_preregistration_evidence(evidence) - ) + frozen = None + else: + frozen = check_preregistration_evidence(evidence) + if frozen != read_preregistration_evidence(): + raise U2FrozenIdentityMismatch( + "the evidence passed is not the committed pre-registration " + f"record ({PREREGISTRATION_EVIDENCE_PATH}): a frozen load " + "reads PSID records only against the reviewed record the " + "section 20 step-4 pass wrote" + ) registries = sources.RegistrySet.committed() preflight = source_preflight(registries) # Stages 2 and 3 run only once every required route is resolved. @@ -582,7 +604,12 @@ def load_u2_inputs( "execution (section 14)" ) check_frozen_files(frozen, data_root) - with psid2010.record_files_read(data_root) as files: + guard = ( + contextlib.nullcontext() + if frozen is None + else _only_frozen_files(data_root, frozen["psid_files_sha256"]) + ) + with guard, psid2010.record_files_read(data_root) as files: incomes, wealth, pensions = {}, {}, {} for wave in sources.SUPPORT_WAVES: sps_path, txt_path = family._family_paths(wave, data_root) @@ -840,6 +867,60 @@ def check_frozen_files( return dict(frozen["psid_files_sha256"]) +#: The PSID directory and frozen files of the frozen load in progress +#: (:func:`_only_frozen_files`), or ``None``. +_FROZEN_OPENS: contextvars.ContextVar[ + tuple[tuple[Path, ...], frozenset[str]] | None +] = contextvars.ContextVar("u2_frozen_opens", default=None) +_FROZEN_HOOK_INSTALLED = False + + +def _refuse_unfrozen_open(event: str, args: tuple[Any, ...]) -> None: + """Audit hook: during a frozen load, refuse to open a file under the + PSID directory that the evidence does not freeze.""" + + if event != "open": + return + state = _FROZEN_OPENS.get() + if state is None or not args or isinstance(args[0], int): + return + roots, allowed = state + literal = Path(os.path.abspath(os.fsdecode(args[0]))) + for path in (literal, literal.resolve()): + for root in roots: + if path.is_relative_to(root): + relative = path.relative_to(root).as_posix() + if relative in allowed: + return + raise U2FrozenIdentityMismatch( + f"{relative} is under the PSID directory but not in the " + "frozen pre-registration evidence: an unfrozen file " + "is never read (section 14)" + ) + + +@contextlib.contextmanager +def _only_frozen_files( + data_root: Path, frozen_files: Mapping[str, str] +) -> Iterator[None]: + """While the block runs, opening any file under ``data_root`` that is + not in ``frozen_files`` refuses before the file is read (a Python audit + hook, installed once per process).""" + + global _FROZEN_HOOK_INSTALLED + if not _FROZEN_HOOK_INSTALLED: + sys.addaudithook(_refuse_unfrozen_open) + _FROZEN_HOOK_INSTALLED = True + literal = Path(os.path.abspath(Path(data_root).expanduser())) + token = _FROZEN_OPENS.set( + ((literal, literal.resolve()), frozenset(frozen_files)) + ) + try: + yield + finally: + _FROZEN_OPENS.reset(token) + + def _check_against_frozen( frozen: Mapping[str, Any], files: Mapping[str, str], frames: str ) -> None: diff --git a/src/populace_dynamics/uniform_cut_track_u2/rows.py b/src/populace_dynamics/uniform_cut_track_u2/rows.py index d833743bd..6d7bce878 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/rows.py +++ b/src/populace_dynamics/uniform_cut_track_u2/rows.py @@ -21,9 +21,9 @@ record it. :func:`check_u2_rulings_against_block` refuses a block whose ``decisions`` differ, and refuses U1's ``MAX_RULINGS`` outright: copying U1's authorization is insufficient (section 14). The current - ``u2-draft-4`` block still records ``u2_ratification: pending_Max`` - (and section 16a's amendments await Max's ruling); section 20 step 5 - materializes the final block, so a registered run refuses. + ``u2-draft-4`` block still records ``u2_ratification: pending_Max``; + section 20 step 5 materializes the final block, so a registered run + refuses. """ from __future__ import annotations diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 196b7465c..65e96d8cd 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,499 | -| `artifact` | 4,186 | +| `unit` | 7,502 | +| `artifact` | 4,201 | | `integration_psid` | 1,401 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,914** | +| **Total** | **13,932** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index c18185f47..498c37b56 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,8 +1,8 @@ { "schema_version": 1, "counts": { - "unit": 7499, - "artifact": 4186, + "unit": 7502, + "artifact": 4201, "integration_psid": 1401, "reproduction_legacy": 520, "oracle_policyengine": 308 diff --git a/tests/track_u2/test_adjudication_applied.py b/tests/track_u2/test_adjudication_applied.py index e2c3712e1..2d2ee3b32 100644 --- a/tests/track_u2/test_adjudication_applied.py +++ b/tests/track_u2/test_adjudication_applied.py @@ -521,10 +521,13 @@ def test_d637_rulings_are_recorded_on_their_entries(documents, monkeypatch): ruling["specification_section"], ruling["amendment"], ) == ("d637", "2026-09-30", "16b", amendment) - # 2013's code-90 meaning (1b) has no adjudication record. - record = entry.get("adjudication", {"disposition": None}) - assert record["disposition"] == D637_APPLIED[amendment], key - if record["disposition"] is not None: + if D637_APPLIED[amendment] is None: + # 2013's code-90 meaning (1b) has no adjudication record + # (review of 7950065b). + assert "adjudication" not in entry, key + else: + record = entry["adjudication"] + assert record["disposition"] == D637_APPLIED[amendment], key assert record["amendment"] == number, key assert entry["status"] == "RESOLVED", key assert any( diff --git a/tests/track_u2/test_source_registries.py b/tests/track_u2/test_source_registries.py index 42ce345ca..dd28ddfc8 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -1029,6 +1029,69 @@ def _copy_ruling(document, source, target): ] ), ), + # Review of e74a5546: 1a's per-entry role conventions and typed text. + "a1a_code_90_annuity_dropped": ( + "roles", + lambda d: _entry(d, "2015.relationship.90").update( + legal_spouse_annuity=False + ), + ), + "a1a_code_90_marital_dropped": ( + "roles", + lambda d: _entry(d, "2017.relationship.90").update( + marital_resolution=False + ), + ), + "a1a_code_92_annuity_added": ( + "roles", + lambda d: _entry(d, "2017.relationship.92").update( + legal_spouse_annuity=True + ), + ), + "a1a_code_92_marital_added": ( + "roles", + lambda d: _entry(d, "2017.relationship.92").update( + marital_resolution=True + ), + ), + "a1a_code_92_birth_support_added": ( + "roles", + lambda d: _entry(d, "2017.relationship.92").update( + administrative_birth_support=True + ), + ), + "a1a_marked_absent": ( + "roles", + lambda d: _entry(d, "2017.relationship.90").update( + present=False, label=None + ), + ), + "a1a_disclosure_blank": ( + "roles", + lambda d: _entry(d, "2017.relationship.92").update( + completeness_disclosure=" " + ), + ), + "a1a_disclosure_not_text": ( + "roles", + lambda d: _entry(d, "2015.relationship.90").update( + completeness_disclosure=True + ), + ), + "a1a_effect_blank": ( + "roles", + lambda d: _entry(d, "2017.relationship.90").update(ruling_effect=" "), + ), + "a1b_adjudication_null": ( + "roles", + lambda d: _entry(d, "2013.relationship.90").update(adjudication=None), + ), + "a1b_birth_support_dropped": ( + "roles", + lambda d: _entry(d, "2013.relationship.90").update( + administrative_birth_support=False + ), + ), "a1b_meaning_changed": ( "roles", lambda d: _entry(d, "2013.relationship.90").update( @@ -1120,6 +1183,27 @@ def _copy_ruling(document, source, target): "confirmed_by" ), ), + # Review of 630a68c3, finding 2: required prose is nonblank text. + "a1c_resolution_a_number": ( + "roles", + lambda d: _entry(d, "2019.relationship.88").update(resolution=42), + ), + "a1c_question_a_list": ( + "roles", + lambda d: _entry(d, "2019.relationship.88").update( + superseded_question=["not a question"] + ), + ), + "a1c_resolution_blank": ( + "roles", + lambda d: _entry(d, "2021.relationship.88").update(resolution=" "), + ), + "a2_restriction_blank": ( + "income", + lambda d: _entry(d, "income.2019.wife_age").update( + use_restriction="\t" + ), + ), "a1c_adjudication_changed": ( "roles", lambda d: _entry(d, "2023.relationship.88")["adjudication"].update( diff --git a/tests/track_u2/test_u2_loader.py b/tests/track_u2/test_u2_loader.py index 1ec0f8a08..959892905 100644 --- a/tests/track_u2/test_u2_loader.py +++ b/tests/track_u2/test_u2_loader.py @@ -11,7 +11,7 @@ and an individual file and setup -- in a temporary directory and runs :func:`loader.load_u2_inputs` against it, an unfrozen load with an invented authorization record (:data:`TEST_AUTHORIZATION`, naming no -real decision), with four patches, each named where it is applied: +real decision), with up to five patches, each named where it is applied: * ``sources.blockers`` returns no blocker, so stage 1 and the registry gate pass whatever the committed registries hold (the registry's own @@ -20,7 +20,11 @@ cite the real PSID setup files and codebooks, which are not staged here (the rehash itself is tested in ``test_u2_mappings.py``); * the inherited marriage-history and earnings readers return the - invented frames. + invented frames; +* for a frozen load, ``loader.read_preregistration_evidence`` returns the + invented evidence record the load is given, standing in for the + committed record (none is committed yet; a frozen load refuses any + other record, ``test_a_frozen_load_needs_the_committed_record``). An audit hook refuses any open under the PSID directory -- the default one and the one under the account's real home, which differ when HOME is @@ -205,6 +209,14 @@ def _load( "family_earnings_panel", lambda *, waves=None, data_dir=None: inputs.observed_earnings, ) + if evidence is not None: + patch.setattr( + loader, + "read_preregistration_evidence", + lambda *args, **kwargs: loader.check_preregistration_evidence( + evidence + ), + ) _GUARD["active"], _GUARD["opened"] = True, [] if evidence is None: return loader.load_u2_inputs( @@ -877,8 +889,76 @@ def test_a_file_read_but_not_frozen_refuses( "psid_files_sha256": files, "psid_files_bundle_sha256": loader.files_bundle_sha256(files), } - with pytest.raises(loader.U2LoaderRefusal, match="read but not frozen"): - _load(tmp_path, u2_inputs, evidence=narrower) + # Review of 630a68c3, finding 1: the unfrozen file is refused when it + # is opened, before any of its records is parsed. + parsed = [] + patch = pytest.MonkeyPatch() + original = loader.read_family_records + patch.setattr( + loader, + "read_family_records", + lambda handle, wave, gate: parsed.append(wave) + or original(handle, wave, gate), + ) + try: + with pytest.raises( + loader.U2FrozenIdentityMismatch, + match="family/2017/FAM2017ER.txt is under the PSID directory " + "but not in the frozen", + ): + _load(tmp_path, u2_inputs, evidence=narrower) + finally: + patch.undo() + assert 2017 not in parsed + assert parsed == [2013, 2015] + + +def test_a_frozen_load_needs_the_committed_record( + tmp_path, evidence, monkeypatch +): + """Review of 630a68c3, finding 1: a frozen load reads PSID records only + against the committed pre-registration record, so a fabricated record, + however well-formed, cannot stand in for the authorization an unfrozen + load needs. It refuses before any PSID path is resolved or any file + opened: while no record is committed, and when the committed one + differs.""" + + import sys + + from populace_dynamics.data import psid + + resolved, opened = [], [] + + def spy(event, args): + if event == "open" and args and str(args[0]).startswith(str(tmp_path)): + opened.append(str(args[0])) + + sys.addaudithook(spy) + original = psid._resolve_data_dir + monkeypatch.setattr( + psid, + "_resolve_data_dir", + lambda data_dir: resolved.append(data_dir) or original(data_dir), + ) + assert not loader.PREREGISTRATION_EVIDENCE_PATH.exists() + with pytest.raises( + loader.U2FrozenIdentityMismatch, match="no pre-registration evidence" + ): + loader.load_u2_inputs(data_dir=tmp_path, evidence=evidence) + committed = {**evidence, "input_frames_sha256": "0" * 64} + monkeypatch.setattr( + loader, + "read_preregistration_evidence", + lambda *args, **kwargs: loader.check_preregistration_evidence( + committed + ), + ) + with pytest.raises( + loader.U2FrozenIdentityMismatch, + match="not the committed pre-registration record", + ): + loader.load_u2_inputs(data_dir=tmp_path, evidence=evidence) + assert resolved == [] and opened == [] def _altered(evidence, **changes): diff --git a/tests/track_u2/test_u2_mappings.py b/tests/track_u2/test_u2_mappings.py index 7bb62cc92..61bc96e10 100644 --- a/tests/track_u2/test_u2_mappings.py +++ b/tests/track_u2/test_u2_mappings.py @@ -193,8 +193,27 @@ def test_milestone_1b_moved_only_citations_and_prose(): def _changed_keys(before, after): + """The keys whose presence or JSON value differs: a key added as null + is a change, and ``true`` is not ``1`` (review of e74a5546, finding + 3).""" keys = set(before) | set(after) - return {k for k in keys if before.get(k, None) != after.get(k, None)} + return { + k + for k in keys + if k not in before + or k not in after + or json.dumps(before[k], sort_keys=True) + != json.dumps(after[k], sort_keys=True) + } + + +def test_changed_keys_sees_a_key_added_as_null(): + """Review of e74a5546, finding 3: the differential's field comparison + sees presence and JSON type, not just ``dict.get``.""" + assert _changed_keys({"a": 1}, {"a": 1, "b": None}) == {"b"} + assert _changed_keys({"a": 1, "b": None}, {"a": 1}) == {"b"} + assert _changed_keys({"a": 1}, {"a": True}) == {"a"} + assert _changed_keys({"a": [1, {"x": 2}]}, {"a": [1, {"x": 2}]}) == set() def test_d1090_moved_only_the_ruled_gate_fields(): diff --git a/tests/track_u2/test_u2_roles.py b/tests/track_u2/test_u2_roles.py index ce3e29c52..e5d0dcd51 100644 --- a/tests/track_u2/test_u2_roles.py +++ b/tests/track_u2/test_u2_roles.py @@ -344,6 +344,71 @@ def refusing(): ) +def test_a_code_88_target_has_the_ofum_role_and_keeps_its_history( + u2_inputs, u2_params +): + """Review of 1bcb8765, finding 1: a code-88 first-year cohabitor who is + itself a U0 target (born 1951, observed in 2019) in a family with OFUM + Social Security and SSI. Under amendment 1c, and under both role + contexts, its own observation has the OFUM role and keeps its recorded + (ended) marriage; in U4 (the head/wife income unit) it keeps the + family income basis and the OFUM SSI cap, so a builder that gave it + the spouse slot's role would fail here.""" + + variant = invented.invented_variant( + u2_inputs, "code_88_target_with_ofum_income_2019" + ) + anchor = variant.anchors[2019] + (person,) = set(anchor.loc[anchor["relationship"].eq(88), "person_id"]) + for context in ( + sources.RoleContext.declared(), + sources.RoleContext.from_registry(), + ): + built = cohort.build_u2_cohort(variant, role_context=context) + obs = built.observations + (row,) = obs[obs["person_id"].eq(person)].itertuples(index=False) + assert (row.birth_year, row.wave, row.member_role) == ( + 1951, + 2019, + "ofum", + ), context.kind + assert (row.marital_status, row.marital_resolution) == ( + "divorced", + "marriage_history", + ), context.kind + assert not row.married and pd.isna(row.spouse_person_id) + built = cohort.build_u2_cohort( + variant, role_context=sources.RoleContext.declared() + ) + members = cohort.income_rows(built, variant).reset_index(drop=True) + spec = estimator.U2IncomeSpec(income_unit="head_wife") + out = estimator.u2_adjusted_incomes( + members, + spec, + context=estimator.U2EstimatorContext(sources.INVENTED_DECLARED), + data_provenance=ap.INVENTED, + life_table=u2_params.life_tables[spec.mortality_basis], + thresholds=u2_params.thresholds, + ssi=u2_params.ssi, + ) + (index,) = members.index[members["person_id"].eq(person)] + row = members.loc[index] + assert row["member_role"] == "ofum" + assert out.loc[index, "income_basis"] == "family_unit" + assert row["ofum_ss"] > 0 and row["ofum_ssi"] > 0 + assert row["head_ssi"] == row["wife_ssi"] == 0 + # The OFUM SSI cap (U1's rule): the countable Social Security fall + # under the 13 percent cut, capped at the individual rate less the + # OFUM SSI, as the whole SSI offset. + general = 12 * u2_params.ssi.general_income_exclusion_monthly + ss, cut = float(row["ofum_ss"]), spec.cut_rate + fall = max(0.0, ss - general) - max(0.0, (1.0 - cut) * ss - general) + year = int(row["income_year"]) + cap = max(0.0, u2_params.ssi.fbr_annual(year, False) - row["ofum_ssi"]) + assert out.loc[index, "ssi_offset"] == pytest.approx(min(fall, cap)) + assert out.loc[index, "ssi_offset"] > 0 + + def test_declared_context_refuses_non_invented_inputs(u2_inputs, declared): caller = cohort.replace_provenance(u2_inputs, kind="caller_frames") with pytest.raises(cohort.U2CohortError, match="invented"): diff --git a/tests/track_u2/test_u2_scripts.py b/tests/track_u2/test_u2_scripts.py index daf837810..3d4c3dd35 100644 --- a/tests/track_u2/test_u2_scripts.py +++ b/tests/track_u2/test_u2_scripts.py @@ -183,6 +183,7 @@ def test_dry_run_records_every_branch_and_refusal(dry_run): "zero_weight_target_in_excluded_unit_2015", "sex_unknown_target_in_excluded_unit_2019", "spouse_slot_occupant_without_family_spouse_2019", + "code_88_target_with_ofum_income_2019", ] assert all(entry["regenerated"] for entry in variants.values()) # Amendment 1c: the two code-88 variants build under the declared From ce3d90cf6a61422eb016ebfd747f0facc2bc4cf6 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Thu, 15 Oct 2026 08:19:33 -0400 Subject: [PATCH 21/21] Cover the frozen-load guard's readers and the cited-source check The GPT-6.1 Sol delta review of the answers (Subfleet 20261014-055752-d637-review-r745b) approved and left three test-coverage notes, all closed here (tests only): - The open-time refusal of an unfrozen file was tested only on a family file opened through Path.open. It is now tested on each reader's own opener: a family file, a setup file read by the layout parser, and the individual file read through pandas; each refuses at open, before the reader returns. - The frozen comparison of the cited setup files and codebooks had no test (every load patched the rehash to nothing): a record whose cited sources differ now refuses before any family record is parsed. - In the code-88 cohort target's test the countable Social Security fall, not the OFUM SSI cap, was the binding bound. With the OFUM SSI raised to $500 below the individual rate the cap binds, so a cap at the couple rate, or one that ignored the OFUM SSI, would fail. Section 19 and the exposure log record it; the spec is re-pinned; tier counts recounted by collection (+3 unit). Co-Authored-By: Claude Opus 5.5 --- data/external/track_u2/u1_identity.json | 2 +- docs/design/boomers2004_1946_55_comparison.md | 1 + docs/design/u2_m1b_exposure.md | 1 + tests/README-tiers.md | 4 +- tests/tier_counts.json | 2 +- tests/track_u2/test_u2_loader.py | 94 ++++++++++++++++--- tests/track_u2/test_u2_roles.py | 21 +++++ 7 files changed, 109 insertions(+), 16 deletions(-) diff --git a/data/external/track_u2/u1_identity.json b/data/external/track_u2/u1_identity.json index ea85c298d..153e66f90 100644 --- a/data/external/track_u2/u1_identity.json +++ b/data/external/track_u2/u1_identity.json @@ -60,7 +60,7 @@ "draft_3_commit": "f7412e00cdcdba6ee1156b74fa93039f5329cb8f", "draft_3_git_blob": "e07ee0d589d20f3b6d100e5d4ce173f9a653f5e9", "version": "u2-draft-4", - "sha256": "cdfef5ebc9f3208410de3d52220d34e27c3c713f4acbe986136c4d75c61d37eb", + "sha256": "7b1350ff7099405282c50bad27bd90d5d2aad38f07f391d1b459840d56b03269", "cited_lines_sha256": { "74": "7b299c9e4473fd51d04bdfec51af83268e517181ab734d3e76ccd1c744f6daa9", "75": "44454cc04e6a50863035d69ccb53edec175b4ad70a19d8877437901d2deb8428", diff --git a/docs/design/boomers2004_1946_55_comparison.md b/docs/design/boomers2004_1946_55_comparison.md index e8a50b635..4519dd0ed 100644 --- a/docs/design/boomers2004_1946_55_comparison.md +++ b/docs/design/boomers2004_1946_55_comparison.md @@ -1872,6 +1872,7 @@ Revision verification comprised the two touched test files and `tests/track_u2/t - The structural pass (§20 step 4) still needs Max's authorization, recorded as a chief-of-staff decision. `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` now refuse without `--authorized-by `, exiting non-zero before any PSID path is resolved or opened, and the loader refuses an unfrozen load without the record; the flag records the decision id, the git head and the UTC time, and does not grant the authorization. §17 item 2 and §20 step 4 say so. - Wording: the six registries that record the independent adjudication no longer end their note with the six code-88 records staying TO VERIFY pending d745; each says Max confirmed 1a–1c on 2026-10-12 and that the 1c commit resolved them. The package docstring names 1a–1c among d637's ratified amendments. - After the reviews of the d745, 1a, 1b, 1c-builder, 1c and wording commits (GPT-6.1 Sol, Subfleet jobs `20261012-101531-d637-review-d745`, `-review-a1a`, `20261012-101532-d637-review-a1b`, `20261012-102316-d637-review-b1c`, `20261012-104946-d637-review-a1c` and `20261012-105640-d637-review-w745`; 1b APPROVE, the rest REQUEST CHANGES, the 1c review with one blocking finding): a frozen load must pass the committed pre-registration record, and any other refuses before any PSID path is resolved, so a fabricated record cannot stand in for the authorization an unfrozen load needs; while a frozen load reads, opening a file under the PSID directory that the record does not freeze refuses before the file is read. Every required prose field of a ruled entry must be a nonblank string; 1a pins each code's annuity, marital and birth-support conventions; 1b refuses any adjudication record, null included; the d637 differential counts a field added as null as a change. A new invented variant puts a code-88 cohabitor who is itself a U0 target (born 1951, observed in 2019) in a family with OFUM Social Security and SSI: it keeps the OFUM role and its recorded marriage and, in U4, the family income basis and the OFUM SSI cap. Stale wording in `rows.py`, the dry run, the master record and the exposure log is corrected. +- After the delta review of those answers (GPT-6.1 Sol, Subfleet job `20261014-055752-d637-review-r745b`; APPROVE with three test-coverage notes): the open-time refusal of an unfrozen file is tested on each reader's own opener (a family file, a setup file, the individual file read through pandas); a frozen load whose cited setup files and codebooks differ from the record refuses before any family record is parsed, now under test; and the code-88 cohort target's test also makes the OFUM SSI cap the binding bound. Tests only. - No count, statistic or outcome was computed, no model or estimator was run on real data, and no file on the restricted list was opened for reading. Reading and exposure are appended to `docs/design/u2_m1b_exposure.md`. ## 20. Ratification and execution record diff --git a/docs/design/u2_m1b_exposure.md b/docs/design/u2_m1b_exposure.md index 8012a46d8..7509cb5bf 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -414,6 +414,7 @@ The U2 tests ran with the PSID root staged. They read staged PSID documentation - Amendment 1c: `roles.json` (the six code-88 records and the d637 record), `income.json` (amendment 2's ten `use_restriction` sentences) and `u1_identity.json`; the specification's §17 item 2, §19 and §20 step 4; the master record (the summary, the code-88 and spouse age/sex blocker rows, the d637 section and sixteen per-route rows); `u2_source_registry.py` (1c's scope), `sources.py` (the roles and income byte pins and docstrings), `loader.py` (the structural-pass authorization), `invented.py` (two variant notes), `scripts/track_u2_structure.py` and `scripts/track_u2_component_diagnostics.py` (`--authorized-by`), `scripts/track_u2_dry_run.py` and `scripts/run_track_u2_registered.py` (checks and docstrings); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_mappings.py`, `test_u2_roles.py`, `test_u2_scripts.py` and `test_u2_loader.py`; and this section. - Wording after d745: the six `independent_adjudication` notes (`income.json`, `wealth.json`, `pension.json`, `roles.json`, `support.json`, `weights.json`) and `u1_identity.json`; the specification's §19; `sources.py` (six byte pins and three docstring and comment paragraphs rewrapped) and the package docstring; `tests/track_u2/test_u2_mappings.py`; and this section. - Answers to the d745 and 1a–1c reviews: `loader.py` (the committed-record check and the unfrozen-open guard), `u2_source_registry.py` (typed prose fields, 1a's governed role conventions, 1b's absent adjudication), `invented.py` (one variant appended), `rows.py` and `scripts/track_u2_dry_run.py` (docstrings), and `u1_identity.json`; the specification's §19; the master record (the d637 section's introduction); `tests/track_u2/test_adjudication_applied.py`, `test_source_registries.py`, `test_u2_loader.py`, `test_u2_mappings.py`, `test_u2_roles.py` and `test_u2_scripts.py`; and this section. +- Delta-review notes: `tests/track_u2/test_u2_loader.py` and `test_u2_roles.py`; the specification's §19 and `u1_identity.json`; and this section. - Outside the repository: the registry and master-record edit scripts and the review records, copied to `EV/rulings-records-20261011/`. No U1 file, engine file, `gates.yaml` or `runs/*.json` was edited. diff --git a/tests/README-tiers.md b/tests/README-tiers.md index 65e96d8cd..24014f0fc 100644 --- a/tests/README-tiers.md +++ b/tests/README-tiers.md @@ -39,9 +39,9 @@ pytest --collect-only -q -m oracle_policyengine | tail -1 | Tier | Tests at HEAD | |---|---:| -| `unit` | 7,502 | +| `unit` | 7,505 | | `artifact` | 4,201 | | `integration_psid` | 1,401 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **13,932** | +| **Total** | **13,935** | diff --git a/tests/tier_counts.json b/tests/tier_counts.json index 498c37b56..da9adc782 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,7 +1,7 @@ { "schema_version": 1, "counts": { - "unit": 7502, + "unit": 7505, "artifact": 4201, "integration_psid": 1401, "reproduction_legacy": 520, diff --git a/tests/track_u2/test_u2_loader.py b/tests/track_u2/test_u2_loader.py index 959892905..bcbc1e8a8 100644 --- a/tests/track_u2/test_u2_loader.py +++ b/tests/track_u2/test_u2_loader.py @@ -183,10 +183,14 @@ def _load( *, evidence=None, history: pd.DataFrame | None = None, + source_hashes: dict[str, str] | None = None, ) -> cohort.U2Inputs: """Run the real loader on ``root`` with the four named patches (``evidence`` is passed through; ``history`` replaces the invented - marriage history the patched reader returns).""" + marriage history the patched reader returns; ``source_hashes`` is what + the patched cited-source rehash returns, nothing by default).""" + + cited = {} if source_hashes is None else dict(source_hashes) history = inputs.marriage_history if history is None else history @@ -197,7 +201,7 @@ def _load( try: patch.setattr(sources, "blockers", lambda entry, name: []) patch.setattr( - loader, "check_source_hashes", lambda registries, data_dir: {} + loader, "check_source_hashes", lambda registries, data_dir: cited ) patch.setattr( marriage, @@ -878,19 +882,82 @@ def test_a_changed_frame_digest_refuses( _load(tmp_path, u2_inputs, evidence=evidence, history=history) -def test_a_file_read_but_not_frozen_refuses( - tmp_path, u2_inputs, committed_registries, evidence +@pytest.mark.parametrize( + "relative", + [ + # Each reader's own opener: the family file through Path.open, a + # setup file through the layout parser, the individual file + # through pandas (review of the d745 and 1a-1c answers, nit 1). + "family/2017/FAM2017ER.txt", + "family/2013/FAM2013ER.sps", + "ind2023er/IND2023ER.txt", + ], +) +def test_a_file_read_but_not_frozen_refuses_at_open( + tmp_path, u2_inputs, committed_registries, evidence, relative ): + """Review of 630a68c3, finding 1: a file under the PSID directory that + the record does not freeze is refused when it is opened, before any + reader returns its contents.""" + _stage(tmp_path, u2_inputs, committed_registries) files = dict(evidence["psid_files_sha256"]) - files.pop("family/2017/FAM2017ER.txt") + files.pop(relative) narrower = { **evidence, "psid_files_sha256": files, "psid_files_bundle_sha256": loader.files_bundle_sha256(files), } - # Review of 630a68c3, finding 1: the unfrozen file is refused when it - # is opened, before any of its records is parsed. + parsed, layouts, products = [], [], [] + patch = pytest.MonkeyPatch() + family_reader = loader.read_family_records + layout_reader = psid.parse_sps_layout + product_reader = psid.read_psid + patch.setattr( + loader, + "read_family_records", + lambda handle, wave, gate: parsed.append(wave) + or family_reader(handle, wave, gate), + ) + + def layout(path): + frame = layout_reader(path) + layouts.append(Path(path).relative_to(tmp_path).as_posix()) + return frame + + def product(name, *args, **kwargs): + frame = product_reader(name, *args, **kwargs) + products.append(name) + return frame + + patch.setattr(psid, "parse_sps_layout", layout) + patch.setattr(psid, "read_psid", product) + try: + with pytest.raises( + loader.U2FrozenIdentityMismatch, + match=f"{relative} is under the PSID directory but not in the " + "frozen", + ): + _load(tmp_path, u2_inputs, evidence=narrower) + finally: + patch.undo() + if relative == "family/2017/FAM2017ER.txt": + assert parsed == [2013, 2015] + elif relative == "family/2013/FAM2013ER.sps": + assert parsed == [] and relative not in layouts + else: + assert parsed == list(sources.SUPPORT_WAVES) + assert "ind2023er" not in products + + +def test_frozen_cited_sources_must_match( + tmp_path, u2_inputs, committed_registries, evidence +): + """The setup files and codebooks the registries cite must hash as the + evidence froze them; a difference refuses before any family record is + parsed (review of the d745 and 1a-1c answers, nit 2).""" + + _stage(tmp_path, u2_inputs, committed_registries) parsed = [] patch = pytest.MonkeyPatch() original = loader.read_family_records @@ -903,14 +970,17 @@ def test_a_file_read_but_not_frozen_refuses( try: with pytest.raises( loader.U2FrozenIdentityMismatch, - match="family/2017/FAM2017ER.txt is under the PSID directory " - "but not in the frozen", + match="cited setup files and codebooks differ", ): - _load(tmp_path, u2_inputs, evidence=narrower) + _load( + tmp_path, + u2_inputs, + evidence=evidence, + source_hashes={"PSID/family/2019/FAM2019ER.sps": "a" * 64}, + ) finally: patch.undo() - assert 2017 not in parsed - assert parsed == [2013, 2015] + assert parsed == [] def test_a_frozen_load_needs_the_committed_record( diff --git a/tests/track_u2/test_u2_roles.py b/tests/track_u2/test_u2_roles.py index e5d0dcd51..c0c6dfc42 100644 --- a/tests/track_u2/test_u2_roles.py +++ b/tests/track_u2/test_u2_roles.py @@ -407,6 +407,27 @@ def test_a_code_88_target_has_the_ofum_role_and_keeps_its_history( cap = max(0.0, u2_params.ssi.fbr_annual(year, False) - row["ofum_ssi"]) assert out.loc[index, "ssi_offset"] == pytest.approx(min(fall, cap)) assert out.loc[index, "ssi_offset"] > 0 + # Here the fall binds. With the OFUM SSI raised to $500 below the + # individual rate the cap binds instead, so a cap at the couple rate, + # or one that ignored the OFUM SSI, would fail (review of the d745 and + # 1a-1c answers, nit 3). + assert fall < cap + individual = u2_params.ssi.fbr_annual(year, False) + assert u2_params.ssi.fbr_annual(year, True) - individual > fall + raised = members.copy() + raised.loc[index, "ofum_ssi"] = int(individual) - 500 + capped = estimator.u2_adjusted_incomes( + raised, + spec, + context=estimator.U2EstimatorContext(sources.INVENTED_DECLARED), + data_provenance=ap.INVENTED, + life_table=u2_params.life_tables[spec.mortality_basis], + thresholds=u2_params.thresholds, + ssi=u2_params.ssi, + ) + expected = individual - (int(individual) - 500) + assert expected < fall + assert capped.loc[index, "ssi_offset"] == pytest.approx(expected) def test_declared_context_refuses_non_invented_inputs(u2_inputs, declared):