From a982175bf8569daa839eeb82dda573c9d9a26bf5 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Mon, 25 May 2026 11:11:39 -0400 Subject: [PATCH 1/3] Allocate taxable UC by recipient, not all to head (#8494) `taxable_unemployment_compensation` was assigning the entire tax unit's taxable UI to the head of household and zero to the spouse. The original shortcut preserved the federal AGI sum but broke per-person AGI used by state combined-return logic (KY, AR, DE, IA, MS, MT, WV, MI), inflating or zeroing one spouse's apparent income and shifting state std deductions to the wrong person. Mirror the existing `taxable_social_security` pattern: allocate the tax unit's taxable UC across persons in proportion to each person's `unemployment_compensation`. The federal sum is preserved (shares sum to 1); per-person AGI now reflects the actual recipient. Closes #8494. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../fix-taxable-uc-allocation.fixed.md | 1 + .../taxable_unemployment_compensation.yaml | 84 +++++++++++++++++++ .../taxable_unemployment_insurance.py | 16 ++-- 3 files changed, 94 insertions(+), 7 deletions(-) create mode 100644 changelog.d/fix-taxable-uc-allocation.fixed.md create mode 100644 policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml diff --git a/changelog.d/fix-taxable-uc-allocation.fixed.md b/changelog.d/fix-taxable-uc-allocation.fixed.md new file mode 100644 index 00000000000..f457e2818c6 --- /dev/null +++ b/changelog.d/fix-taxable-uc-allocation.fixed.md @@ -0,0 +1 @@ +Allocate taxable unemployment compensation by actual recipient instead of all to head. diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml new file mode 100644 index 00000000000..964a4f25cc0 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml @@ -0,0 +1,84 @@ +- name: Taxable UC allocated to spouse when only spouse received UC + period: 2025 + input: + people: + head: + age: 75 + unemployment_compensation: 0 + spouse: + age: 40 + unemployment_compensation: 19_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: KY + output: + taxable_unemployment_compensation: [0, 19_000] + +- name: Taxable UC split in proportion to person-level UC + period: 2025 + input: + people: + head: + age: 50 + unemployment_compensation: 5_000 + spouse: + age: 48 + unemployment_compensation: 15_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: CA + output: + taxable_unemployment_compensation: [5_000, 15_000] + +- name: Zero UC produces zero allocation + period: 2025 + input: + people: + head: + age: 35 + employment_income: 50_000 + unemployment_compensation: 0 + tax_units: + tax_unit: + members: [head] + marital_units: + marital_unit: + members: [head] + families: + family: + members: [head] + spm_units: + spm_unit: + members: [head] + households: + household: + members: [head] + state_code: CA + output: + taxable_unemployment_compensation: [0] diff --git a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py index a4a3d049d22..707baf719c0 100644 --- a/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py +++ b/policyengine_us/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_insurance.py @@ -9,13 +9,15 @@ class taxable_unemployment_compensation(Variable): definition_period = YEAR def formula(person, period, parameters): - # The taxable amount of unemployment compensation is decided at the tax unit level, but - # gross income (which contains taxable UI) is person-level. Therefore, we include the - # taxable UI in gross income by assigning it to the head of the tax unit: this will be - # not affect overall tax liability. - - is_tax_unit_head = person("is_tax_unit_head", period) + # The taxable amount of unemployment compensation is decided at the tax + # unit level, but gross income (which contains taxable UI) is person-level. + # Allocate the tax unit's taxable UC to each person in proportion to their + # unemployment_compensation, so that per-person AGI reflects the actual + # recipient (required by states with combined-return optimization). tax_unit_taxable_uc = person.tax_unit( "tax_unit_taxable_unemployment_compensation", period ) - return where(is_tax_unit_head, tax_unit_taxable_uc, 0) + person_uc = person("unemployment_compensation", period) + tax_unit_uc = person.tax_unit.sum(person_uc) + share = where(tax_unit_uc > 0, person_uc / tax_unit_uc, 0) + return tax_unit_taxable_uc * share From 964df606c3828264e1bb58c5a82af0b8d18a65cd Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Mon, 25 May 2026 14:30:57 -0400 Subject: [PATCH 2/3] Add downstream tests for taxable UC allocation Cover the drift surfaces that the variable-level fix corrects: - adjusted_gross_income_person: per-person federal AGI reflects actual UC recipient (spouse-only UC + both-spouses UC scenarios) - ky_agi: per-person KY AGI no longer inflates head with spouse's UC Co-Authored-By: Claude Opus 4.7 (1M context) --- .../adjusted_gross_income_person.yaml | 59 +++++++++++++++++++ .../ky/tax/income/ky_agi_uc_allocation.yaml | 32 ++++++++++ 2 files changed, 91 insertions(+) create mode 100644 policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml index dc8b705e5ac..0ddd082a1be 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml @@ -107,3 +107,62 @@ output: adjusted_gross_income: 300_800 adjusted_gross_income_person: [200_400, 100_400, 0] + +- name: Per-person AGI reflects actual UC recipient when UC is on spouse only + period: 2025 + input: + people: + head: + age: 75 + unemployment_compensation: 0 + spouse: + age: 40 + employment_income: 10_000 + unemployment_compensation: 19_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + output: + adjusted_gross_income_person: [0, 29_000] + +- name: Per-person AGI reflects proportional UC allocation when both spouses have UC + period: 2025 + input: + people: + head: + age: 50 + employment_income: 30_000 + unemployment_compensation: 4_000 + spouse: + age: 48 + employment_income: 20_000 + unemployment_compensation: 12_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + output: + adjusted_gross_income_person: [34_000, 32_000] diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml b/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml new file mode 100644 index 00000000000..08b72308990 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml @@ -0,0 +1,32 @@ +- name: KY per-person AGI reflects UC recipient (regression test for #8494) + period: 2025 + input: + people: + head: + age: 75 + unemployment_compensation: 0 + spouse: + age: 40 + employment_income: 10_000 + unemployment_compensation: 19_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + state_code: KY + output: + # Before the fix, head's KY AGI was inflated by the spouse's UC ($19,000) + # and the spouse's KY AGI was deflated by the same amount. KY's combined- + # return optimization then gave the head an unearned standard deduction. + ky_agi: [0, 29_000] From 86f5d87f9194d2fb9c28c5021310d52c99422ec6 Mon Sep 17 00:00:00 2001 From: PavelMakarchuk Date: Thu, 28 May 2026 23:15:15 -0400 Subject: [PATCH 3/3] Move UC allocation tests to variables --- .../adjusted_gross_income_person.yaml | 59 ------------------- .../adjusted_gross_income_person.yaml | 58 ++++++++++++++++++ .../taxable_unemployment_compensation.yaml | 0 .../gov/states/ky/tax/income/ky_agi.yaml} | 0 4 files changed, 58 insertions(+), 59 deletions(-) create mode 100644 policyengine_us/tests/variables/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml rename policyengine_us/tests/{policy/baseline => variables}/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml (100%) rename policyengine_us/tests/{policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml => variables/gov/states/ky/tax/income/ky_agi.yaml} (100%) diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml index 0ddd082a1be..dc8b705e5ac 100644 --- a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml @@ -107,62 +107,3 @@ output: adjusted_gross_income: 300_800 adjusted_gross_income_person: [200_400, 100_400, 0] - -- name: Per-person AGI reflects actual UC recipient when UC is on spouse only - period: 2025 - input: - people: - head: - age: 75 - unemployment_compensation: 0 - spouse: - age: 40 - employment_income: 10_000 - unemployment_compensation: 19_000 - tax_units: - tax_unit: - members: [head, spouse] - marital_units: - marital_unit: - members: [head, spouse] - families: - family: - members: [head, spouse] - spm_units: - spm_unit: - members: [head, spouse] - households: - household: - members: [head, spouse] - output: - adjusted_gross_income_person: [0, 29_000] - -- name: Per-person AGI reflects proportional UC allocation when both spouses have UC - period: 2025 - input: - people: - head: - age: 50 - employment_income: 30_000 - unemployment_compensation: 4_000 - spouse: - age: 48 - employment_income: 20_000 - unemployment_compensation: 12_000 - tax_units: - tax_unit: - members: [head, spouse] - marital_units: - marital_unit: - members: [head, spouse] - families: - family: - members: [head, spouse] - spm_units: - spm_unit: - members: [head, spouse] - households: - household: - members: [head, spouse] - output: - adjusted_gross_income_person: [34_000, 32_000] diff --git a/policyengine_us/tests/variables/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml b/policyengine_us/tests/variables/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml new file mode 100644 index 00000000000..43b72665add --- /dev/null +++ b/policyengine_us/tests/variables/gov/irs/income/taxable_income/adjusted_gross_income/adjusted_gross_income_person.yaml @@ -0,0 +1,58 @@ +- name: Per-person AGI reflects actual UC recipient when UC is on spouse only + period: 2025 + input: + people: + head: + age: 75 + unemployment_compensation: 0 + spouse: + age: 40 + employment_income: 10_000 + unemployment_compensation: 19_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + output: + adjusted_gross_income_person: [0, 29_000] + +- name: Per-person AGI reflects proportional UC allocation when both spouses have UC + period: 2025 + input: + people: + head: + age: 50 + employment_income: 30_000 + unemployment_compensation: 4_000 + spouse: + age: 48 + employment_income: 20_000 + unemployment_compensation: 12_000 + tax_units: + tax_unit: + members: [head, spouse] + marital_units: + marital_unit: + members: [head, spouse] + families: + family: + members: [head, spouse] + spm_units: + spm_unit: + members: [head, spouse] + households: + household: + members: [head, spouse] + output: + adjusted_gross_income_person: [34_000, 32_000] diff --git a/policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml b/policyengine_us/tests/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml similarity index 100% rename from policyengine_us/tests/policy/baseline/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml rename to policyengine_us/tests/variables/gov/irs/income/taxable_income/adjusted_gross_income/irs_gross_income/unemployment_insurance/taxable_unemployment_compensation.yaml diff --git a/policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml b/policyengine_us/tests/variables/gov/states/ky/tax/income/ky_agi.yaml similarity index 100% rename from policyengine_us/tests/policy/baseline/gov/states/ky/tax/income/ky_agi_uc_allocation.yaml rename to policyengine_us/tests/variables/gov/states/ky/tax/income/ky_agi.yaml