diff --git a/data/external/track_u2/income.json b/data/external/track_u2/income.json index b34cc8ae6..72b1e67a6 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; 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", + "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", @@ -4568,7 +4576,6 @@ "wave": 2015, "route": "wife_age", "variable": "ER60019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2015/FAM2015ER.sps", @@ -4600,13 +4607,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: 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", + "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 +7507,6 @@ "wave": 2015, "route": "wife_sex", "variable": "ER60020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2015/FAM2015ER.sps", @@ -7522,13 +7538,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: 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", + "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", @@ -8922,7 +8948,6 @@ "ER65296", "ER65298" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -8989,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; 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", + "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", @@ -9138,7 +9172,6 @@ "wave": 2017, "route": "wife_age", "variable": "ER66019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2017/FAM2017ER.sps", @@ -9170,13 +9203,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: 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", + "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 +12119,6 @@ "wave": 2017, "route": "wife_sex", "variable": "ER66020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2017/FAM2017ER.sps", @@ -12108,13 +12150,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: 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", + "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", @@ -13500,7 +13552,6 @@ "ER71373", "ER71375" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -13567,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; 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", + "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", @@ -13716,7 +13776,6 @@ "wave": 2019, "route": "wife_age", "variable": "ER72019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2019/FAM2019ER.sps", @@ -13748,13 +13807,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: 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", + "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 +16715,6 @@ "wave": 2019, "route": "wife_sex", "variable": "ER72020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2019/FAM2019ER.sps", @@ -16678,13 +16746,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: 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", + "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", @@ -18070,7 +18148,6 @@ "ER77395", "ER77397" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -18137,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; 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", + "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", @@ -18296,7 +18382,6 @@ "wave": 2021, "route": "wife_age", "variable": "ER78019", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2021/FAM2021ER.sps", @@ -18328,13 +18413,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: 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", + "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 +21391,6 @@ "wave": 2021, "route": "wife_sex", "variable": "ER78020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2021/FAM2021ER.sps", @@ -21333,13 +21427,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: 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", + "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", @@ -22865,7 +22969,6 @@ "ER81722", "ER81724" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -22932,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; 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", + "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", @@ -23091,7 +23203,6 @@ "wave": 2023, "route": "wife_age", "variable": "ER82020", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2023/FAM2023ER.sps", @@ -23123,13 +23234,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: 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", + "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 +26212,6 @@ "wave": 2023, "route": "wife_sex", "variable": "ER82021", - "status": "TO VERIFY", "citations": [ { "file": "PSID/family/2023/FAM2023ER.sps", @@ -26128,13 +26248,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: 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", + "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", @@ -27660,7 +27790,6 @@ "ER85576", "ER85578" ], - "status": "TO VERIFY", "label": "Earlier U1 route represented by later split variables", "citations": [ { @@ -27727,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; 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", + "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": [ @@ -27877,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); 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": [ { @@ -27885,6 +28023,16 @@ "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", + "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; 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/individual.json b/data/external/track_u2/individual.json index 35d579cf7..73bf6d37a 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", @@ -4944,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 4a0d32476..6bf5970cf 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, 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", + "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", @@ -10267,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", @@ -10324,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", @@ -10967,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", @@ -11024,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", @@ -12128,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", @@ -12185,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", @@ -12828,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", @@ -12885,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", @@ -13151,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", @@ -13230,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, 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", + "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", @@ -13292,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", @@ -14400,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", @@ -14457,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", @@ -15100,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", @@ -15157,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", @@ -16253,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", @@ -16310,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", @@ -16945,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", @@ -17002,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", @@ -17268,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", @@ -17347,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, 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", + "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", @@ -17409,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", @@ -18617,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", @@ -18674,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", @@ -19382,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", @@ -19439,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", @@ -20663,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", @@ -20720,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", @@ -21420,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", @@ -21477,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", @@ -21763,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", @@ -21842,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, 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", + "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", @@ -21904,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", @@ -23167,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", @@ -23224,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", @@ -23932,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", @@ -23989,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", @@ -25213,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", @@ -25270,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", @@ -25970,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", @@ -26027,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", @@ -26313,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", @@ -26392,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, 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", + "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", @@ -26454,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", @@ -26685,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); 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": [ { @@ -26693,6 +26544,16 @@ "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", + "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; 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/roles.json b/data/external/track_u2/roles.json index 240dec2e8..c3f3348dc 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", @@ -281,6 +290,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" } }, { @@ -471,7 +489,6 @@ "variable": "ER34303", "present": true, "label": "First-year cohabitor of Head", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -494,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, @@ -515,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", @@ -576,6 +603,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" } }, { @@ -737,7 +774,6 @@ "variable": "ER34503", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -764,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, @@ -785,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", @@ -842,6 +888,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 +955,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" } }, { @@ -1028,7 +1094,6 @@ "variable": "ER34703", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -1055,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, @@ -1076,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", @@ -1353,7 +1428,6 @@ "variable": "ER34903", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -1380,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, @@ -1401,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", @@ -1678,7 +1762,6 @@ "variable": "ER35103", "present": true, "label": "First-year cohabitor of Reference Person", - "status": "TO VERIFY", "citations": [ { "file": "PSID/ind2023er/IND2023ER_formats.sps", @@ -1705,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, @@ -1726,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", @@ -1882,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 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); 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": [ { @@ -1890,6 +1983,17 @@ "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", + "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; 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/support.json b/data/external/track_u2/support.json index ed8ad156a..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 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); 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 c26f34da9..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": "a97b66d1bea9eaf06a105153a411ee2adc301693bfe3332ebf698d07fe0a23bf", + "sha256": "7b1350ff7099405282c50bad27bd90d5d2aad38f07f391d1b459840d56b03269", "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..4a53549ff 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" } }, { @@ -9002,13 +9056,24 @@ ] } }, - "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); 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": [ + { + "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/data/external/track_u2/weights.json b/data/external/track_u2/weights.json index 08b901b0a..1762bbcc2 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, @@ -335,15 +334,42 @@ ], "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).", + "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, "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", @@ -527,6 +553,17 @@ "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); 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": [ + { + "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 6a2b96fc0..4519dd0ed 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. 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. +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, 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. @@ -1837,14 +1843,46 @@ 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-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 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. +- 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 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-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. 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. +- 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. +- 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 **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. +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 b0c8acc87..b0b6e17ff 100644 --- a/docs/design/u2_m1_source_adjudication.md +++ b/docs/design/u2_m1_source_adjudication.md @@ -2,17 +2,17 @@ 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. +**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 | |---|---:|---:|---:| -| income | 748 | 0 | 16 | +| income | 764 | 0 | 0 | | wealth | 234 | 0 | 0 | | individual | 59 | 0 | 0 | -| pension | 647 | 0 | 1 | -| roles | 30 | 0 | 6 | +| pension | 648 | 0 | 0 | +| roles | 36 | 0 | 0 | | support | 16 | 0 | 0 | -| weights | 6 | 0 | 1 | +| weights | 7 | 0 | 0 | | design | 2 | 0 | 0 | | SSI and Census | 26 | 0 | 0 | @@ -25,15 +25,15 @@ 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 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. | -| 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. | +| 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 | 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 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`). | | 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. | @@ -48,12 +48,12 @@ 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` | -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) @@ -66,7 +66,25 @@ 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-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); the 1a, 1b and 1c rows below record their registry commits. + +| 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 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 | +| 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 | +| 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 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 @@ -195,12 +213,12 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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; 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` | 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: 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 | @@ -283,7 +301,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: 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 | @@ -323,12 +341,12 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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; 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` | 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: 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 | @@ -411,7 +429,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: 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 | @@ -451,12 +469,12 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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; 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` | 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: 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 | @@ -539,7 +557,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: 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 | @@ -579,12 +597,12 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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; 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` | 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: 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 | @@ -667,7 +685,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: 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` | @@ -707,12 +725,12 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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; 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` | 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: 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 | @@ -795,7 +813,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: 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` | @@ -835,7 +853,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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; 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 | @@ -855,10 +873,10 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -893,10 +911,10 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -931,10 +949,10 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -970,7 +988,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1010,7 +1028,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1050,7 +1068,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1087,7 +1105,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1252,17 +1270,17 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1275,17 +1293,17 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1310,17 +1328,17 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1333,28 +1351,28 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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, 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. 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` | @@ -1382,8 +1400,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1399,8 +1417,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1428,8 +1446,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1445,20 +1463,20 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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, 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. 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` | @@ -1486,8 +1504,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1503,8 +1521,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1532,8 +1550,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 | @@ -1549,20 +1567,20 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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, 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. 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` | @@ -1590,8 +1608,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1607,8 +1625,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1636,8 +1654,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1653,20 +1671,20 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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, 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. 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` | @@ -1695,8 +1713,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1712,8 +1730,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1741,8 +1759,8 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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` | @@ -1758,20 +1776,20 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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, 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. 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` | @@ -1780,37 +1798,37 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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.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.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.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.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.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.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` | @@ -1831,7 +1849,7 @@ The cohort builder applies the two exclusions (`src/populace_dynamics/uniform_cu | `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 f638bec4c..7509cb5bf 100644 --- a/docs/design/u2_m1b_exposure.md +++ b/docs/design/u2_m1b_exposure.md @@ -371,3 +371,50 @@ 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-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. + +### 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`. +- On 2026-10-12, the decision log's record of d745 (its ruling, date and note), after the coordinating session relayed it. + +### 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. +- 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. +- 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. +- 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. +- 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. +- 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/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 81b1c06b6..7f8f14ba2 100644 --- a/scripts/track_u2_dry_run.py +++ b/scripts/track_u2_dry_run.py @@ -18,10 +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 refuses under every context, 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 @@ -96,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 = ( @@ -559,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"]: @@ -612,6 +617,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 +883,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 + # under either context. + "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( @@ -899,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, @@ -1138,14 +1177,20 @@ 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']}.", + "- 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 7a343d954..3082c199f 100644 --- a/src/populace_dynamics/data/u2_source_registry.py +++ b/src/populace_dynamics/data/u2_source_registry.py @@ -75,6 +75,398 @@ 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) +#: 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 +#: 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), 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"). +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", + "present": True, + "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",), + # 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). + "1b": { + "registry": "roles", + "entries": ("2013.relationship.90",), + "ruling": {**_D637, "amendment": "1b", **_D745}, + "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", + "family_unit_exclusion", + ), + "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( + f"income.{wave}.{route}" + for wave in _LATER_WAVES + 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", + "entries": tuple( + f"income.{wave}.wife_retirement_annuities" + 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", "variable"), + "required": ("resolution", "superseded_question", "variables"), + "markers": ("use_restriction",), + }, + "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"}, + "disposition": "D", + "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"), + "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", + "entries": ("2015.route.formula_unknown_checkpoint",), + "ruling": { + **_D637, + "amendment": "5b", + "replaces_draft_3_lines": [1173, 1174, 1175, 1176], + }, + "disposition": "A", + "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", + ), + "required": ( + "condition", + "resolution", + "superseded_question", + "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", + "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], + }, + "disposition": "D", + "fields": {"status": "RESOLVED"}, + "absent": ("action", "refusal", "question", "blocking_dependencies"), + "required": ( + "required_amendment", + "resolution", + "ruling_effect", + "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", + "entries": ("2017.cross_section_weight",), + "ruling": { + **_D637, + "amendment": "6", + "option": "1", + "amends_draft_3_lines": [134], + }, + "disposition": "A", + "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", + "registration_disclosure", + "superseded_question", + ), + "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). +_AMENDMENT_OF_ENTRY = { + (scope["registry"], entry): name + for name, scope in RATIFIED_AMENDMENTS.items() + for entry in scope["entries"] +} class SourceAdjudicationError(ValueError): @@ -131,6 +523,86 @@ 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: + """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']}" + ) + for field in scope["required"]: + if not entry.get(field): + 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 "adjudication" in entry: + 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])) + ): + 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: + _fields(entry, {"source_wording_conflict": dict}) + 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: @@ -312,6 +784,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, @@ -522,7 +998,59 @@ 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" + ) + # 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: + 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/__init__.py b/src/populace_dynamics/uniform_cut_track_u2/__init__.py index e2e5bdcd7..54857360f 100644 --- a/src/populace_dynamics/uniform_cut_track_u2/__init__.py +++ b/src/populace_dynamics/uniform_cut_track_u2/__init__.py @@ -1,10 +1,15 @@ """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 -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 +(``u2-draft-4``), sections 3-15, with section 16a's amendments as Max +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 @@ -33,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/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 2db11cdb7..8f422c08b 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. @@ -1047,12 +1049,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, @@ -1068,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 @@ -1091,15 +1100,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) @@ -1270,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. """ @@ -1415,6 +1416,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] @@ -1506,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, @@ -1523,12 +1595,20 @@ 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 + ), + "code_88_target_with_ofum_income_2019": ( + _code_88_target_with_ofum_income_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 under either role context" ), "missing_family_record_2019": ( "the first 2019 family-file income record is dropped; an " @@ -1568,9 +1648,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, under either role " + "context" ), "zero_weight_target_in_excluded_unit_2015": ( "the target of U0's first 2015 observation that the B1 exclusion " @@ -1583,6 +1664,20 @@ 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)" + ), + "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 c38fd7754..bc27c5a0c 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,14 +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 - (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. + 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 @@ -37,6 +42,18 @@ (: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, 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 these before the one-shot; execution rechecks their frozen @@ -46,27 +63,37 @@ 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. 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 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 @@ -81,6 +108,7 @@ "ANCHOR_CONCEPTS", "PREREGISTRATION_EVIDENCE_PATH", "PREREGISTRATION_SCHEMA", + "U2AuthorizationRefusal", "U2FrozenIdentityMismatch", "U2LoaderRefusal", "U2UndocumentedValue", @@ -89,6 +117,7 @@ "check_layouts_against_setup", "check_preregistration_evidence", "check_source_hashes", + "check_structural_pass_authorization", "family_record_specs", "files_bundle_sha256", "individual_specs", @@ -99,6 +128,7 @@ "read_preregistration_evidence", "required_entries", "source_preflight", + "structural_pass_authorization", ] #: The individual-file anchors of each support wave (section 3 table). @@ -169,6 +199,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): @@ -186,6 +232,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]]: @@ -440,24 +545,48 @@ 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 - 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`). """ - frozen = ( - None if evidence is None else check_preregistration_evidence(evidence) - ) + if evidence is None: + check_structural_pass_authorization(authorization) + 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. @@ -475,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) @@ -733,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/src/populace_dynamics/uniform_cut_track_u2/sources.py b/src/populace_dynamics/uniform_cut_track_u2/sources.py index b8495f362..3e0b15468 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 @@ -35,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 @@ -57,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 @@ -66,17 +69,21 @@ 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 -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 @@ -84,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 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 @@ -96,8 +105,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 +131,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 +164,7 @@ "check_invented_records", "check_overlaps", "dc_route", + "declared_dc_route", "decode_income", "declared_role_rules", "employer_dc_balances", @@ -223,38 +235,71 @@ #: 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): 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), 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), and +#: amendment 6 the weights and individual bytes (the 2017 weight ER34651 +#: 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. 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) and the pension bytes (amendment 5's texts name the +#: 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. 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, 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. The six +#: adjudication notes then record d745 and the 1c commit (six byte pins). REGISTRY_SHA256: dict[str, str] = { "income": ( - "3d4e8742356ba06986dc7dd3c664ffb1d679498e133fde8801456a76d9ff7431" + "79b8f953eda2111389b762608cea1025fb45d96ceb0468169eaf9ed405b0d4eb" ), "wealth": ( - "3a22247849ea2eccbef6e522c0d74c901beb90195f92fd2e7b23b123fab6b816" + "c5c8fa1ddf283c07ac4dcac4490afb9754d336ef3618a159e028ce2b39f874a1" ), "individual": ( - "4eab8e0abc69668c287310931baa049b8546d3f3e265a106f1b945b948c7649e" + "283c65c46ccfbc6b3a5b700ec7d0d1783e61c931352455b842828cc0186cedda" ), "pension": ( - "0e329d0a871b70800a93908f2927415a728ac020175a079b38664b532b551d9a" + "7cc17ff1f6f4a5896b86cafa5edc59028102d28bfcdfa42840d55b628074f3dd" ), "roles": ( - "df2b7afe76cda4f92380f88b478d9e6866e0f532b0d85f081d8226ee2e33dc87" + "f10c4a7dff38f74a29291fcec4a96c8c7b31d58561798a10c53094d6dcf393b7" ), "support": ( - "5102825d8555e1e8648dfafbd4e6c72eef78d09e453a6d25279ec3b9298d5109" + "279515f5dab832155583829aec312078547a28d7152bcd07e90a2cf9bb863b8a" ), "weights": ( - "356fcbd8dd19f4baf3e34406d75b4a6ae608dc25330d975ecdb9c2d9bb5cf84f" + "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 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. +#: 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", @@ -309,14 +354,38 @@ "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",), +} +#: 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") @@ -603,8 +672,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). """ @@ -751,12 +820,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). @@ -767,10 +830,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] = {} @@ -795,7 +863,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( @@ -947,12 +1015,13 @@ 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, 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. + ``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. The kind is a binding, not a label: construction refuses rules other than the kind's own (the committed registry's or the declared @@ -1123,7 +1192,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") @@ -1136,6 +1210,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, @@ -1390,8 +1472,13 @@ 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 unique person whose role rule has ``spouse_slot``, + and + :func:`.cohort.income_rows` refuses any observation whose flag + disagrees with it. """ out = frame.copy() @@ -1527,13 +1614,14 @@ 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 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. + 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" + items of a combined plan; account items of the wave's declared + previous-plan types), recording the registry blockers. """ wave = int(wave) @@ -1554,16 +1642,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"] @@ -1603,6 +1682,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 c590fff4a..2aa5b04bf 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` | 8,786 | -| `artifact` | 4,080 | -| `integration_psid` | 1,384 | +| `unit` | 8,956 | +| `artifact` | 4,201 | +| `integration_psid` | 1,401 | | `reproduction_legacy` | 520 | | `oracle_policyengine` | 308 | -| **Total** | **15,078** | +| **Total** | **15,386** | 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 158083354..3d5868aa6 100644 --- a/tests/tier_counts.json +++ b/tests/tier_counts.json @@ -1,9 +1,9 @@ { "schema_version": 1, "counts": { - "unit": 8786, - "artifact": 4080, - "integration_psid": 1384, + "unit": 8956, + "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 bb1d4143e..2d2ee3b32 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 @@ -56,18 +57,38 @@ 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 1c, 2, 3, 5 and 6, every one now. EXPECTED_COUNTS = { - "income": (748, 0, 16), + "income": (764, 0, 0), "wealth": (234, 0, 0), "individual": (59, 0, 0), - "pension": (647, 0, 1), - "roles": (30, 0, 6), + "pension": (648, 0, 0), + "roles": (36, 0, 0), "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; 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", + "5b": "A", + "5a": "D", + "6": "A", +} D_ITEMS = { "wealth": { @@ -258,7 +279,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 +438,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): @@ -432,8 +459,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): @@ -451,7 +484,452 @@ 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) + 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( + "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, defers slot + 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] + 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 + # 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 ( + "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. + 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_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 + + +#: The text fields a d637 ruling writes or keeps. +D637_TEXT_FIELDS = ( + "completeness_disclosure", + "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 = { + "1a": ("2f4bca660f85b99cac371c970bb02fe3ae3978e5e52c12d661dd3fdd5690b88d"), + "1b": ("ca251770719feb29812705e2ef7d3868ec6f95a925806e40792f1fda63c69be8"), + "1c": ("23767199b72112d6e686ad7fd99bcbcf77a7225f511ef5257817d509edef87cb"), + "2": ("6f6df518e66bd3588b6bed230c0c21bd6acc07db7586c4b38fa65c91719a9099"), + "3": ("8d93d501819d981c6605b02fde3ff89d05488738146757bd179a84c02e131598"), + "4": ("0b79677a5d1b41a76b17709152bec0d3789d118c75859b9716ea505c3413cd9c"), + "5b": ("ca59c757b3b2b3f9402643c51ae910c16f9dd5927848d03fed327172cb38b513"), + "5a": ("7f2b79ac154d570a4f246f7f985f4aaef47ba40c3506dc0b12f5707ba69da24e"), + "6": ("ad76188063ca16ddcdcc2d8e2165348293a4c88e9437e8e173765df91ffc0d2f"), +} +#: 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).", + ), + ], + "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"), + ], + "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", + "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"), + ], + "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"), + ( + "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", + "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"), + ], + "6": [ + ("resolution", "took amendment 6's option 1 for blocker B3"), + ("resolution", "ER34651 is used as released"), + ( + "resolution", + "no immigrant exclusion, no reconstructed weight and 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 = [ + ( + "roles", + "2019.relationship.88", + "resolution", + "; does not occupy the spouse slot", + "", + ), + ( + "income", + "income.2019.wife_age", + "use_restriction", + ", and a code-88 first-year cohabitor does not occupy the slot", + "", + ), + ( + "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", + ), + ( + "pension", + "2019.route.inherited_route_amendment", + "ruling_effect", + "both_plan_account_items_reasked", + "off_route", + ), + ( + "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, 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) + 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" + ) + weight["registration_disclosure"]["not_published"].pop() + with pytest.raises(AssertionError): + check_d637_texts(changed) def test_corrected_specification_citations(documents): @@ -768,6 +1246,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. @@ -818,11 +1321,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 " @@ -842,7 +1347,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.", ], @@ -985,14 +1491,29 @@ 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 " - "dependents. Until the registry commits for d637 (and, for code 88, " - "d745) land, the 24 TO VERIFY records stay open and the " - "2017\N{EN DASH}2023 P64/P65 records stay blocked.", + "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\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. Max confirmed 1a\N{EN DASH}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; " + "\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 " @@ -1009,9 +1530,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.", ], } @@ -1092,6 +1613,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]] @@ -1111,6 +1641,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). @@ -1164,7 +1716,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.", ), ( @@ -1207,10 +1759,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"), @@ -1277,8 +1828,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 ( @@ -1334,18 +1886,32 @@ 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 - for wave in (2017, 2019, 2021, 2023): + # 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 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): 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) ) @@ -1355,7 +1921,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_pension_registry.py b/tests/track_u2/test_pension_registry.py index 27b7453a7..7fdc9cd2f 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 @@ -104,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 335f68b67..dd28ddfc8 100644 --- a/tests/track_u2/test_source_registries.py +++ b/tests/track_u2/test_source_registries.py @@ -585,3 +585,667 @@ 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" + ), + ), + "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"), + ), + "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"), + ), + "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]), + ), + "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"] + ), + ), + "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" + ), + ), + # 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" + ), + ), + # 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." + ), + ), + # 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] + ), + ), + # 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" + ] + ), + ), + # 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( + 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" + ), + ), + "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" + ), + ), + # 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( + disposition="D" + ), + ), + "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_employer_dc.py b/tests/track_u2/test_u2_employer_dc.py index 24c04b52b..70a7611c7 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,106 @@ 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) + + +@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 + 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 +209,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 +237,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, @@ -182,59 +265,112 @@ 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]) -def test_later_wave_routes_refuse_under_the_registry_gate( - wave, committed_registries +#: The waves whose employer-DC route the registry gate applies: 2013 +#: (U1's route) and, since the d637 5b and 5a commits, every later wave. +REGISTRY_DC_WAVES = sources.SUPPORT_WAVES + + +#: 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 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.{route}" + + def entry(self, name, entry_id): + found = original(self, name, entry_id) + if (name, entry_id) == ("pension", key): + return {**found, **block} + 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=message): sources.dc_route(wave, gate) - with pytest.raises(sources.U2SourceRefusal): - 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, ): - # 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_loader.py b/tests/track_u2/test_u2_loader.py index ccb9c9332..bcbc1e8a8 100644 --- a/tests/track_u2/test_u2_loader.py +++ b/tests/track_u2/test_u2_loader.py @@ -1,23 +1,30 @@ """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 up to five 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``); * 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 @@ -159,16 +166,31 @@ 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, *, 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 @@ -179,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, @@ -191,9 +213,19 @@ 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(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 @@ -850,19 +882,153 @@ 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), } - with pytest.raises(loader.U2LoaderRefusal, match="read but not frozen"): - _load(tmp_path, u2_inputs, evidence=narrower) + 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 + 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="cited setup files and codebooks differ", + ): + _load( + tmp_path, + u2_inputs, + evidence=evidence, + source_hashes={"PSID/family/2019/FAM2019ER.sps": "a" * 64}, + ) + finally: + patch.undo() + assert parsed == [] + + +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 8213337fc..61bc96e10 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 @@ -90,11 +91,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 +124,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 +137,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,35 +173,55 @@ 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): + """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(): - """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 +256,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,10 +295,242 @@ 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 = { + # 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", + "use_restriction", + "resolution", + "ruling", + "superseded_question", + }, + "3": { + "status", + "use_restriction", + "resolution", + "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 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 2017-2023 D items keep their documentary resolution; the + # refusal pending the ruling moves out. + "5a": { + "action", + "refusal", + "ruling_effect", + "ruling", + "superseded_refusal", + }, + "6": { + "status", + "resolution", + "registration_disclosure", + "ruling", + "superseded_question", + }, +} +#: 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) + ), + "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, "individual": 1} +#: 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", "independent_adjudication"}, + "wealth": {"rulings_applied", "independent_adjudication", "status_scope"}, + "pension": {"rulings_applied", "independent_adjudication"}, + "individual": {"rulings_applied"}, + "roles": {"rulings_applied", "independent_adjudication"}, + "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 " + "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." +) +#: The amendments each registry's ``rulings_applied`` d637 record lists. +D637_RECORDED = { + "income": ["2", "3"], + "wealth": ["4"], + "pension": ["5b", "5a"], + "weights": ["6"], + "individual": ["6"], + "roles": ["1a", "1b", "1c"], +} + + +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: + 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: + (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 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 + if "independent_adjudication" in top: + old_meta = before["independent_adjudication"] + new_meta = after["independent_adjudication"] + assert _changed_keys(old_meta, new_meta) == {"note"}, name + # 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( + "is separately refused by", "is decided separately by" + ) + 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 + 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(): - """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"] @@ -460,11 +722,87 @@ def test_wealth1_identity_counts_a_broken_identity(u2_inputs, declared_gate): ) -def test_crosswalks_are_never_executable_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, 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 + + 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 + root = Path(registry.REGISTRY_DIRECTORY).parents[2] + # 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 + 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): 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", @@ -476,30 +814,59 @@ 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. -FIRST_BLOCKER = { - wave: f"income:income.{wave}.wife_age: TO VERIFY" - for wave in (2015, 2017, 2019, 2021, 2023) -} +#: The waves whose family-record specs (income, wealth and employer-DC +#: 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 +#: 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 -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: 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 - 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]) + # U1's); 2015-2023 since the d637 commits for amendments 2, 5b and + # 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) + + +@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" + # 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) + 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) @@ -545,45 +912,127 @@ 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"] - assert "income:income.2019.wife_age" 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( +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) - assert "weights:2017.cross_section_weight" in str(error.value) or ( - "registry blockers" in str(error.value) - ) +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 = [] + 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 refused == [] + for wave in sources.SUPPORT_WAVES: + 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 @@ -742,8 +1191,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"] # --------------------------------------------------------------------------- @@ -776,8 +1224,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 5e98ec846..c0c6dfc42 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 @@ -178,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)} ) @@ -280,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) @@ -292,24 +297,139 @@ 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, 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, under + the declared and the registry role context alike.""" + 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) 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): + 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, cohort.U2CohortSpec(row="U1"), role_context=registry + variant, spec, role_context=sources.RoleContext.declared() ) +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 + # 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): caller = cohort.replace_provenance(u2_inputs, kind="caller_frames") with pytest.raises(cohort.U2CohortError, match="invented"): @@ -715,10 +835,11 @@ 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, under either + role context (review of 07916bb, finding 3).""" changed = invented.invented_variant( u2_inputs, "code_88_in_excluded_unit_2019" @@ -727,8 +848,15 @@ def test_code_88_in_an_excluded_unit_still_refuses(u2_inputs): sources.RoleContext.declared(), sources.RoleContext.from_registry(), ): - with pytest.raises(sources.U2RoleRefusal, match="88"): - cohort.build_u2_cohort(changed, role_context=context) + 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() + ) def test_structure_and_runner_disclose_each_exclusion(u1_cohort, u2_run): @@ -839,6 +967,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] @@ -882,7 +1044,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), @@ -892,7 +1054,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 930c53866..3d4c3dd35 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, @@ -98,8 +99,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-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 @@ -115,24 +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 - assert roles["code_88_under_registry_context"]["refused"] - assert roles["code_88_under_declared_context"]["refused"] + # 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", @@ -161,12 +182,13 @@ 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", + "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 + # 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", @@ -182,6 +204,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(): @@ -256,7 +281,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 @@ -323,35 +355,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 = [ @@ -362,8 +412,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], @@ -371,15 +421,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"] ) @@ -532,13 +752,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) @@ -559,7 +799,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 @@ -570,8 +810,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"]