From d14b38665a6bd5e8d37adcdaf3630f28443aedf5 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 20:01:21 -0400 Subject: [PATCH 1/8] Leave dependents' income out of Wisconsin homestead credit household income A Wisconsin homestead household is the claimant and the claimant's spouse (Wis. Stat. 71.52(4)); Schedule H tells claimants to leave out children's social security and SSI. wi_homestead_income summed the nontaxable sources over every tax unit member, and wi_homestead_eligible counted a dependent's earnings toward the earned income test. Sum only the head and spouse with tax_unit_non_dep_add. Co-Authored-By: Claude Opus 5.5 --- changelog.d/wi-homestead-dependent-income.fixed.md | 1 + .../credits/homestead/wi_homestead_eligible.py | 12 ++++++++++-- .../income/credits/homestead/wi_homestead_income.py | 11 ++++++++++- 3 files changed, 21 insertions(+), 3 deletions(-) create mode 100644 changelog.d/wi-homestead-dependent-income.fixed.md diff --git a/changelog.d/wi-homestead-dependent-income.fixed.md b/changelog.d/wi-homestead-dependent-income.fixed.md new file mode 100644 index 00000000000..d731408c7e5 --- /dev/null +++ b/changelog.d/wi-homestead-dependent-income.fixed.md @@ -0,0 +1 @@ +Leave a tax unit dependent's income, such as a child's Social Security, SSI or unemployment compensation, out of Wisconsin homestead credit household income, and count only the claimant's and spouse's earnings toward its earned income test. diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py index c616d3e4e0e..acc8efe9012 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py @@ -6,7 +6,12 @@ class wi_homestead_eligible(Variable): entity = TaxUnit label = "Wisconsin homestead credit eligibility status" definition_period = YEAR - reference = "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7" + reference = ( + "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7", + # PDF pages 4, 10: the claimant or the claimant's spouse must have + # earned income. + "https://www.revenue.wi.gov/TaxForms2024/2024-ScheduleH-inst.pdf#page=4", + ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): @@ -17,7 +22,10 @@ def formula(tax_unit, period, parameters): min_age = p.homestead.eligible.min_age age_eligible = (head_age >= min_age) | (spouse_age >= min_age) # earnings eligibility - earnings = add(tax_unit, period, p.homestead.eligible.earnings_sources) + # Only the claimant's or spouse's earnings qualify, not a dependent's. + earnings = tax_unit_non_dep_add( + tax_unit, period, p.homestead.eligible.earnings_sources + ) has_positive_earnings = earnings > 0 min_elderly_age = p.homestead.eligible.min_elderly_age head_is_elderly = head_age >= min_elderly_age diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py index 7ab07ce19be..ec04afdb076 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py @@ -11,12 +11,21 @@ class wi_homestead_income(Variable): "https://www.revenue.wi.gov/TaxForms2021/2021-ScheduleH.pdf", "https://www.revenue.wi.gov/TaxForms2022/2022-ScheduleH.pdf", "https://www.revenue.wi.gov/TaxForms2023/2023-ScheduleH.pdf#page=4", + # Wis. Stat. 71.52(4)-(6): household, household income and income + "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/52", + # PDF pages 8, 13: the household is the claimant and spouse; leave + # out children's social security and SSI. + "https://www.revenue.wi.gov/TaxForms2024/2024-ScheduleH-inst.pdf#page=8", ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wi.tax.income.credits - income = add(tax_unit, period, p.homestead.income.sources) + # A household is the claimant and the claimant's spouse (Wis. Stat. + # 71.52(4)), so household income leaves out a tax unit dependent's + # income, such as a child's social security, SSI or unemployment + # compensation. + income = tax_unit_non_dep_add(tax_unit, period, p.homestead.income.sources) disqualified_losses = tax_unit("wi_homestead_disqualified_losses", period) dependents = tax_unit("tax_unit_dependents", period) return income + disqualified_losses - dependents * p.homestead.income.exemption From 4c0b18e1f100cb166c236f4a7059d54ff1faf995 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 20:08:12 -0400 Subject: [PATCH 2/8] Use the filers' tax-exempt social security in WI homestead income; add tests tax_exempt_social_security is a tax-unit amount that includes dependents' benefits, so the homestead list now takes filer_tax_exempt_social_security. New cases from the 2024 Schedule H instructions: a grandchild's survivor benefits (Case 6, credit Case 5), a child's SSI and 2020 excluded UC (Case 7), and the earned income test (eligibility Cases 8-9). Four fail on main. Co-Authored-By: Claude Opus 5.5 --- .../credits/homestead/income/sources.yaml | 4 +- .../wi/tax/income/wi_homestead_credit.yaml | 32 ++++++++++ .../wi/tax/income/wi_homestead_eligible.yaml | 45 ++++++++++++++ .../wi/tax/income/wi_homestead_income.yaml | 60 +++++++++++++++++++ 4 files changed, 140 insertions(+), 1 deletion(-) diff --git a/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml b/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml index 879c387a65f..12f48eb0ef2 100644 --- a/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wi/tax/income/credits/homestead/income/sources.yaml @@ -12,7 +12,9 @@ values: - adjusted_gross_income # additional homestead income sources: - tax_exempt_interest_income - - tax_exempt_social_security + # The household is the claimant and spouse (Wis. Stat. 71.52(4)), so + # this leaves out a dependent's benefits, unlike tax_exempt_social_security. + - filer_tax_exempt_social_security - tax_exempt_pension_income - tax_exempt_unemployment_compensation # cash transfer income sources: diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_credit.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_credit.yaml index 92fc3545c55..dab9984d57f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_credit.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_credit.yaml @@ -41,3 +41,35 @@ state_code: WI output: wi_homestead_credit: 0 + +- name: Case 5, a dependent grandchild's survivor benefits do not reduce the claimant's credit. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + social_security_retirement: 18_000 + taxable_pension_income: 6_000 + rent: 9_000 + person2: + age: 17 + is_tax_unit_dependent: true + social_security_survivors: 12_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + # Household income 23,500 (6,000 + 18,000 - 500), without the + # grandchild's 12,000; it is under the 24,680 limit. + wi_homestead_income: 23_500 + wi_homestead_eligible: true + # Rent with heat included: 9,000 x 20% = 1,800, capped at 1,460. + wi_homestead_property_tax: 1_800 + # (1,460 - (23,500 - 8,060) x 8.785%) x 80% = (1,460 - 1,356.40) x 80%. + # Schedule H's Tables A and B round to bands and give 84. + wi_homestead_credit: 82.88 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index 4802db8710f..e6320b98df4 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -95,3 +95,48 @@ state_code: WI output: wi_homestead_eligible: false + +# The claimant or the claimant's spouse must have earned income (2024 +# Schedule H instructions, page 10); unemployment compensation is not earned +# income (page 7), and a dependent's wages do not qualify the claimant. + +- name: Case 8, a dependent's wages do not meet the claimant's earned income test. + period: 2024 + input: + people: + person1: + age: 40 + unemployment_compensation: 6_000 + person2: + age: 17 + is_tax_unit_dependent: true + employment_income: 4_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_eligible: false + +- name: Case 9, the claimant's own wages meet the earned income test. + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 4_000 + person2: + age: 17 + is_tax_unit_dependent: true + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 2cea0b82040..e2ec507d27b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -99,3 +99,63 @@ # loss_ald deducts the loss of total_self_employment_income (0 here), so # the add-back follows that total and household income is the same. wi_homestead_income: 5_000 + +# A household is the claimant and the claimant's spouse (Wis. Stat. 71.52(4); +# 2024 Schedule H instructions, page 8), and the instructions say to leave out +# children's social security and SSI (page 13), so a dependent's income is not +# household income. The $500 deduction for each dependent still applies. + +- name: Case 6, a dependent grandchild's survivor benefits are not household income. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + social_security_retirement: 18_000 + taxable_pension_income: 6_000 + person2: + age: 17 + is_tax_unit_dependent: true + social_security_survivors: 12_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + # No social security is taxable: 6,000 + 9,000 is under the 25,000 base. + adjusted_gross_income: 6_000 + # Schedule H: 6,000 pension + 18,000 social security - 500 for one + # dependent = 23,500. The grandchild's 12,000 is left out. + wi_homestead_income: 23_500 + +- name: Case 7, a dependent's SSI and 2020 excluded unemployment compensation are not household income. + period: 2020 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 66 + social_security_retirement: 15_000 + taxable_pension_income: 5_000 + person2: + age: 17 + is_tax_unit_dependent: true + unemployment_compensation: 8_000 + ssi: 3_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + # The child's 8,000 is under the 10,200 exclusion on the child's own + # federal return. + tax_exempt_unemployment_compensation: [0, 8_000] + # 5,000 pension + 15,000 social security - 500 = 19,500. + wi_homestead_income: 19_500 From a576e21681ec581a7addd5eb5c9769ed9354c766 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 22:30:44 -0400 Subject: [PATCH 3/8] Address review: filer-only capital loss, rent and taxes; per-claimant eligibility Wis. Stat. 71.52(4) limits the household to the claimant and spouse: - wi_homestead_disqualified_losses adds back filer_loss_limited_net_capital_gains, so a dependent's gains and losses no longer move household income. - wi_homestead_property_tax counts only the head's and spouse's property taxes (71.52(7)) and rent (71.52(8)). - wi_homestead_eligible tests each of the head and spouse as the claimant: 18 or older, and 62 or older, disabled or with their own earned income (71.52(1g); 2024 Schedule H instructions, pages 4 and 10). Summed earnings let a spouse's self-employment loss cancel the other's wages. Co-Authored-By: Claude Opus 5.5 --- .../wi_homestead_disqualified_losses.yaml | 4 +- .../wi/tax/income/wi_homestead_eligible.yaml | 43 ++++++++++++ .../wi/tax/income/wi_homestead_income.yaml | 65 +++++++++++++++++++ .../tax/income/wi_homestead_property_tax.yaml | 54 +++++++++++++++ .../wi_homestead_disqualified_losses.py | 8 ++- .../homestead/wi_homestead_eligible.py | 42 ++++++------ .../homestead/wi_homestead_property_tax.py | 10 ++- 7 files changed, 200 insertions(+), 26 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml index 3b66f547433..c9da217d705 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_disqualified_losses.yaml @@ -12,7 +12,7 @@ tax_units: tax_unit: members: [head] - loss_limited_net_capital_gains: -3_000 # line 3 + filer_loss_limited_net_capital_gains: -3_000 # line 3 households: household: members: [head] @@ -156,7 +156,7 @@ tax_units: tax_unit: members: [head, child] - loss_limited_net_capital_gains: 0 + filer_loss_limited_net_capital_gains: 0 households: household: members: [head, child] diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index e6320b98df4..6afcc6f3a65 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -140,3 +140,46 @@ state_code: WI output: wi_homestead_eligible: true + +# Either spouse can be the claimant, who must be 18 or older and 62 or older, +# disabled or have their own earned income (2024 Schedule H instructions, +# pages 4 and 10; Wis. Stat. 71.52(1g)). + +- name: Case 10, one spouse's wages qualify even when the other has a larger self-employment loss. + period: 2024 + input: + people: + person1: + age: 40 + employment_income: 5_000 + person2: + age: 40 + self_employment_income: -8_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_eligible: true + +- name: Case 11, the earner must also be 18 or older. + period: 2024 + input: + people: + person1: + age: 17 + employment_income: 6_000 + person2: + age: 19 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_eligible: false diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index e2ec507d27b..9c2a88ea019 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -159,3 +159,68 @@ tax_exempt_unemployment_compensation: [0, 8_000] # 5,000 pension + 15,000 social security - 500 = 19,500. wi_homestead_income: 19_500 + +- name: Case 8, a dependent's capital loss is not added back to household income. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + social_security_retirement: 12_000 + taxable_pension_income: 6_000 + person2: + age: 17 + is_tax_unit_dependent: true + long_term_capital_gains: -2_000 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + # No W-2 payments, which would count too (Tax 14.03(4)(b)3.b). + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # The child's loss is on the child's own return, so AGI stays 6,000 and + # Schedule 4 line 3 adds nothing back: 6,000 + 12,000 - 500 = 17,500. + adjusted_gross_income: 6_000 + wi_homestead_disqualified_losses: 0 + wi_homestead_income: 17_500 + +- name: Case 9, a dependent's capital gain does not shrink the claimant's loss add-back. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + social_security_retirement: 12_000 + taxable_pension_income: 6_000 + long_term_capital_gains: -3_000 + person2: + age: 17 + is_tax_unit_dependent: true + long_term_capital_gains: 2_000 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + # No W-2 payments, which would count too (Tax 14.03(4)(b)3.b). + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # AGI = 6,000 - 3,000 claimant's allowed loss = 3,000; the 3,000 loss is + # added back: 3,000 + 3,000 + 12,000 - 500 = 17,500. + adjusted_gross_income: 3_000 + wi_homestead_disqualified_losses: 3_000 + wi_homestead_income: 17_500 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_property_tax.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_property_tax.yaml index b2c684b1e20..73a9f2ade91 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_property_tax.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_property_tax.yaml @@ -7,3 +7,57 @@ state_code: WI output: wi_homestead_property_tax: 360 # = 200 + 800 * 0.20 + +# Property taxes count on a homestead the claimant or a household member owns +# (Wis. Stat. 71.52(7)), and rent the claimant and household pay (71.52(8)). +# The household is the claimant and spouse (71.52(4)). + +- name: Case 2, property taxes a dependent pays do not count. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 30 + employment_income: 12_000 + person2: + age: 70 + is_tax_unit_dependent: true + social_security_retirement: 26_000 + real_estate_taxes: 2_000 + tax_units: + tax_unit: + members: [person1, person2] + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_property_tax: 0 + wi_homestead_credit: 0 + +- name: Case 3, the claimant's rent and the spouse's property taxes count; a dependent's rent does not. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 45 + rent: 6_000 + person2: + age: 44 + real_estate_taxes: 300 + person3: + age: 17 + is_tax_unit_dependent: true + rent: 1_000 + tax_units: + tax_unit: + members: [person1, person2, person3] + households: + household: + members: [person1, person2, person3] + state_code: WI + output: + # 300 + 6,000 x 20% = 1,500. + wi_homestead_property_tax: 1_500 diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_disqualified_losses.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_disqualified_losses.py index eba85c08e25..a9ddfac9ff3 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_disqualified_losses.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_disqualified_losses.py @@ -39,8 +39,12 @@ def formula(tax_unit, period, parameters): # (Form 4797), a tax-unit amount. other_net_gain does not separate # the losses from involuntary conversions that line 9 leaves out. business_property_loss = max_(0, -tax_unit("other_net_gain", period)) - # Schedule 4, line 3: net capital loss included in income. - capital_loss = max_(0, -tax_unit("loss_limited_net_capital_gains", period)) + # Schedule 4, line 3: net capital loss included in income. Only the + # claimant's and spouse's: a dependent's gains and losses are on the + # dependent's own return and outside the household (71.52(4)). + capital_loss = max_( + 0, -tax_unit("filer_loss_limited_net_capital_gains", period) + ) return ( tax_unit.sum(person_losses * head_or_spouse) + business_property_loss diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py index acc8efe9012..4aa0ffd5594 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py @@ -8,35 +8,37 @@ class wi_homestead_eligible(Variable): definition_period = YEAR reference = ( "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7", - # PDF pages 4, 10: the claimant or the claimant's spouse must have - # earned income. + # PDF pages 4, 10: the claimant must be 18 or older and 62 or older, + # disabled or have earned income; either spouse may be the claimant. "https://www.revenue.wi.gov/TaxForms2024/2024-ScheduleH-inst.pdf#page=4", ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): p = parameters(period).gov.states.wi.tax.income.credits - # minimum age eligibility + # Either the head or the spouse can be the claimant, who must meet + # every test alone: 18 or older, and 62 or older, disabled or with + # their own earned income (Wis. Stat. 71.52(1g) computes earned + # income per claimant). A dependent's earnings do not qualify anyone. + min_age = p.homestead.eligible.min_age + min_elderly_age = p.homestead.eligible.min_elderly_age + person = tax_unit.members + earnings = add(person, period, p.homestead.eligible.earnings_sources) + head_earnings = tax_unit.sum(earnings * person("is_tax_unit_head", period)) + spouse_earnings = tax_unit.sum(earnings * person("is_tax_unit_spouse", period)) head_age = tax_unit("age_head", period) spouse_age = tax_unit("age_spouse", period) - min_age = p.homestead.eligible.min_age - age_eligible = (head_age >= min_age) | (spouse_age >= min_age) - # earnings eligibility - # Only the claimant's or spouse's earnings qualify, not a dependent's. - earnings = tax_unit_non_dep_add( - tax_unit, period, p.homestead.eligible.earnings_sources + head_qualifies = (head_age >= min_age) & ( + (head_age >= min_elderly_age) + | tax_unit("head_is_disabled", period) + | (head_earnings > 0) + ) + spouse_qualifies = (spouse_age >= min_age) & ( + (spouse_age >= min_elderly_age) + | tax_unit("spouse_is_disabled", period) + | (spouse_earnings > 0) ) - has_positive_earnings = earnings > 0 - min_elderly_age = p.homestead.eligible.min_elderly_age - head_is_elderly = head_age >= min_elderly_age - spouse_is_elderly = spouse_age >= min_elderly_age - is_elderly = head_is_elderly | spouse_is_elderly - head_is_disabled = tax_unit("head_is_disabled", period) - spouse_is_disabled = tax_unit("spouse_is_disabled", period) - is_disabled = head_is_disabled | spouse_is_disabled - earnings_eligible = has_positive_earnings | is_elderly | is_disabled # income eligibility homestead_income = tax_unit("wi_homestead_income", period) income_eligible = homestead_income < p.homestead.eligible.max_income - # overall eligibility - return age_eligible & earnings_eligible & income_eligible + return (head_qualifies | spouse_qualifies) & income_eligible diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py index f5d30fed3b6..830271002c8 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py @@ -10,12 +10,18 @@ class wi_homestead_property_tax(Variable): reference = ( "https://www.revenue.wi.gov/TaxForms2021/2021-ScheduleH.pdf#page=2", "https://www.revenue.wi.gov/TaxForms2022/2022-ScheduleH.pdf#page=2", + # Wis. Stat. 71.52(4), (7) and (8) + "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/52", ) defined_for = StateCode.WI def formula(tax_unit, period, parameters): - ptax_owner = add(tax_unit, period, ["real_estate_taxes"]) - rent = add(tax_unit, period, ["rent"]) + # Property taxes on a homestead the claimant or a household member + # owns (71.52(7)) and rent the claimant and household pay (71.52(8)). + # The household is the claimant and spouse (71.52(4)), so a + # dependent's property taxes and rent do not count. + ptax_owner = tax_unit_non_dep_add(tax_unit, period, ["real_estate_taxes"]) + rent = tax_unit_non_dep_add(tax_unit, period, ["rent"]) p = parameters(period).gov.states.wi.tax.income.credits ptax_renter = rent * p.homestead.property_tax.rent_ratio return ptax_owner + ptax_renter From 509e5c9d71443b27fd4cbb39aa3009efe2626e2a Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Mon, 12 Oct 2026 22:49:01 -0400 Subject: [PATCH 4/8] Give eligibility Case 11 explicit roles; widen the changelog Co-Authored-By: Claude Opus 5.5 --- changelog.d/wi-homestead-dependent-income.fixed.md | 2 +- .../gov/states/wi/tax/income/wi_homestead_eligible.yaml | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/changelog.d/wi-homestead-dependent-income.fixed.md b/changelog.d/wi-homestead-dependent-income.fixed.md index d731408c7e5..afbad3e14fc 100644 --- a/changelog.d/wi-homestead-dependent-income.fixed.md +++ b/changelog.d/wi-homestead-dependent-income.fixed.md @@ -1 +1 @@ -Leave a tax unit dependent's income, such as a child's Social Security, SSI or unemployment compensation, out of Wisconsin homestead credit household income, and count only the claimant's and spouse's earnings toward its earned income test. +Leave a tax unit dependent's income, such as a child's Social Security, SSI, unemployment compensation or capital gains, and a dependent's rent and property taxes out of the Wisconsin homestead credit, and test each spouse separately as the claimant for its age and earned income requirements. diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index 6afcc6f3a65..aa411873be3 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -172,8 +172,10 @@ person1: age: 17 employment_income: 6_000 + is_tax_unit_head: true person2: age: 19 + is_tax_unit_spouse: true tax_units: tax_unit: members: [person1, person2] From 778923f404eb64110dd5d5d76b286d76f55da9ea Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 20:45:19 -0400 Subject: [PATCH 5/8] Test the spouse side of Wisconsin's homestead claimant rules --- .../wi/tax/income/wi_homestead_eligible.yaml | 118 ++++++++++++++++++ .../wi/tax/income/wi_homestead_income.yaml | 66 ++++++++++ 2 files changed, 184 insertions(+) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index aa411873be3..0fd7744319a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -185,3 +185,121 @@ state_code: WI output: wi_homestead_eligible: false + +# Only one spouse needs earned income, and that spouse must be the claimant +# (2024 Schedule H instructions, page 10). Either tax-unit role can qualify. + +- name: Case 12, only the spouse earns and can be the claimant. + period: 2024 + input: + people: + person1: + age: 40 + is_tax_unit_head: true + ssi: 0 + person2: + age: 38 + employment_income: 6_000 + is_tax_unit_spouse: true + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_income: 6_000 + wi_homestead_eligible: true + +- name: Case 13, the same earning spouse can be labeled as the head. + period: 2024 + input: + people: + person1: + age: 40 + is_tax_unit_spouse: true + ssi: 0 + person2: + age: 38 + employment_income: 6_000 + is_tax_unit_head: true + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_income: 6_000 + wi_homestead_eligible: true + +# The person meeting the age-62 condition can be the claimant (2024 +# Schedule H instructions, page 4), whichever tax-unit role they hold. + +- name: Case 14, only the spouse is 65 and can be the claimant. + period: 2024 + input: + people: + person1: + age: 50 + is_tax_unit_head: true + ssi: 0 + person2: + age: 65 + social_security_retirement: 9_000 + is_tax_unit_spouse: true + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_income: 9_000 + wi_homestead_eligible: true + +- name: Case 15, the same older spouse can be labeled as the head. + period: 2024 + input: + people: + person1: + age: 50 + is_tax_unit_spouse: true + ssi: 0 + person2: + age: 65 + social_security_retirement: 9_000 + is_tax_unit_head: true + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + wi_homestead_income: 9_000 + wi_homestead_eligible: true diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 9c2a88ea019..0e1d2e17617 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -224,3 +224,69 @@ adjusted_gross_income: 3_000 wi_homestead_disqualified_losses: 3_000 wi_homestead_income: 17_500 + +# Married claimants include both spouses' incomes (2024 Schedule H, page 1). + +- name: Case 10, the spouse's Social Security and SSI are household income. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_head: true + taxable_pension_income: 6_000 + social_security_retirement: 6_000 + ssi: 0 + person2: + age: 64 + is_tax_unit_spouse: true + social_security_retirement: 4_000 + ssi: 1_000 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # No Social Security is taxable: 6,000 + 5,000 is below the joint + # 32,000 base. Household income = 6,000 + 10,000 + 1,000 = 17,000. + adjusted_gross_income: 6_000 + wi_homestead_income: 17_000 + +- name: Case 11, spouses' household income is unchanged when their roles are swapped. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_spouse: true + taxable_pension_income: 6_000 + social_security_retirement: 6_000 + ssi: 0 + person2: + age: 64 + is_tax_unit_head: true + social_security_retirement: 4_000 + ssi: 1_000 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + adjusted_gross_income: 6_000 + wi_homestead_income: 17_000 From 3687df6de8ea4a3fa2b122c9ac42c41ac69cc70c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 20:45:26 -0400 Subject: [PATCH 6/8] Cite Wisconsin homestead claimant eligibility statutes --- .../gov/states/wi/tax/income/wi_homestead_eligible.yaml | 8 ++++---- .../tax/income/credits/homestead/wi_homestead_eligible.py | 4 ++++ 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml index 0fd7744319a..2e3bdd5188b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml @@ -96,9 +96,9 @@ output: wi_homestead_eligible: false -# The claimant or the claimant's spouse must have earned income (2024 -# Schedule H instructions, page 10); unemployment compensation is not earned -# income (page 7), and a dependent's wages do not qualify the claimant. +# The claimant must have their own earned income; either spouse may be the +# claimant (2024 Schedule H instructions, page 10). Unemployment compensation +# is not earned income (page 7), and a dependent's wages do not qualify them. - name: Case 8, a dependent's wages do not meet the claimant's earned income test. period: 2024 @@ -143,7 +143,7 @@ # Either spouse can be the claimant, who must be 18 or older and 62 or older, # disabled or have their own earned income (2024 Schedule H instructions, -# pages 4 and 10; Wis. Stat. 71.52(1g)). +# pages 4 and 10; Wis. Stat. 71.53(2)(c), 71.54(1)(dm)-(gm)). - name: Case 10, one spouse's wages qualify even when the other has a larger self-employment loss. period: 2024 diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py index 4aa0ffd5594..22d43f65af7 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py @@ -8,6 +8,10 @@ class wi_homestead_eligible(Variable): definition_period = YEAR reference = ( "https://docs.legis.wisconsin.gov/misc/lfb/informational_papers/january_2021/0013_homestead_tax_credit_informational_paper_13.pdf#page=7", + # Minimum age (71.53(2)(c)) and claimant qualification paths + # (71.54(1)(dm)-(gm)). + "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/53/2/c", + "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/54", # PDF pages 4, 10: the claimant must be 18 or older and 62 or older, # disabled or have earned income; either spouse may be the claimant. "https://www.revenue.wi.gov/TaxForms2024/2024-ScheduleH-inst.pdf#page=4", From b4ec08c342ccf2f6d7a5373d5797e1262def290c Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 20:45:33 -0400 Subject: [PATCH 7/8] Document separate homestead claims for elderly dependents --- .../tax/income/credits/homestead/wi_homestead_property_tax.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py index 830271002c8..9b9c2ae8faa 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_property_tax.py @@ -12,6 +12,7 @@ class wi_homestead_property_tax(Variable): "https://www.revenue.wi.gov/TaxForms2022/2022-ScheduleH.pdf#page=2", # Wis. Stat. 71.52(4), (7) and (8) "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/52", + "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/53/2/d", ) defined_for = StateCode.WI @@ -20,6 +21,9 @@ def formula(tax_unit, period, parameters): # owns (71.52(7)) and rent the claimant and household pay (71.52(8)). # The household is the claimant and spouse (71.52(4)), so a # dependent's property taxes and rent do not count. + # A dependent age 62 or older may file a separate claim under + # 71.53(2)(d). This tax-unit model does not represent that separate + # claim, so their taxes and rent are not credited to anyone here. ptax_owner = tax_unit_non_dep_add(tax_unit, period, ["real_estate_taxes"]) rent = tax_unit_non_dep_add(tax_unit, period, ["rent"]) p = parameters(period).gov.states.wi.tax.income.credits From 4f5e4a6ff16cdf9b61148604602cafd6fa26f098 Mon Sep 17 00:00:00 2001 From: Max Ghenis Date: Sun, 18 Oct 2026 20:52:46 -0400 Subject: [PATCH 8/8] Preserve qualifying Wisconsin homestead Social Security repayments --- .../wi-homestead-dependent-income.fixed.md | 2 +- .../wi/tax/income/wi_homestead_income.yaml | 161 ++++++++++++++++++ .../credits/homestead/wi_homestead_income.py | 17 +- 3 files changed, 176 insertions(+), 4 deletions(-) diff --git a/changelog.d/wi-homestead-dependent-income.fixed.md b/changelog.d/wi-homestead-dependent-income.fixed.md index afbad3e14fc..6946751a93a 100644 --- a/changelog.d/wi-homestead-dependent-income.fixed.md +++ b/changelog.d/wi-homestead-dependent-income.fixed.md @@ -1 +1 @@ -Leave a tax unit dependent's income, such as a child's Social Security, SSI, unemployment compensation or capital gains, and a dependent's rent and property taxes out of the Wisconsin homestead credit, and test each spouse separately as the claimant for its age and earned income requirements. +Leave a tax unit dependent's income, such as a child's Social Security, SSI, unemployment compensation or capital gains, and a dependent's rent and property taxes out of the Wisconsin homestead credit, and test each spouse separately as the claimant for its age and earned income requirements. Preserve qualifying negative net Social Security repayments in household income. diff --git a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml index 0e1d2e17617..5d29a3770b5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_income.yaml @@ -290,3 +290,164 @@ output: adjusted_gross_income: 6_000 wi_homestead_income: 17_000 + +# Negative Social Security represents required repayments of nontaxable +# benefits included in household income on a prior homestead claim. +# Wis. Stat. 71.52(6): +# https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/52/6 +# 2024 Schedule H instructions, page 12, Repaid amounts: +# https://www.revenue.wi.gov/TaxForms2024/2024-ScheduleH-inst.pdf#page=12 + +- name: Case 12, qualifying Social Security repayments reduce a single claimant's household income. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_head: true + taxable_pension_income: 6_000 + social_security_retirement: -1_000 + ssi: 0 + tax_units: + tax_unit: + members: [person1] + spm_units: + spm_unit: + members: [person1] + tanf: 0 + households: + household: + members: [person1] + state_code: WI + output: + # The repayment is not taxable income: 6,000 - 1,000 = 5,000. + adjusted_gross_income: 6_000 + wi_homestead_income: 5_000 + +- name: Case 13, spouses' qualifying Social Security repayments can produce negative net benefits. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_head: true + taxable_pension_income: 6_000 + social_security_retirement: 1_000 + ssi: 0 + person2: + age: 64 + is_tax_unit_spouse: true + social_security_retirement: -2_000 + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Combine spouses before applying the repayment: 6,000 + 1,000 - 2,000. + adjusted_gross_income: 6_000 + wi_homestead_income: 5_000 + +- name: Case 14, qualifying repayments are counted once when spouses' net benefits remain positive. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_head: true + taxable_pension_income: 6_000 + social_security_retirement: -1_000 + ssi: 0 + person2: + age: 64 + is_tax_unit_spouse: true + social_security_retirement: 4_000 + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # The combined 3,000 of benefits is nontaxable: 6,000 - 1,000 + 4,000. + adjusted_gross_income: 6_000 + wi_homestead_income: 9_000 + +- name: Case 15, spouses' qualifying repayment treatment is unchanged when their roles are swapped. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_spouse: true + taxable_pension_income: 6_000 + social_security_retirement: -1_000 + ssi: 0 + person2: + age: 64 + is_tax_unit_head: true + social_security_retirement: 4_000 + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + adjusted_gross_income: 6_000 + wi_homestead_income: 9_000 + +- name: Case 16, a dependent's Social Security does not offset the claimant's qualifying repayment. + period: 2024 + absolute_error_margin: 0.01 + input: + people: + person1: + age: 70 + is_tax_unit_head: true + taxable_pension_income: 6_000 + social_security_retirement: -1_000 + ssi: 0 + person2: + age: 17 + is_tax_unit_dependent: true + social_security_survivors: 3_000 + ssi: 0 + tax_units: + tax_unit: + members: [person1, person2] + spm_units: + spm_unit: + members: [person1, person2] + tanf: 0 + households: + household: + members: [person1, person2] + state_code: WI + output: + # Leave out the child's benefits: 6,000 - 1,000 - 500 = 4,500. + adjusted_gross_income: 6_000 + wi_homestead_income: 4_500 diff --git a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py index ec04afdb076..e30f967a6c6 100644 --- a/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py +++ b/policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_income.py @@ -11,10 +11,10 @@ class wi_homestead_income(Variable): "https://www.revenue.wi.gov/TaxForms2021/2021-ScheduleH.pdf", "https://www.revenue.wi.gov/TaxForms2022/2022-ScheduleH.pdf", "https://www.revenue.wi.gov/TaxForms2023/2023-ScheduleH.pdf#page=4", - # Wis. Stat. 71.52(4)-(6): household, household income and income + # Wis. Stat. 71.52(4)-(6): household, income and qualifying repayments. "https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/52", - # PDF pages 8, 13: the household is the claimant and spouse; leave - # out children's social security and SSI. + # PDF pages 8, 12-13: claimant/spouse household, qualifying repayments, + # and exclusion of children's social security and SSI. "https://www.revenue.wi.gov/TaxForms2024/2024-ScheduleH-inst.pdf#page=8", ) defined_for = StateCode.WI @@ -26,6 +26,17 @@ def formula(tax_unit, period, parameters): # income, such as a child's social security, SSI or unemployment # compensation. income = tax_unit_non_dep_add(tax_unit, period, p.homestead.income.sources) + # The filer Social Security source floors combined net benefits at + # zero for federal taxability. Wisconsin allows qualifying repayments + # of prior-year nontaxable household income (71.52(6); 2024 Schedule H + # instructions, page 12). Treat negative net benefits as qualifying + # repayments; prior inclusion cannot be checked without claim history. + # Combine spouses first, so repayments already offset by a spouse's + # positive benefits are not subtracted twice. Leave dependents out. + net_social_security = tax_unit_non_dep_add( + tax_unit, period, ["social_security"] + ) + income += min_(net_social_security, 0) disqualified_losses = tax_unit("wi_homestead_disqualified_losses", period) dependents = tax_unit("tax_unit_dependents", period) return income + disqualified_losses - dependents * p.homestead.income.exemption