Skip to content

Count household members' income in Montana's elderly homeowner/renter credit - #9882

Closed
MaxGhenis wants to merge 5 commits into
mainfrom
fix-mt-ehrc-household-member-income
Closed

MaxGhenis wants to merge 5 commits into
mainfrom
fix-mt-ehrc-household-member-income

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Montana's elderly homeowner/renter credit left out two groups of household members when it computed gross household income:

  • Tax unit dependents. The model summed each person's adjusted_gross_income_person over the tax unit. That is zero for a dependent, because irs_gross_income leaves dependents off the filer's return. So a dependent's wages, interest, gains and other gross income were never counted.
  • Household members who file their own returns, such as an adult child living with the claimant. They are outside the claimant's tax unit, so they were never counted.

The law counts both:

  • MCA 15-30-2337(4): "'Gross household income' means all income received by all individuals of a household while they are members of the household."
  • §2337(7)(a): "'Household' means an association of persons who live in the same dwelling, sharing its furnishings, facilities, accommodations, and expenses."
  • The 2023 Elderly Homeowner/Renter Credit Schedule (Form 2 p. 10): "For lines 1-7 and 9, use the amounts reported on Forms 2, page 1, for ALL members of the household."
    • Its instructions (PDF p. 51) combine the returns "completed by each member of the household".
    • Line 8 (PDF p. 52) says members "who are not filing a Form 2 or a Federal Form 1040 must report all wages, business income, and investment income".
  • The 2024 and 2025 Schedule 2EC ask "How many people lived in your household". Line 17 is "Income received by other members of your household" (2024 instructions PDF p. 48): "Report the combined taxable and nontaxable items of income listed above for all other members of the household." The instructions (PDF p. 46) add: "You do not need to be related to be in the same household."

Stacked on #9877. This PR is based on fix-mt-separate-filing-allocations, which edits the same variable to add back losses. The PR workflow runs only for PRs into main, so this PR targets main, and its diff includes #9877's commits until #9877 merges. This PR's own changes are the last two commits (compare). Merge #9877 first, then merge main into this branch.

Example: 2024, a 70-year-old with a working dependent

The claimant has $20,000 of taxable pension and is billed $2,000 of property tax. Their 16-year-old dependent earned $10,000 of wages.

2024 Schedule 2EC line Form Before After
5 Pensions 20,000 20,000 20,000
17 Other household members' income 10,000 0 10,000
18 Gross household income 30,000 20,000 30,000
20 = 18 − 12,600 17,400 7,400 17,400
21 Reduction rate 0.05 0.035 0.05
22 Net household income 870 259 870
27 = 2,000 − line 22 1,130 1,741 1,130
30 Credit (line 28 capped at 1,150 × 100%) 1,130 1,150 1,130

Now make the other member a 40-year-old who files their own return with $10,000 of wages. Before this PR the credit was $1,150. After it is $1,126.84: gross household income is $30,063.20, because their $63.20 Montana EITC (mt_eitc, 10% of their $632 federal EITC) counts as a refundable credit received (line 8).

Changes

  • mt_elderly_homeowner_or_renter_credit_gross_household_income (per person) now adds is_tax_unit_dependent * dependent_gross_income.
    • dependent_gross_income is the existing person-level variable for a dependent's own gross income. It takes the IRS gross income sources at positive amounts, gains without losses, unemployment compensation in full, and the IRC 86 taxable part of Social Security.
    • Gating on is_tax_unit_dependent, the same flag that zeroes irs_gross_income, splits people exactly between the federal-AGI path and the dependent path. Without the gate, dependent_gross_income's own test, "not head or spouse", would count a person with neither role twice. That case is a minor alone in a tax unit whose dependent flag is input as false.
    • The untaxed Social Security add-back now subtracts dependent_taxable_social_security as well as taxable_social_security. Otherwise a dependent's taxable benefits would count twice.
  • mt_elderly_homeowner_or_renter_credit_net_household_income and mt_elderly_homeowner_or_renter_credit sum gross household income over person.household instead of person.tax_unit. They also get statute and Schedule 2EC references.

Scope: why the household, not the tax unit or SPM unit

I chose PolicyEngine's Household entity.

  • The tax unit is what this PR fixes. It misses every member who files their own return, and the 2023 schedule, the 2024–2025 line 17 and §2337(4) all count them.
  • The SPM unit groups people who share resources. It leaves out unrelated roommates, but they share "furnishings, facilities, accommodations, and expenses" (§2337(7)(a)), and the instructions say "You do not need to be related".
  • The household entity is everyone sharing the dwelling, which matches §2337(7)(a). The repo's Utah homeowner's/renter's relief (ut_homeowner_renter_relief_household_income) uses it for the same kind of definition.

Limits of the proxy:

  • §2337(7)(b) excludes "bona fide lessees, tenants, or roomers and boarders on contract". PolicyEngine has no input that identifies them, so they count if they are in the household.
  • §15-30-2341(1) allows "only one claimant per household". The model still does not enforce it, which is unchanged by this PR. If a household has two tax units with someone aged 62 or older, each still computes a credit. Each now uses the whole household's income, so the over-count is smaller than before.

Tests

  • 9 new YAML cases, all worked from the schedules. All fail on Align Montana separate-filing deductions and the elderly credit's income with the forms #9877's head (6eefe271dc) and pass here.
    • ..._gross_household_income.yaml: three cases. A dependent's wages count. A dependent's Social Security counts once: $25,000 wages and $10,000 benefits give $35,000, not $37,500, with dependent_taxable_social_security $2,500 asserted. A dependent's capital loss does not offset their wages (2024 line 7: "do not include any capital losses").
    • ..._net_household_income.yaml: one case. A member in another tax unit counts: [870, 0].
    • mt_elderly_homeowner_or_renter_credit.yaml: five cases.
      • The worked example above: 1,130.
      • A member filing their own return: 1,130.
      • A dependent's wages moving the credit into the 40% band: 36,000 gross gives 830 × 0.4 = 332.
      • Gross household income of exactly $45,000: 0.
      • The 2023 schedule, where a non-filing dependent's wages go on line 8: 1,130.
  • Property and differential test in policyengine_us/tests/test_mt_elderly_credit_household_members.py, using Hypothesis.
    • Setup. A claimant aged 62 or older and one other household member, placed three ways: (A) as the claimant's dependent, (B) filing their own return, and (C) as A with more interest.
    • The differential check. A and B reach the same quantity through different code: dependent_gross_income and dependent_taxable_social_security in A, and federal AGI, Align Montana separate-filing deductions and the elderly credit's income with the forms #9877's loss add-back and taxable_social_security in B. YAML can't check agreement across the input space; this test does. It caught the double count that the gate above now prevents.
    • Properties asserted:
      1. Household gross income equals the independent Schedule 2EC total (pension + Social Security + interest + positive gains + the member's Social Security) in A, B and C.
      2. The claimant's credit is the same in A and B, and the other member's own tax unit claims nothing.
      3. 0 ≤ credit ≤ $1,150, and the credit is 0 when gross household income is at least $45,000.
      4. More income never raises the credit (C ≤ A).
    • Cost. It uses 15 examples of 1–8 cases, all in one vectorized Simulation per example. Locally on a loaded macOS host, the file runs in 4.0 s of test time and 19 s wall including model import, with a 0.95 GB peak footprint. These are local measurements, not CI. A one-off 400-example local run, with up to 12 cases each, also passed. The file lands in the Rest job's remaining group (make test-other-python-rest-remaining); code_health/test_rest_python_groups.py assigns it there automatically.
  • Broader sweep. The mt/ folder, contrib/states/mt and the state tax rollups are queued under the shared heavy lock. Results to follow below.

Invariants

  • Each household member's income enters gross household income exactly once, through federal AGI for filers and through dependent_gross_income for tax unit dependents. The two paths are disjoint because both are keyed to is_tax_unit_dependent.
  • Social Security counts in full, once: taxable part + untaxed part = social_security.
  • Household gross household income does not depend on how members are split into tax units (property test, A vs B).
  • Gross household income does not fall when any member's income rises, so the credit does not rise (property test, C vs A).
  • 0 ≤ credit ≤ cap, and the credit is 0 at gross household income of $45,000 or more.

Microsim impact

MICROSIM_PENDING

Not changed (follow-ups)

  • Federal refundable credits. Schedule 2EC line 8 counts "the federal and Montana earned income tax credits, the refundable portion of the federal child tax credit", but gov.states.mt.tax.income.credits.refundable lists only mt_eitc. This gap predates the PR.
  • One claimant per household (§2341(1)), above.
  • Dependents' above-the-line deductions are not subtracted. The 2024–2025 Schedule 2EC builds income from gross items, so this matches the current forms.

axiom: TheAxiomFoundation/rulespec-us#1512 queued (required output 6 already specifies gross household income over "every statutory household member … including dependents", membership per §2337(7); this PR's companion cases are added in this comment)

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits October 6, 2026 07:08
… the forms

- Federal income tax deduction (through 2023): attribute federal tax to the
  spouse who earned the income and cap each spouse at $5,000, instead of
  prorating the jointly capped amount by Montana AGI (2022 Form 2
  instructions, Itemized Deductions Schedule, line 4).
- Child and dependent care expense deduction (through 2023): phase out on the
  couple's combined Montana AGI and split the result equally only for spouses
  filing separately on the same form; a single filer and a joint return take
  the whole deduction (Form 2441-M lines 3-7).
- Elderly homeowner/renter credit: count federal AGI without regard to losses
  (MCA 15-30-2337(9)(a); Form 2EC line 9).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ions

Each spouse filing separately on the same form deducts their own federal
income tax up to $5,000, so both spouses itemize and Montana tax is 6,295.56.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…chedule

- A married person filing on a separate form (federal filing status
  SEPARATE) cannot take the child and dependent care expense deduction:
  "married couples shall file a joint return or file separately on the same
  form" (MCA 15-30-2131(1)(c)(vi)(A)); 2022 instructions, line 14.
- The 2023 credit schedule was the Elderly Homeowner/Renter Credit
  Schedule; it became Schedule 2EC in 2024.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MCA 15-30-2337(4) defines gross household income as "all income received
by all individuals of a household while they are members of the
household". The 2023 Elderly Homeowner/Renter Credit Schedule combines the
returns of "ALL members of the household" (lines 1-7 and 9) and has
non-filing members report their wages on line 8; the 2024 and 2025
Schedule 2EC put "Income received by other members of your household" on
line 17.

The model summed each person's federal AGI over the tax unit. A tax unit
dependent's federal AGI is zero (their income is on their own return), so
a dependent's wages and other income were left out, and household members
who file their own returns were never counted.

- Add dependent_gross_income to the gross income sources. It counts a
  dependent's gross income items, gains but not losses, and the taxable
  part of their Social Security; the formula now subtracts that part
  before adding the untaxed remainder, so the benefits count once.
- Sum gross household income over the household instead of the tax unit
  in the net household income and the credit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… tests

dependent_gross_income counts anyone who is not the head or spouse, while
irs_gross_income counts anyone who is not a dependent. A person with
neither role (a minor alone in a tax unit whose dependent flag is input as
false) was counted by both. Count dependent_gross_income in the formula
for tax unit dependents only, so the two paths split people exactly.

The property tests check, for random claimants and household members,
that gross household income is the Schedule 2EC total whether the other
member is a dependent or files their own return, that the credit is the
same either way, stays within its bounds, and never rises with income.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Oct 7, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Count household members' income in Montana's elderly homeowner/renter credit

PR #9882 (author: MaxGhenis, head 54d30751e0, 5 commits ahead of main). The PR is stacked on #9877 ("Align Montana separate-filing deductions and the elderly credit's income with the forms", head 6eefe271dc), and #9877's head is an ancestor of this PR's head.

This review covers only the two commits on top of #9877: 940e69f031 and 54d30751e0. #9877's own changes, including the loss_ald add-back this PR builds on, are covered by #9877's review, and its findings are not repeated here.

Summary

The two commits change how gross household income is counted for Montana's elderly homeowner/renter credit:

  1. Dependents' income. Each person's gross household income now adds is_tax_unit_dependent * dependent_gross_income. A tax unit dependent's wages, interest and gains (without losses) were left out before, because irs_gross_income zeroes dependents. The untaxed Social Security add-back now also subtracts dependent_taxable_social_security, so a dependent's benefits count once, in full.
  2. Members outside the tax unit. The credit and net household income now sum gross household income over person.household instead of person.tax_unit. Household members who file their own returns, such as an adult child, now count.

Both changes follow the law:

  • MCA 15-30-2337(4): "'Gross household income' means all income received by all individuals of a household while they are members of the household."
  • §2337(7)(a): "'Household' means an association of persons who live in the same dwelling, sharing its furnishings, facilities, accommodations, and expenses."
  • 2024 Schedule 2EC line 17 (2024 instructions PDF p.48): "Report the combined taxable and nontaxable items of income listed above for all other members of the household."

What I checked:

  • All 29 cases in the elderly credit test folder pass on the extracted head tree.
  • Both functions in the new Hypothesis file pass: test_properties runs 15 examples in 11.4 s and test_worked_example takes 1.6 s.
  • CI is green (35/35), and the branch merges cleanly.
  • I hand-checked 9 expectations and ran 5 probe households on the head tree.

I found no critical issues. Each household member's income now enters exactly once, and multi-tax-unit households are handled consistently.

Two items need attention:

  • S1: a person on their own return still has a short-term loss netted against a long-term gain. So gross household income depends on how members are split into tax units, which contradicts the invariant the PR body, the test docstring and the rulespec-us addendum state. The property test cannot see this.
  • S2: the is_tax_unit_dependent gate that commit 54d30751e0 adds has no deterministic test. No YAML case reaches it, and only the randomized property test exercises it.

Critical (must fix)

None.

Should address

S1. Mixed-sign capital gains: the filer path nets a person's own losses, so gross household income depends on the tax-unit split.

  • Where:
    • policyengine_us/variables/gov/states/mt/tax/income/credits/mt_elderly_homeowner_or_renter/mt_elderly_homeowner_or_renter_credit_gross_household_income.py:29,54-56. A filer contributes adjusted_gross_income_person plus the head-only loss_ald add-back.
    • policyengine_us/tests/test_mt_elderly_credit_household_members.py:76,142,231-233. The property test draws only long_term_capital_gains.
  • What's wrong:
    • For a person on their own return, irs_gross_income counts max_(0, capital_gains), where capital_gains is long-term plus short-term, already netted.
    • loss_ald holds only the deductible net capital loss and losses netted across persons. A short-term loss netted against the same person's long-term gain is never deducted there, so the add-back never restores it.
    • The new dependent path counts max_(LTCG⁺ + STCG⁺, capital_gains), which is "gains but not losses" (dependent_gross_income.py:63-66).
    • So the same person gives different totals depending on whether they are a dependent or file their own return. Count household members' income in Montana's elderly homeowner/renter credit #9882 sends every other household member who files their own return through the filer path, and it claims the two paths agree:
      • PR body, Invariants: "Household gross household income does not depend on how members are split into tax units (property test, A vs B)."
      • Test docstring, test_mt_elderly_credit_household_members.py:20-22: "...whichever path counts the other member."
      • rulespec-us#1512 addendum: "Gross household income is the same however members are divided among returns."
  • Law:
    • §2337(9)(a): income is "federal adjusted gross income, without regard to loss".
    • 2024 Schedule 2EC line 7 (2024 instructions PDF p.47): "Report the total amount of capital gains … do not include any capital losses." Line 17 carries the same items for other household members.
  • Head-tree probe (2024). Claimant 70 with a $20,000 pension and $2,000 of property tax. The other household member, 30, has LTCG +10,000 and STCG −4,000.
Arrangement Gross household income Net household income Credit Schedule 2EC
Member files their own return 26,000 ([20_000, 6_000]) 670 1,150 30,000 → 1,130
Member is the claimant's dependent 30,000 ([20_000, 10_000]) 870 1,130 30,000 → 1,130
  • Form arithmetic: (30,000 − 12,600) × 0.05 = 870, and 2,000 − 870 = 1,130.
  • The own-return row gives (26,000 − 12,600) × 0.05 = 670, and 2,000 − 670 = 1,330, capped at 1,150.
  • The claimant's own return has the same gap, which comes from Align Montana separate-filing deductions and the elderly credit's income with the forms #9877's add-back. Count household members' income in Montana's elderly homeowner/renter credit #9882 extends it to every other filer in the household and states the invariant that it breaks.
  • Impact: for any household member with mixed-sign gains on their own return, gross household income is understated by the netted loss. The credit is then too high, and the $45,000 limit test can be affected.
  • Fix:
    • In the filer path, add back each non-dependent's within-person netted capital loss. Mirror dependent_gross_income.py:63-66, so that a directly input net capital_gains still works:
      not_dependent = ~is_dependent
      gains_without_losses = max_(
          max_(0, person("long_term_capital_gains", period))
          + max_(0, person("short_term_capital_gains", period)),
          person("capital_gains", period),
      )
      netted_capital_loss = not_dependent * (
          gains_without_losses - max_(0, person("capital_gains", period))
      )
    • This leaves cross-person netting to the existing loss_ald add-back. For a joint couple with the gain on one spouse and the loss on the other, each person's term is 0, and the total stays 30,000.
    • Add a short_term_capital_gains draw to the property test (:231-233).
    • Add a YAML case: a member on their own return with LTCG 10,000 and STCG −4,000 gives gross household income [20_000, 10_000] and credit [1_130, 0].
    • If the fix is deferred, qualify the invariant in the PR body, in the test docstring and in the Correct MA income tax Part A AGI calculation #1512 addendum. Suggestion 1 below describes a second way the invariant fails: above-the-line deductions.

S2. The dependent gate from commit 54d30751e0 has no deterministic test.

  • Where: mt_elderly_homeowner_or_renter_credit_gross_household_income.py:36-37:
    is_dependent = person("is_tax_unit_dependent", period)
    dependent_income = is_dependent * person("dependent_gross_income", period)
  • What's wrong:
    • dependent_gross_income counts anyone who is not the head or spouse (dependent_gross_income.py:72-73), while irs_gross_income counts anyone who is not a dependent. The gate matters only for a person with neither role, which is the double count that commit 54d30751e0 fixes. That person is a minor alone in a tax unit whose is_tax_unit_dependent is input as false.
    • Head-tree probe (2024). Claimant 70 with a $20,000 pension and $2,000 of property tax. A 16-year-old with $10,000 of wages is alone in a second tax unit, with is_tax_unit_dependent: false.
      • The minor is not head, spouse or dependent. dependent_gross_income is 10,000, and adjusted_gross_income_person is also 10,000.
      • With the gate, gross household income is [20_000, 10_000] and the credit is 1,130.
      • Without the gate, the minor's income would count twice: 40,000 in total, so net household income would be 27,400 × 0.05 = 1,370, and the credit (2,000 − 1,370) × 30% = 189.
    • No YAML case reaches this branch. No case in the folder inputs is_tax_unit_dependent, so in every YAML household the flag equals "not head or spouse" and the gate has no effect. All 29 cases would pass without it.
    • Only the randomized property test reaches it. It does so in arrangement B when it draws member_age 15–17 (test_mt_elderly_credit_household_members.py:88,229). The examples are random on each CI run, because the settings at :238 do not set derandomize.
    • A mutation check shows the property test is not a reliable guard. I removed the gate on the head tree, in scratch only, and ran test_properties under 10 Hypothesis seeds with the database off. It failed under 8 seeds and passed under 2 (seeds 6 and 7). So about one CI run in five would let a regression of the fix that commit 54d30751e0 makes go unnoticed.
  • Fix:
    • Add a YAML case to mt_elderly_homeowner_or_renter_credit_gross_household_income.yaml with the probe above:
      • person1: age 70, taxable_pension_income: 20_000, is_tax_unit_dependent: false, in tax_unit1.
      • person2: age 16, employment_income: 10_000, is_tax_unit_dependent: false, alone in tax_unit2.
      • Both are in one Montana household.
      • Expected: mt_elderly_homeowner_or_renter_credit_gross_household_income: [20_000, 10_000].
      • Optionally also assert mt_elderly_homeowner_or_renter_credit: [1_130, 0] with person1's real_estate_taxes: 2_000.
      • Without the gate the result would be [20_000, 20_000].
    • Optionally set derandomize=True, as policyengine_us/tests/core/test_dependent_above_the_line_deductions_invariants.py:395 does.

Suggestions

  1. The two paths use different income readings, and another tax unit can now pull household income down.
    • Where: mt_elderly_homeowner_or_renter_credit_gross_household_income.py:29,36-37.
    • The difference:
      • Filers contribute adjusted_gross_income_person, which is net of above-the-line deductions. That follows the statute's "federal adjusted gross income" and line 1 of the 2023 schedule.
      • Dependents contribute gross items. That follows 2024–2025 Schedule 2EC, and the PR body says so under "Not changed".
      • Head probe (2024): another member with $20,000 of self-employment income. As a dependent, gross household income is 40,000 and the credit is 189. On their own return it is 38,587.08 and the credit is 210.19.
    • New with this PR: because the sum now crosses tax units, a negative AGI in another tax unit offsets the claimant's income.
      • My head probe (2024): the claimant from S1, plus a 30-year-old with no income and $2,500 of student loan interest on their own return. Per-person gross household income is [20_000, -2_500].
      • Net household income falls from 259 to 117.60. The credit stays at the 1,150 cap here, but it would not for a claimant with a smaller property tax bill.
      • Schedule 2EC lines 1–17 are non-negative receipts: line 10 says "Do not include any losses".
    • Fix: at minimum, floor each other tax unit's total contribution at zero.
      • Floor per tax unit, not per person. On a joint return, adjusted_gross_income_person splits loss_ald 50/50 and the add-back sits on the head, so a spouse's row can legitimately be negative.
      • Or pick one reading for both paths, for example subtracting dependents' own PERSON_ABOVE_THE_LINE_DEDUCTIONS as dependent_taxable_ss_magi does.
      • Either way, state the reading in a comment and in the PR body's invariants.
  2. Line 8's federal refundable credits are missing.
    • This is documented in the PR body and predates the PR, but its reach grows here.
    • gov.states.mt.tax.income.credits.refundable lists only mt_eitc, while line 8 counts "the federal and Montana earned income tax credits, the refundable portion of the federal child tax credit".
    • Before the PR, only the elderly claimant's own tax unit entered the sum, and it rarely has the federal EITC or ACTC. Now working household members with children enter it.
    • Head probe (2024): a three-generation household. The grandmother is 70 with a $15,000 pension and $2,000 of property tax. Her daughter is 35 with $25,000 of wages, and the daughter's children are 5 and 8 (the 8-year-old has $3,000 of wages).
      • The model gives gross household income of 43,647.98 and a credit of 44.76.
      • Adding the federal EITC (6,479.83) and the refundable CTC (3,375) gives 53,502.81, which is at least $45,000, so the credit on the form is $0.
    • The sources list also has no SSI, TANF or workers' compensation. 2024 line 16 names "supplemental security income (SSI) payments, worker's compensation", and these now matter for any household member.
    • Fix: link a follow-up issue. Use a separate line 8 list, because credits.refundable also drives Montana tax.
  3. Limits of the Household proxy.
  4. Reference, comment and PR-body polish.
    • The p.51 comment. mt_elderly_homeowner_or_renter_credit_gross_household_income.py:17-19 labels the p.51 link "lines 1 and 8 … the wages of members who do not file". Page 51 shows line 1. Line 8's "must report all wages, business income, and investment income" is on p.52, which is already the first form link in the tuple. Retitle the comment "line 1 and the household-members paragraph".
    • The credit's citations. mt_elderly_homeowner_or_renter_credit.py:11-15 cites only the definitions section, §2337. The computation (cap, multiplier) is §15-30-2340, and the $45,000 limit is §15-30-2338(1)(d). Add both. _net_household_income.py:11-15 could add §2340(4).
    • The parameter-file comment. gross_income_sources.yaml:31-33 still describes the add-back as social_security - taxable_social_security, and it does not say that dependents' income is added in the formula. The PR touches this file and then restores it, so this is optional.
    • Test names. "2024 Schedule 2EC worked case …" and "2023 Elderly Homeowner/Renter Credit Schedule worked case …" (mt_elderly_homeowner_or_renter_credit.yaml:258,290,322,372) can read as official DOR examples. Align Montana separate-filing deductions and the elderly credit's income with the forms #9877 got the same note.
    • The PR body. "Microsim impact: MICROSIM_PENDING" and "Broader sweep … Results to follow below" are still open, and no comment followed. The switch to the household entity is the change most likely to move totals, because claimants in multi-tax-unit households lose credit. Fill these in or drop them.
  5. Make the property test reproducible. @hypothesis.settings(max_examples=15, …) (test_mt_elderly_credit_household_members.py:238) draws new examples on each CI run. A latent failure would then surface at random on an unrelated PR. derandomize=True (see tests/core/test_dependent_above_the_line_deductions_invariants.py:395) keeps the cost the same and makes CI reproducible.
  6. Optional YAML tests. All values were confirmed on the head tree.
    • A dependent of another tax unit. In the three-generation household from suggestion 2, the grandchild's $3,000 of wages counts through the daughter's unit. Per-person gross household income is [15_000, 25_647.98, 0, 3_000], and the credit is [44.76, 0, 0, 0].
    • A member on their own return with a refundable credit. This is the PR body's example: a 40-year-old with $10,000 of wages gives gross household income [20_000, 10_063.20] and a credit of [1_126.84, 0].
    • A member on their own return with a loss. This checks the add-back on another unit's head. The property test covers it only at random.

Verified correct

  • Household-wide sum.
    • add(person.household, …) in the credit (mt_elderly_homeowner_or_renter_credit.py:26-30) and in net household income (_net_household_income.py:28-32) counts everyone in the dwelling, as §2337(4) and (7)(a), the 2023 "ALL members of the household" instruction and 2024–2025 line 17 require.
    • The Household entity fits "You do not need to be related to be in the same household" (2024 instructions p.46) better than the SPM unit, which would drop roommates. Utah's ut_homeowner_renter_relief_household_income uses the same entity.
  • Each person counts once.
    • irs_gross_income zeroes is_tax_unit_dependent, and the new term is gated on the same flag, so each person takes exactly one path.
    • The neither-role probe in S2 gives [20_000, 10_000], so that person counts once.
    • The new path does not overlap the Montana source list. dependent_gross_income iterates gov.irs.gross_income.sources, and none of the Montana nontaxable items appear there: tax-exempt interest, IRA and pension income, veterans' benefits, child support, strike benefits, general_assistance and mt_refundable_credits_before_renter_credit. So a dependent's nontaxable items are not double counted.
  • Social Security counts once.
    • tax_unit_social_security_for_taxability removes dependents' benefits, so a head's taxable_social_security never includes a dependent's benefits.
    • dependent_taxable_social_security is defined_for = "is_tax_unit_dependent", so it is zero for every filer.
    • Taxable plus untaxed equals social_security on both paths.
  • Multi-tax-unit households in microsimulation.
    • Every tax unit head in a household sees the same household total.
    • loss_ald is added once per tax unit head, and mt_eitc is per person, so summing across tax units adds nothing twice.
    • Property tax and rent are still summed over the claimant's own tax unit (mt_elderly_homeowner_or_renter_credit.py:39-40).
    • The only over-count is two eligible claimants in one household (suggestion 3, §2341). It is smaller than before the PR.
  • Performance.
    • dependent_gross_income, and through it dependent_taxable_social_security, is already computed for every person in baseline runs, through head_of_household_eligible → is_qualifying_relative_dependent. So the PR adds only a household aggregation.
    • There is no circularity: the household sum reads other units' mt_eitc, never the elderly credit.
  • Callers (grep at head).
    • Gross household income feeds only the credit and net household income.
    • The credit feeds mt_refundable_credits and the TAXSIM-only state_property_tax_credits list.
    • dependent_gross_income's other caller, is_qualifying_relative_dependent, is unchanged.
  • Code patterns.
    • The code is vectorized, with no hard-coded policy values. The property test's CAP = 1_150 and INCOME_LIMIT = 45_000 are test constants that match cap.yaml (2022+) and multiplier.yaml.
    • Entities and YEAR periods are correct, and defined_for is unchanged and appropriate.
    • The references are tuples with comments.
    • The new property-test file follows the repo's existing top-level Hypothesis tests, for example test_mi_household_resources_losses.py, and hypothesis>=6.100.0 is a dev dependency (pyproject.toml:57).
  • Hand-checked expectations (head tree, all match).
    1. Dependent wages (credit.yaml:258): (30,000 − 12,600) × 0.05 = 870, and 2,000 − 870 = 1,130. Under Align Montana separate-filing deductions and the elderly credit's income with the forms #9877's tax-unit sum it would be 1,150.
    2. Member on own return with $10,000 of interest (:290): 1,130. There is no EITC, because there are no earnings.
    3. 40% band (:322): 36,000 − 12,600 = 23,400; × 0.05 = 1,170; 2,000 − 1,170 = 830; × 0.4 = 332.
    4. Exactly 45,000 (:349): multiplier 0, so 0. §2338(1)(d) says "less than $45,000".
    5. 2023 non-filing dependent (:372): 12,600 exclusion and 1,150 cap, so 1,130. $10,000 of wages is under the $13,850 dependent filing threshold.
    6. Dependent Social Security (gross_household_income.yaml:114):
      • Provisional income is 25,000 + 5,000 = 30,000, between the 25,000 base and the 34,000 adjusted base.
      • Taxable is min(5,000, 0.5 × 5,000) = 2,500.
      • Gross household income is 25,000 + 2,500 + 7,500 untaxed = 35,000.
    7. Dependent capital loss (:141): gains without losses, so 10,000.
    8. Net household income for a member of another unit (net_household_income.yaml:44): 870.
    9. Property worked example: 35,000 − 12,600 = 22,400; × 0.05 = 1,120; 2,000 − 1,120 = 880; × 0.4 = 352.
  • Tests run on the head tree.
    • Elderly credit folder: 29/29 pass, with local policyengine-core 3.26.
    • test_mt_elderly_credit_household_members.py: 2/2 pass. Hypothesis reports "15 passing, 0 failing".
    • All test periods are plain.
  • Changelog. changelog.d/fix-mt-ehrc-household-member-income.fixed.md is top-level, has type fixed, matches the branch name, and describes the behavior.

Axiom line assessment

  • The line: axiom: TheAxiomFoundation/rulespec-us#1512 queued (required output 6 …). The <rulespec issue> queued form is valid.
  • The issue: rulespec-us#1512 is OPEN and labelled pe-parity. It was updated 2026-10-06T22:57Z, after head 54d30751e0.
  • Coverage: required output 6 already covers gross household income over "every statutory household member … including dependents", with membership per §2337(7).
  • The addendum (comment 6016650339) adds:
    • verbatim §2337(4) and (7)(a)–(b), and the 2023 and 2024 schedule text;
    • eight companion rows that match the YAML cases;
    • a property to pin;
    • a methodology boundary (household proxy, roomers, §2341).
  • Dispatch-readiness: it is dispatch-ready on the Add AMT parameters for 2020-2023 #1467 model.
  • Caveat: the addendum says "Gross household income is the same however members are divided among returns." That sentence is false for mixed-sign capital gains (S1) and for above-the-line deductions (suggestion 1). Qualify it, or fix S1 and suggestion 1.

CI status

All green: gh pr checks 9882 shows 35/35 passing at head 54d30751e0.

Branch status

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This review covers only the two commits on top of #9877 (#9877's findings are in its own review). Counting household members' income follows MCA 15-30-2337(4), the elderly-credit tests pass on the head, and rulespec-us#1512 is dispatch-ready. Two should items (see the comment above): S1, a member on their own return still nets a short-term loss against a long-term gain, so gross household income depends on how members are split into tax units, which contradicts the invariant the PR claims (26,000 on their own return vs 30,000 as a dependent; the form gives 30,000); and S2, the is_tax_unit_dependent gate has no deterministic test (add the minor-alone-in-a-tax-unit YAML case). Merge #9877 first.

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Closing as superseded. This PR's two commits (940e69f031, 54d30751e0) landed on main in #9915 (6c62c7f217, 2026-10-11). #9915 was stacked on this branch and squash-merged with them. On main, the credit and net household income files (the household-wide sum) and the property test are identical to this PR's head; #9915 then extended the dependent path.

Thanks @DTrim99 for the review. Here is where each item from it, and from the second independent review (GPT-6.1 Sol, Subfleet, 2026-10-06), now stands.

Fixed in #10139 (draft, off main):

Already handled on main by #9915:

  • Line 8 federal refundable credits. They count from 2017 (mt_elderly_homeowner_or_renter_credit_federal_refundable_credits).
  • Dependents' above-the-line deductions (second review P1-2, and your suggestion 1). Dependents' own non-loss deductions are now subtracted, so both paths follow the AGI reading.
  • The p. 51 comment was retitled.

Owned by #9900 (the US + core hub), to keep a single writer on files it is rewriting:

  • derandomize=True and short-term gain draws in test_mt_elderly_credit_household_members.py.
  • The §15-30-2340 and §15-30-2338(1)(d) citations on the credit.
  • The stale "one claim per household" comment in net household income.
  • One claimant per household (§15-30-2341(1)) itself.

Filed as a follow-up: SSI, TANF and workers' compensation are still missing from gross household income (2024 Schedule 2EC line 16).

Not changed, with reasons:

🤖 Generated with Claude Code

@MaxGhenis MaxGhenis closed this Oct 12, 2026
@MaxGhenis
MaxGhenis deleted the fix-mt-ehrc-household-member-income branch October 12, 2026 09:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants