Skip to content

Fix the 2020 unemployment compensation exclusion: cutoff test, per-spouse cap, dependents - #10175

Open
MaxGhenis wants to merge 7 commits into
mainfrom
fix-uc-exclusion-2020
Open

MaxGhenis wants to merge 7 commits into
mainfrom
fix-uc-exclusion-2020

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 12, 2026 •

Copy link
Copy Markdown
Contributor

Fixes three gaps in the 2020 federal unemployment compensation exclusion (IRC 85(c), added by ARPA § 9042).

The law

26 U.S.C. 85(c) (checked 2026-10-12):

(1) In general. In the case of any taxable year beginning in 2020, if the adjusted gross income of the taxpayer for such taxable year is less than $150,000, the gross income of such taxpayer shall not include so much of the unemployment compensation received by such taxpayer (or, in the case of a joint return, received by each spouse) as does not exceed $10,200.
(2) Application. For purposes of paragraph (1), the adjusted gross income of the taxpayer shall be determined— (A) after application of sections 86, 135, 137, 219, 221, 222, and 469, and (B) without regard to this section.

Section 85(a) is what puts unemployment compensation in gross income, so "without regard to this section" leaves all of it out. The IRS implements this in the Unemployment Compensation Exclusion Worksheet in the 2020 Instructions for Form 1040 (rev. 4-2021), file page 88:

  • Lines 1–4 add total income without unemployment compensation (line 2: "Don't include any amount of unemployment compensation from Schedule 1, line 7").
  • Line 5 subtracts the adjustments, and line 6 is "your modified adjusted gross income".
  • Line 7: "$150,000 or more? Yes. Stop."
  • Lines 8 and 9 take the compensation paid to you and, on a joint return, to your spouse, each up to $10,200.

Page 87 has the joint example: "$20,000 … and your spouse was paid $5,000 … report $15,200 on line 8 as a negative amount".

What was wrong

  1. The compensation was subtracted twice in the $150,000 test. taxable_uc_agi already sums the gross income sources other than taxable_unemployment_compensation, less above-the-line deductions, so it is AGI without the compensation (worksheet line 6). tax_unit_taxable_unemployment_compensation then compared taxable_uc_agi - tax_unit_unemployment_compensation with the cutoff. A single filer in Montana, age 66, with $150,000 of taxable pension income and $8,000 of compensation, got taxable_unemployment_compensation 0. The law gives 8,000, because line 6 is 150,000, which is not under $150,000. The two boundary tests added in Fix 2020 unemployment exclusion boundary at $150,000 AGI #8900 encoded the same misreading: their comments say "AGI over UC = 155,000 - 5,000 = 150,000".
  2. The $10,200 was per tax unit, not per spouse. The model excluded min(tax unit compensation, 10_200). In the IRS joint example it gave taxable [11_840, 2_960] (14,800 in all); the law gives [9_800, 0].
  3. Dependents' compensation used the filers' exclusion. tax_unit_unemployment_compensation adds every member, dependents included. Take a 2020 joint couple whose spouse received $8,000 and whose 20-year-old student dependent received $4,000. The model gave taxable [0, 1_200, 600], and irs_gross_income then dropped the dependent's 600. The law gives the couple 0 taxable: the spouse's 8,000 is under 10,200, and the dependent's 4,000 is on the dependent's own return.

Changes

  • taxable_unemployment_compensation (Person) is now each person's own compensation less their own exclusion. That exclusion is min(max(0, compensation), 10_200) when the AGI of the return the compensation is on is under the cutoff, and nothing otherwise.
    • Head and spouse: taxable_uc_agi of the tax unit, compared directly with the cutoff, with no second subtraction.
    • Dependent: the new dependent_taxable_uc_agi, their own § 85(c)(2) AGI, against the single cutoff, or the separate one if they live with their spouse. It reuses dependent_income_and_deductions from dependent_taxable_ss_magi.py, less the compensation, plus dependent_taxable_social_security.
    • Taxable compensation is never negative. irs_gross_income counts positive amounts only, so a net repayment never reduced gross income. Before this PR a person's share was 0 when the tax-unit total was not positive. A negative taxable amount would let California, Pennsylvania and Massachusetts subtract it. tax_exempt_unemployment_compensation (received less taxable) carries the repayment as a negative amount, as before this PR, which Wisconsin's homestead income reads.
  • tax_unit_taxable_unemployment_compensation is the sum over members who are not dependents (sum_among_non_dependents), as irs_gross_income counts them.
  • taxable_uc_agi: the label, documentation and references now say what it is (worksheet line 6). The formula is unchanged.
  • The exemption amount and cutoff parameters get descriptions, labels and references (IRC 85(c), worksheet lines).
  • tax_unit_unemployment_compensation is unchanged and no longer feeds the exclusion.
  • dependent_taxable_ss_magi.py: estate_income joins BUSINESS_LOSS_SOURCES, so a dependent's own estate or trust loss (Schedule E Part III, Schedule 1 line 5) reduces the modified AGI of the dependent's own return. limited_business_loss already deducts these losses for the head and spouse, and the list's own comment says it holds the sources loss_ald deducts. The shared helper feeds dependent_taxable_uc_agi, dependent_taxable_ss_magi (and so dependent_taxable_social_security) and dependent_traditional_ira_deduction, so all three now net the loss. Tests: dependent_taxable_uc_agi.yaml Case 3 and a new dependent_taxable_ss_magi.yaml case.
  • The same helper now applies IRC 461(l) on the dependent's own return, as limited_business_loss does for the filers. Business losses (self-employment, farm, rental, partnership/S corporation, estate) are allowed up to business income plus gov.irs.ald.loss.max for a single (or, living with a spouse, separate) filer. That threshold is infinite for 2018–2020 (CARES Act § 2304), so the 2020 test keeps the whole loss. Without the limit, a $700,000 estate business loss would have wiped out a 2025 dependent's taxable Social Security. Tests: the 2025 above-limit case in dependent_taxable_ss_magi.yaml (modified AGI 88,000, taxable benefits 17,000), and dependent_taxable_uc_agi.yaml Case 4 (2020, suspended).

Direction of allocation. Before, the person-level amount was the tax-unit amount times the person's share of the unit's compensation. Now persons are primary and the tax unit sums them. No dataset input sets tax_unit_taxable_unemployment_compensation: the hub's screen sample of the default dataset, which keeps its inputs, has 18 tax-unit columns and none is this one, and no reform overrides it. One test sets it as an input (in_unemployment_compensation_deduction.yaml Case 1), and only Indiana's deduction reads it there, which is unchanged. The only remaining readers of the tax-unit variable are Indiana's deduction and taxsim_v11.

Effect outside 2020

The exclusion amount is 0 outside 2020, so the person-level taxable_unemployment_compensation is each person's own compensation in every other year, before and after this PR. The tax-unit total now leaves out dependents' compensation in every year, which moves its two readers:

  • Indiana in_unemployment_compensation_deduction (IC 6-3-2-10, in effect from 2021). It deducted a dependent's compensation that is not in the filers' federal AGI, and now does not. Test: in_unemployment_compensation_deduction.yaml Case 3.
  • taxsim_v11 (contrib TAXSIM output), which reports the tax-unit total.

Invariants

These hold for every input, net repayments included, and are tested on random households in tests/core/test_unemployment_compensation_exclusion_invariants.py (Hypothesis):

  1. Bounds. For each person, taxable ≥ 0, exempt ≤ min(max(0, compensation), 10,200), and taxable + exempt = compensation. A net repayment is neither taxable nor excluded: exempt = compensation. In 2021 nothing is excluded.
  2. Cutoff. The head's and spouse's compensation is exempt only when taxable_uc_agi < 150,000, and a dependent's only when their own dependent_taxable_uc_agi is. When it is, the exempt part is exactly min(max(0, compensation), 10,200).
  3. Differential. taxable_uc_agi = max(0, adjusted_gross_income − tax_unit_taxable_unemployment_compensation). These are two independent ways of figuring AGI without the compensation, and they agree only if the compensation is left out exactly once.
  4. Return boundary. The tax-unit total is the sum over the head and spouse. Zeroing dependents' compensation changes nothing on the filers' return: their taxable compensation, taxable_uc_agi, AGI and the tax-unit total all stay the same.
  5. Symmetry. Swapping the head's and spouse's compensation swaps their taxable compensation and leaves the total unchanged.

The property test fails on the old formulas. Hypothesis shrank to wages of 150,000 and compensation of 1, which the old code excluded.

Tests

YAML cases are worked line by line from the 2020 worksheet. On the old formulas, 11 of the 18 cases in the two unemployment test files failed and 7 passed. Two of the 11 also read dependent_taxable_uc_agi, which did not exist yet, but their taxable amounts are wrong on the old code as well. All pass now.

  • taxable_unemployment_compensation.yaml Cases 4–14:
    • the Montana repro and the case one dollar under it;
    • the IRS joint example;
    • two spouses over the cap;
    • the dependent repro;
    • a dependent over the cap;
    • a dependent whose own AGI is 150,000;
    • Social Security made taxable by the full compensation (§ 85(c)(2)(A) after § 86; Social Security Benefits Worksheet line 3, file page 31);
    • adjustments;
    • 2021;
    • a net repayment.
  • tax_unit_taxable_unemployment_compensation.yaml: the boundary cases are rewritten on the correct reading, with AGI of exactly 150,000, 149,999, and 154,999 (the last was the old file's "full exclusion" case), plus a joint return with a dependent.
  • dependent_taxable_uc_agi.yaml: the dependent's own adjustments, and Social Security taxable on the full compensation.
  • in_unemployment_compensation_deduction.yaml Cases 2–3. Case 3 fails on the old code.

Local runs: all YAML files above, plus the 2020-affected consumer files (student loan MAGI, IRA phase-out, AR, DE, MA, MT, IN, WI, RI; 73 cases); test_modified_agi_add_backs.py (13 passed); core/test_dependent_above_the_line_deductions_invariants.py (9 passed); code_health/test_rest_python_groups.py (12 passed).

Cost of the new Python test. It runs in the Rest core group. Locally on macOS: 2 tests in 7 s (28 s with the model import), 1.1 GB peak RSS. Each Hypothesis example builds one vectorized simulation, so nothing is retained between tests. These are local measurements, not Linux CI.

Consumers checked

  • Federal. irs_gross_income (filers only). taxable_ss_magi, ira_219g_magi, ira_219g_taxable_social_security, student_loan_interest_ald_magi and capped_qualified_tuition_expenses_ald all use the full compensation, as §§ 86(b)(2)(A), 219(g)(3)(A)(ii), 221(b)(2)(C) and the 2020 instructions' line 7 caution require; unchanged. dependent_gross_income overrides with the full compensation; unchanged.

  • States, 2020. MA's qualified unemployment deduction (head and spouse, capped), MT's subtraction and UT's homeowner/renter relief now read each person's correct federal amount.

  • California, Rhode Island, Wisconsin: land Leave dependents' unemployment compensation off California, Rhode Island and Wisconsin filer returns #10187 (and Leave dependents' income out of Wisconsin homestead credit household income #10191) first. ca_agi_subtractions, ri_additions and wi_unemployment_compensation_subtraction sum every member onto the filers' state return. The independent review of this PR found that once a dependent's 2020 exclusion moves to the dependent's own return, some pooled cases that happened to be right turn wrong. Example: a filer at $160,000 with a dependent's $5,000 of compensation; Rhode Island would add the dependent's excluded $5,000 to the filer's return. Leave dependents' unemployment compensation off California, Rhode Island and Wisconsin filer returns #10187 fixes those three lines. I merged its head 042b072 onto this PR's head 1bf37f4: the merge is clean, its state tests pass, and so do the cases from the reviewer's findings. After Leave dependents' unemployment compensation off California, Rhode Island and Wisconsin filer returns #10187 merges I'll merge main here and add the two 2020 cases that need this PR's dependent exclusion (California: a dependent with $150,000 of dividends; Rhode Island: a filer over the cutoff).

  • Other state lines that pool dependents (independent review, round 2). Delaware de_agi_joint, Montana mt_agi_joint, DC dc_taxable_income_joint, Iowa ia_net_income, Utah homeowner/renter and RI-1040H household income, Wisconsin homestead, and SC TANF each read a dependent's person-level compensation onto the filers' return or a household measure, or count taxable rather than received compensation. In 2020 the old pooled allocation made some of them right by accident, and this PR's per-person allocation makes a different set right by accident.

    A 2020 grid of 96 households per state checks each line against its own rule:

    • Households: joint parents with head wages 20k/80k/140k/165k and spouse compensation 0/6k/14k, plus a dependent aged 17 or a 20-year-old student with 4k/12k of compensation and 0/160k of dividends. Each runs with and without the dependent's compensation, on the old formulas and on this PR.
    • Oracle: filer-return lines don't move; household lines that count every member rise by the compensation; Wisconsin homestead (claimant and spouse) doesn't move; SC TANF rises by it / 12 a month.
    State Line Right before Right after Wrong to right Right to wrong
    DE de_agi_joint 24/96 24/96 12 12
    MT mt_agi_joint 24/96 24/96 12 12
    DC dc_taxable_income_joint 24/96 24/96 12 12
    IA ia_net_income 24/96 24/96 12 12
    PR pr_gross_income_person 48/96 96/96 48 0
    UT homeowner/renter household income 12/96 12/96 6 6
    RI ri_property_tax_household_income 24/96 24/96 12 12
    WI wi_homestead_income 12/96 48/96 42 6
    SC sc_tanf_gross_unearned_income 36/96 48/96 30 18

    Splitting each cell's absolute error (rounds 3-4 of the independent review) shows where the movement comes from:

  • 2020 charitable deduction: the $300 deduction for non-itemizers (§ 62(a)(22), Form 1040 line 10b) was taken below the line, so worksheet line 5 was short by it; Take the 2020 charitable deduction for non-itemizers above the line #10186 fixes it.

  • Count disability benefits and 2020 excluded unemployment compensation in Montana's elderly homeowner/renter credit income #10163 (stacked, open). At its head 32fc16d it no longer pins the federal allocation: Case 7 supplies taxable_unemployment_compensation as an input. I merged that head into this PR's head eabaefd and its unemployment tests pass: the add-back file (9), disability and unemployment (10), and test_mt_elderly_credit_nontaxable_income.py (13). Merge order doesn't matter.

Hub microsimulation impact (current head)

A real microsimulation on the default dataset, main at the merge base 2ea9a7d3c4 against this PR's merged head bceffce584, for 2024 (rerun 2026-10-19 after the main merge; the earlier run compared 00ce2281d0 with 9fde2d1ec4 and gave the same weighted totals). The runs went one at a time under the shared heavy-job lock. Sums are weighted.

Output 2024 change 2024 records changed (weighted)
unemployment_compensation +0 0 (0)
taxable_unemployment_compensation +0 0 (0)
tax_exempt_unemployment_compensation +0 0 (0)
tax_unit_taxable_unemployment_compensation -369.26M 27 (90,545)
taxable_uc_agi +0 0 (0)
adjusted_gross_income +0 0 (0)
in_unemployment_compensation_deduction -78 1 (0)
ca_agi_subtractions +0 0 (0)
ri_additions +0 0 (0)
wi_unemployment_compensation_subtraction +0 0 (0)
dependent_taxable_ss_magi -19 2 (1,221)
state_income_tax +0 0 (0)
income_tax +0 0 (0)
household_net_income +0 0 (0)
household_benefits +0 0 (0)

Current law, 2024. Only the tax-unit total moves: tax_unit_taxable_unemployment_compensation now leaves out dependents' compensation (27 records). Indiana's deduction moves by $78 on one record (its weight rounds to zero). Dependents' own Social Security modified AGI falls by $19 on 2 records, from netting estate losses. Person-level taxable compensation, AGI, income tax, state income tax and household net income are unchanged. The exclusion's own effect, which outside 2020 is zero, is sized in the counterfactual below.

Comparison files are in fixes/10175-impact/ on the hub host.

Counterfactual: 2020 exclusion rules on 2024 data

The default dataset holds 2024 records only, and core does not carry inputs back to earlier years. On the screen sample, 2020 unemployment_compensation and employment_income both sum to exactly 0. A 2020 pair would therefore show no change whatever the code does.

To size the fix itself, this pair runs 2024 with the 2020 exclusion parameters on both sides: gov.irs.unemployment_compensation.exemption.amount 10,200 and every cutoff 150,000, via IMPACT_REFORM_JSON. It compares main at the merge base 2ea9a7d3c4 with this PR's merged head bceffce584, on the full default dataset under the hub's heavy lock (rerun 2026-10-19; every weighted sum matches the earlier 00ce2281d0/9fde2d1ec4 run).

These are 2024 incomes, 2024 federal law apart from the exclusion, and 2024 state law on main's state lines, before #10187. They are not an estimate of 2020 itself. Sums are weighted.

Output Change Increases Decreases Records changed (weighted)
unemployment_compensation +0 +0 +0 0 (0)
taxable_unemployment_compensation +254.36M +592.83M -338.48M 86 (210,236)
tax_exempt_unemployment_compensation -254.36M +338.48M -592.83M 86 (210,236)
tax_unit_taxable_unemployment_compensation +254.36M +592.83M -338.48M 68 (154,162)
taxable_uc_agi +0 +0 +0 0 (0)
adjusted_gross_income +278.22M +592.83M -314.61M 60 (147,533)
in_unemployment_compensation_deduction -4 +0 -4 1 (0)
ca_agi_subtractions +67.91M +83.92M -16.01M 6 (36,153)
ri_additions -669,649 +0 -669,649 3 (531)
wi_unemployment_compensation_subtraction +0 +0 +0 0 (0)
dependent_taxable_ss_magi -19 +0 -38 2 (1,221)
state_income_tax +19.88M +20.84M -961,628 36 (80,744)
income_tax +96.44M +134.19M -37.75M 58 (147,533)
household_net_income -122.40M +38.72M -161.11M 58 (147,533)
household_benefits +0 +0 +0 0 (0)
  • Why taxable compensation rises. From the formulas, a filer's taxable compensation can rise only by losing the exclusion. That happens where modified AGI is $150,000 or more and the old code subtracted the compensation a second time to get under the cutoff.
  • Why it falls. For the same eligibility, the old per-person exclusion was own × 10,200 / unit total, which is never more than the new min(own, 10,200). It falls where both spouses received compensation, or where a dependent's compensation had shared the filers' cap.
  • Why AGI moves less. AGI's decreases ($314.6M) are smaller than taxable compensation's ($338.5M). The difference is dependents' compensation, which was never in the filers' AGI.
  • California and Rhode Island. California's subtraction tracks the filers' taxable compensation. Rhode Island's add-back of excluded compensation falls by $0.67M, because, on main's pooled line, dependents' exempt amounts change.

Files: ~/reviews/uc-exclusion-2020/impact/rules2020-on-2024-bceffce/ (this head) and rules2020-on-2024/ (the earlier head) on the hub host; reform JSON at ~/reviews/uc-exclusion-2020/impact/reform_2020_rules_2024.json.

axiom: TheAxiomFoundation/rulespec-us#1719 queued. us:statutes/26/85 already encodes § 85(c)(1) correctly: per-recipient $10,200 cap, joint return summed per spouse, strict <, with tests temporary_2020_exclusion_applies_per_spouse_on_joint_return and temporary_2020_exclusion_denied_at_threshold. But the § 85(c)(2) AGI and the recipient relation are free inputs. #1719 queues encoding both, with companion tests from the same worksheet cases.

Merge with main

Merged main at 2ea9a7d3c41403b702d8651e8b167792ee09285e. The conflict in taxable_unemployment_insurance.py was between #9558's modeled state unemployment insurance flow and this PR's per-person IRC 85(c) exclusion. The resolution keeps both: received = person("total_unemployment_compensation", period) uses a nonzero reported amount, including a negative repayment, and otherwise falls back to modeled state UI. Each person then receives their own exclusion, using the filers' exclusion AGI for a nondependent and the dependent's own-return exclusion AGI for a dependent:

received = person("total_unemployment_compensation", period)
eligible = where(is_dependent, dependent_eligible, filer_eligible)
excluded = eligible * min_(max_(0, received), p.amount)
return max_(0, received - excluded)

dependent_taxable_uc_agi also removes total_unemployment_compensation, so modeled UI is left out of the dependent's exclusion AGI exactly once. The shared income helper continues to apply IRC 461(l) to the dependent's own business income and losses, with their own single/separate threshold. The limit remains suspended in 2020 under the CARES Act. A new dependent-AGI case checks modeled Alabama UI and a reported-UC override: each selected total enters Social Security MAGI and is then removed from exclusion AGI exactly once.

Montana #10163. Main already routes the elderly-credit UC addback through total_unemployment_compensation. The existing expected values remain unchanged: nondependents add back UC excluded from federal AGI; an ordinary dependent's full UC already enters dependent_gross_income, so no second addback applies, regardless of the dependent's own federal exclusion. This follows MCA 15-30-2337(4), (9)(a), which counts all household members' income, including nontaxable income. A new 2020 cutoff case checks a claimant with $8,000 of excluded UC and a dependent with $150,000 of own-return exclusion AGI and $5,000 of taxable UC: Montana counts each person's UC once, with addbacks of $8,000 and $0 respectively.

The merge also trims trailing blank lines inherited from main in charitable_deduction.yaml so git diff --check is clean.

The handoff's earlier review totals cover the three-PR dependency stack. GitHub now shows #10187 merged on 2026-10-18, and its merge commit is already an ancestor of the fetched main. #10191 remains open and its branch was not merged into this update; this PR must still land after #10191. The 13 requested YAML files and both invariant modules are checked as they exist in this merged tree, without claiming to reproduce the earlier stack that included #10191.

Validation at bceffce5841b46b01d278d73d2780119e689a3f9. All 295 YAML cases pass across 31 files: 73 cases in the requested 13 files, 64 in the five #9558 UI integration files, and 158 in the 13 Montana elderly-credit files. Both invariant modules pass (2 federal plus 6 state-line tests), as do the 13 Montana nontaxable-income Python tests and four UC reader guards: 320 passing tests in total. Every test file ran separately in the foreground with the requested four-thread caps. make format, git diff --check against both merge parents, and conflict-marker checks are clean.

On untouched gh/main at the SHA above, the original PR five YAML files produced 18 failures and 16 passes. Six failures are the new dependent-AGI variable being absent; the other failures demonstrate numerical defects, including the own-return 461(l) case, the strict cutoff, per-spouse caps, dependent return boundary, and net repayments.

Impact rerun at the new head (hub, 2026-10-19). Both tables above now compare main at 2ea9a7d3c4 with merged head bceffce584. Every weighted total matches the earlier run; in the counterfactual, ca_agi_subtractions, state_income_tax and household_net_income change on two fewer records each, with the same weighted counts.

🤖 Generated with Claude Code

MaxGhenis and others added 2 commits October 12, 2026 16:36
- Compare taxable_uc_agi, which already leaves out unemployment
  compensation, with the $150,000 cutoff instead of subtracting the
  compensation from it a second time.
- Exclude up to $10,200 of each person's own compensation, so each spouse
  on a joint return excludes up to $10,200 (worksheet lines 8 and 9).
- Keep a dependent's compensation on the dependent's own return: the tax
  unit's taxable compensation sums only members who are not dependents,
  and a dependent's exclusion is tested on a new dependent_taxable_uc_agi.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Hypothesis batches of single, joint, joint-with-dependent and single-parent
tax units near the $150,000 cutoff check: per-person bounds, the cutoff on
the filers' and each dependent's own return, a differential between
taxable_uc_agi and AGI less the taxable compensation, that dependents'
compensation never moves the filers' return, and spouse symmetry; and that
nothing is excluded in 2021.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The tax unit's taxable unemployment compensation now sums only the head
and spouse, in every year, so Indiana's deduction (IC 6-3-2-10) no longer
deducts a dependent's compensation that is not in the filers' federal AGI.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review October 12, 2026 21:03
MaxGhenis added a commit that referenced this pull request Oct 12, 2026
…ment_compensation_addback; supply Case 7's taxable allocation as an input

Both review rounds noted that the helper adds more than nontaxable compensation
for a person recorded as both a dependent and the head. Case 7 no longer pins
the federal exclusion's allocation, which #10175 changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 3 commits October 12, 2026 20:43
…ts' estate losses

Address the independent review of #10175:
- California's Schedule CA subtraction and Wisconsin's unemployment
  compensation subtraction now read tax_unit_taxable_unemployment_compensation,
  and Rhode Island adds back the new tax_unit_tax_exempt_unemployment_compensation,
  so a dependent's own-return amount no longer lands on the filers' return.
- Add estate_income to the dependent helper's loss sources: limited_business_loss
  deducts estate and trust losses for filers, and the helper's own comment says
  it lists the sources loss_ald deducts. This reaches the dependent's IRC 86 and
  85(c) modified AGIs and the dependent's IRA deduction.
- Fix two form-line citations (Form 1040 line 5b, Schedule 1 line 17) and state
  that the differential invariant covers compensation that is not negative.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#10187 fixes the same three state lines
(ca_agi_subtractions, ri_additions, wi_unemployment_compensation_subtraction)
more broadly and has its own review, impact run and Axiom issues. Restore them
to main here and drop tax_unit_tax_exempt_unemployment_compensation, which only
Rhode Island used. #10175 should land after #10187, so the dependent routing
never meets the old pooled state lines.

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

Address round 2 of the independent review of #10175:
- dependent_income_and_deductions now limits a dependent's business losses
  (self-employment, farm, rental, partnership/S corporation and, since the last
  commit, estate and trust) to business income plus the IRC 461(l) threshold,
  as limited_business_loss does for the filers. The threshold is infinite while
  the limit is suspended (2018-2020), so the 2020 exclusion test keeps the whole
  loss.
- taxable_unemployment_compensation is floored at zero. irs_gross_income counts
  positive amounts only, so a net repayment never reduced gross income; a
  negative taxable amount let CA, PA and MA subtract it anyway. The exempt part
  carries the repayment, as before this PR, which Wisconsin's homestead income
  reads.
- The property test now draws net repayments, and the differential invariant
  holds for every input.

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

Copy link
Copy Markdown
Contributor Author

Hand-off to the US + core hub. Session bebaaf35, head 9fde2d1ec4.

MaxGhenis added a commit that referenced this pull request Oct 13, 2026
… in Montana's elderly homeowner/renter credit income (#10163)

* Count SSI, TANF and workers' compensation in Montana's elderly credit income

MCA 15-30-2337(9)(a) counts all nontaxable income, including (vi) cash
public assistance and relief. Schedule 2EC line 16 (2024, 2025) names SSI
and workers' compensation, and the 2014-2023 instructions list SSI
payments and workers' compensation as disability benefits. Add ssi,
tanf_person (the SPM unit's TANF per capita, so the household sum counts
it once) and workers_compensation to every period of the gross income
sources, with worked Schedule 2EC / Form 2EC cases for 2016-2025.

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

* Add property tests for government assistance in Montana's elderly credit

For a claimant, another adult and a child in four arrangements (one return
or two, one SPM unit or two, and more assistance), gross household income
adds every member's SSI and workers' compensation and the TANF once; the
credit matches an independent reading of Schedule 2EC lines 18 to 30, does
not depend on the arrangement, stays within the cap and never rises with
more assistance.

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

* Format the Montana government assistance property tests

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

* Pin SSI to zero in Montana elderly credit cases that test other income

Gross household income now counts SSI, and the model computes SSI for a
claimant aged 65 or older with little income (take-up defaults to true).
Three gross household income cases and three property-test builders are
about other items, so they now input no SSI; SSI has its own cases in the
government assistance tests.

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

* Count guaranteed income payments; move the cases to variable-named files

Review follow-ups: gi_cash_assistance is outside federal AGI and
15-30-2337(9)(a) counts all nontaxable income (a government-funded pilot
is also "any other government assistance" on Schedule 2EC line 16), so it
joins every period of the sources. The worked cases move into
mt_elderly_homeowner_or_renter_credit.yaml (and the SNAP guard into the gross
household income file), with a guaranteed income case, the 2016 worksheet
total corrected to line 10 and the references consolidated to one per
document, with the 2009 MCA text for the 2009 period.

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

* Count disability benefits and unemployment compensation outside federal AGI in Montana's elderly credit income (WIP)

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

* Test the 2020 cutoff above the band the federal test double subtracts; add changelog

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

* Add a grid invariant test: disability benefits and unemployment compensation count once in every arrangement and year

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

* Pin SSI in the one-claimant property test; format reference titles

#9900's one-claimant property test (now on main) builds claimants aged 65
or older with little income, for whom the model computes SSI, which gross
household income now counts; its reference leaves SSI out, so the builder
inputs none. Reference titles follow the repo format: no page in a
single-page title, and quoted titles ending in "#page N-M" for several.

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

* Name line 5 for disability pensions, describe the add-back's role edge, and test each list period's wiring

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

* Rename the add-back to mt_elderly_homeowner_or_renter_credit_unemployment_compensation_addback; supply Case 7's taxable allocation as an input

Both review rounds noted that the helper adds more than nontaxable compensation
for a person recorded as both a dependent and the head. Case 7 no longer pins
the federal exclusion's allocation, which #10175 changes.

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Fix the 2020 unemployment compensation exclusion: cutoff test, per-spouse cap, dependents

Summary

I checked the law before reading the code.

The statute and form.

  • 26 U.S.C. 85(c)(1), added by ARPA § 9042: "if the adjusted gross income of the taxpayer for such taxable year is less than $150,000, the gross income of such taxpayer shall not include so much of the unemployment compensation received by such taxpayer (or, in the case of a joint return, received by each spouse) as does not exceed $10,200."
  • § 85(c)(2): that AGI is figured "(A) after application of sections 86, 135, 137, 219, 221, 222, and 469, and (B) without regard to this section."
  • Conforming amendments. Pub. L. 117-2 also inserted "85(c)," into § 86(b)(2)(A) and § 219(g)(3)(A)(ii) (Cornell LII amendment notes). Taxable Social Security and the IRA limit are therefore figured with all of the compensation.
  • The worksheet. The 2020 Instructions for Form 1040 (Rev. 4-2021) implement this in the Unemployment Compensation Exclusion Worksheet, file page 88:
    • line 2: "Don't include any amount of unemployment compensation from Schedule 1, line 7";
    • line 6: "This is your modified adjusted gross income";
    • line 7: "Is the amount on line 6 $150,000 or more? The $150,000 threshold applies to all filing statuses ... Yes. Stop.";
    • lines 8 and 9: the compensation paid to you and, if married filing jointly, to your spouse, "Don't enter more than $10,200" on each line.
  • The joint example. File page 87 gives it: $20,000 and $5,000 of compensation, $15,200 excluded.

The PR matches the law on all three fixes:

  • the cutoff AGI leaves out the compensation exactly once, and the test is a strict "<" at $150,000 for every filing status;
  • each spouse's cap is their own compensation up to $10,200;
  • a dependent, who files their own return, is tested on their own § 85(c)(2) AGI and excludes their own compensation there, outside the filers' return.

Code and tests.

  • The person-level formula is vectorized and the entities and periods are right.
  • I worked 25 expectations by hand.
  • On a tree merged with today's main (479 commits ahead of the merge base), the PR's YAML files, its invariants module and the main-side guards it could trip all pass. So do the main-side tests that read the changed variables.

Open items.

  • One should: two new terms survive deletion.
  • Four suggestions.

Critical

None.

Should address

S1. Two new terms are unpinned: deleting either fails no test.

(a) The business income term in the dependent's IRC 461(l) limit.

  • Where: dependent_taxable_ss_magi.py, dependent_income_and_deductions:
    income += business_income - min_(business_loss, business_income + threshold)
  • The law: § 461(l)(3)(A) allows business deductions up to "(I) the aggregate gross income or gain of such taxpayer for the taxable year which is attributable to such trades or businesses, plus (II) $250,000".
  • Why no test catches it: the only test where the limit binds is the 2025 $700,000 estate loss case, and that dependent has no business income. The 2020 case (dependent_taxable_uc_agi.yaml Case 4) has an infinite threshold. Deleting business_income + would cap the loss at the threshold alone, and every test would still pass.
  • Fix: add a 2025 case to dependent_taxable_ss_magi.yaml.
    • Inputs: a dependent with dividend_income 400,000, rental_income 100,000 and estate_income -500,000.
    • Arithmetic: the allowed loss is min(500,000, 100,000 + 313,000) = 413,000, so modified AGI is 400,000 + 100,000 - 413,000 = 87,000.
    • Checks: I confirmed 87,000 on the merged tree. Without the term the result is 187,000.

(b) The floor on the compensation removed in dependent_taxable_uc_agi.

  • Where: dependent_taxable_uc_agi.py:
    income -= max_(0, person("total_unemployment_compensation", period))
  • Why the floor matters: the shared helper adds only positive compensation. The floor keeps the subtraction consistent with that, so a net repayment never raises worksheet line 6.
  • Why no test catches it:
    • No YAML case gives a dependent a net repayment.
    • The Hypothesis invariant 2 uses dependent_taxable_uc_agi only to decide an exclusion whose cap is 0 when compensation is negative, so it can't see the difference.
  • Fix: add a 2020 case to dependent_taxable_uc_agi.yaml.
    • Inputs: a dependent student with employment_income 12,000 and unemployment_compensation -500.
    • Expected: dependent_taxable_uc_agi 12,000, taxable_unemployment_compensation 0, tax_exempt_unemployment_compensation -500.
    • Checks: I confirmed all three on the merged tree. Without the floor, line 6 is 12,500.

Suggestions

G1. Extend the axiom line to the dependent-loss change.

  • The gap: the line covers § 85 only. The PR also changes the dependent's own-return AGI: the § 461(l) limit and netting estate or trust losses. That AGI feeds § 86(b)(2) MAGI, dependent_traditional_ira_deduction and the new § 85(c)(2) AGI, and the changelog lists the estate-loss change as a fix.
  • rulespec-us coverage: us/statutes/26 has 62 and 86 modules but no § 461 module.
  • Fix: say how parity is handled for that part, either a queued issue or a note in NYC School Tax Credit Fixed Amount #1719.

G2. Refresh the PR body before squash. The squash commit will carry it.

G3. Follow-up issue: dependent_gross_income in 2020.

  • The issue: it counts a dependent's full compensation for the § 152(d)(1)(B) gross income test, through the override in DEPENDENT_GROSS_INCOME_SOURCE_OVERRIDES. Yet § 85(c)(1) says "the gross income of such taxpayer shall not include" the excluded part.
  • Why it isn't in scope:
    • It predates this PR and is outside the diff.
    • A direct fix would cycle through is_tax_unit_dependent.
  • Why file it now: this PR computes each dependent's own exclusion, so the gap is easier to close.

G4. Invariant 2 restates the formula.

  • The issue: in test_unemployment_compensation_exclusion_invariants.py, invariant 2 is np.maximum(0, received - under * cap). CLAUDE.md asks not to test a formula against itself.
  • What earns the module its place: invariants 3 to 5 (the AGI differential, the return boundary and spouse symmetry) are independent checks.
  • Options: drop invariant 2, or derive under independently.
  • Cost: the module is cheap. At the head, Rest CI timed it at 0.49 s and 0.19 s per call, and it took 9.6 s locally.

Verified correct

Law and form.

  • § 85(c)(1)-(2) text, checked on Cornell LII.
  • Pub. L. 117-2 inserted 85(c) into § 86(b)(2)(A) and § 219(g)(3)(A)(ii).
  • Worksheet lines 1-11, file page 88.
  • The file page 87 note: the joint example, and the caution that taxable Social Security, the IRA, student loan interest and tuition deductions use the full compensation.
  • Social Security Benefits Worksheet line 3 (file page 31) includes Schedule 1 line 7 in full.
  • The PDF is the Rev. 4-2021 revision.

Code.

  • taxable_unemployment_compensation:
    • each person's own exclusion is eligible * min(max(0, received), 10_200);
    • filers are tested on taxable_uc_agi < cutoff[filing_status], a dependent on dependent_taxable_uc_agi < cutoff[SINGLE or SEPARATE];
    • the result is floored at 0.
  • Every cutoff is $150,000 in 2020. All five filing statuses are 150,000 for 2020 and 0 otherwise, and the amount is 0 outside 2020.
  • Non-dependents are the head and spouse only. is_tax_unit_dependent is ~head & ~spouse, so sum_among_non_dependents matches irs_gross_income.
  • The old double subtraction is gone. taxable_uc_agi already drops taxable_unemployment_compensation, and taxable_ss_magi uses total_unemployment_compensation, so Social Security is figured with all of the compensation, with no cycle.
  • The dependent override counts full compensation. DEPENDENT_GROSS_INCOME_SOURCE_OVERRIDES maps taxable compensation to total_unemployment_compensation, so the dependent's income includes it in full before dependent_taxable_uc_agi removes it once.
  • The new 461(l) helper keeps 2018-2020 results. It mirrors limited_business_loss, and with the infinite 2018-2020 threshold it reduces to the old netting.

Hand checks (each person's own exclusion = min(own compensation, 10,200) if their return's line 6 < 150,000):

  1. taxable_unemployment_compensation Case 4. Line 6 = 150,000 pension. Not under 150,000, so taxable 8,000 and AGI 158,000.
  2. Case 5. Line 6 = 149,999, so 8,000 is excluded; taxable 0, AGI 149,999.
  3. Case 6 (the IRS joint example). Line 6 = 60,000. The head excludes min(20,000, 10,200) = 10,200, so 9,800 is taxable; the spouse excludes all 5,000. Total 9,800; AGI 60,000 + 9,800 = 69,800.
  4. Case 7. Each spouse has 20,000 - 10,200 = 9,800 taxable. Total 19,600 = AGI.
  5. Case 8. The couple's line 6 = 30,000, so the spouse's 8,000 is excluded. The dependent's own line 6 = 4,000 - 4,000 = 0, so their 4,000 is excluded. The tax unit has 0 taxable; AGI 30,000.
  6. Case 9. The dependent's taxable amount is 15,000 - 10,200 = 4,800, all on their own return. The tax unit has 0.
  7. Case 10. The dependent's line 6 = 150,000 + 5,000 - 5,000 = 150,000, so no exclusion and 5,000 taxable. The head's line 6 = 30,000, so 0 taxable.
  8. Case 11. SS worksheet: line 3 = 140,000 + 10,000 = 150,000; plus half of benefits gives 160,000. Taxable benefits = min(0.85 × 20,000 = 17,000, 0.85 × (160,000 - 34,000) + 4,500) = 17,000. Line 6 = 157,000, so no exclusion; AGI 167,000.
  9. Case 12. Line 6 = 150,400 - 500 = 149,900, so 5,000 is excluded; AGI 149,900.
  10. Cases 14 and 15. For -500 of compensation: excluded = min(max(0, -500), 10,200) = 0; taxable = max(0, -500) = 0; exempt = -500 - 0 = -500.
  11. tax_unit_taxable_unemployment_compensation Case 4. Head 15,000 - 10,200 = 4,800; spouse 12,000 - 10,200 = 1,800; child 11,000 - 10,200 = 800, on the child's own return. Return total 6,600; AGI 40,000 + 6,600 = 46,600.
  12. dependent_taxable_uc_agi Case 2. Provisional income = 30,000 + 6,000 + 5,000 = 41,000. Taxable benefits = min(8,500, 0.85 × 7,000 + 4,500 = 10,450) = 8,500. Line 6 = 30,000 + 8,500 = 38,500.
  13. Case 3. 150,000 + 5,000 - 1,000 - 5,000 = 149,000, so 5,000 is excluded.
  14. Case 5. MAGI = 12,000 + 6,000 - 100 = 17,900 and 12,000 + 4,000 - 100 = 15,900. Line 6 = 11,900 each.
  15. dependent_taxable_ss_magi, 2025 461(l) case. Allowed loss = min(700,000, 0 + 313,000) = 313,000. MAGI = 401,000 - 313,000 = 88,000. Taxable benefits = min(17,000, 0.85 × (98,000 - 34,000) + 4,500) = 17,000.
  16. Indiana Case 2. Reduced AGI = max(0, 15,000 - 18,000) = 0, so the deduction is all 5,000.
  17. Indiana Case 3. The return holds 0 of the dependent's compensation, so the deduction is 0.
  18. Montana Case 10. The claimant's line 6 = 20,000, so all 8,000 is excluded and the addback is 8,000. The dependent's 155,000 already includes their full compensation, so their addback is 0. Gross household income = 20,000 + 8,000 + 1,800 = 29,800, where 1,800 is the 2020 recovery rebates: $1,200 CARES plus $600 CAA. The 20-year-old dependent adds no child amount.

Other checks.

  • Changelog: top-level fix-uc-exclusion-2020.fixed.md.
  • References: the amount and cutoff references corroborate $10,200 and $150,000. They give a single #page=88 with no page in the title.
  • Test files: tests sit in variable-named YAML files.
  • NH Add New Hampshire state supplementation for 2026 #10156 pattern: not applicable; there is no new state-gated key.
  • Partner files: none touched.
  • Python test cost: the PR body reports local time and memory and names the Rest core group, where it is collected automatically on main.

Axiom line assessment

Line: axiom: TheAxiomFoundation/rulespec-us#1719 queued, with an encoded-correct statement for us:statutes/26/85.

rulespec-us#1719 is valid.

  • State: OPEN and labelled pe-parity.
  • Dispatch-ready. It has:
    • the module path us/statutes/26/85.yaml and corpus citation us/statute/26/85;
    • the verbatim § 85(c) text;
    • the required outputs, which replace the free § 85(c)(2) AGI input and limit recipients to the taxpayer and spouse;
    • six companion tests (A-F) taken from the 2020 Form 1040 instructions, file pages 87-88 and 31 (YAML Cases 4-8, 11 and 12);
    • the recorded blocker: the signed encoder needs an approved billed run.
  • Matches the head: it names taxable_uc_agi, dependent_taxable_uc_agi and the person and tax-unit outputs.

The encoded-correct claim holds. us/statutes/26/85.yaml was last changed by rulespec-us #525.

  • Exclusion: each recipient's exclusion is min(max(0, unemployment_compensation), 10200), summed over the recipient relation, behind a strict < 150000.
  • Tests: temporary_2020_exclusion_applies_per_spouse_on_joint_return (12,000 and 13,000 give 20,400 excluded) and temporary_2020_exclusion_denied_at_threshold (AGI 150,000 gives 0) both exist.
  • Free inputs: the § 85(c)(2) AGI and the recipient relation are free inputs, as the line says.

Coverage: see G1 for the § 461(l) part.

CI status

All 27 checks pass at bceffce584 (run 39190151231, 2026-10-19). gh pr checks near the end of this review shows nothing pending.

That run predates several main-side guards: #10287 (source-conflict ratchet), #10317, #10408 and the #10439 Rest sharding. I checked them on the merged tree instead (see Branch status).

Branch status

Position: 7 commits ahead and 479 behind main, merge base 2ea9a7d3c4.

git merge-tree --write-tree against today's main is clean.

On the merged tree (one small run at a time), these pass:

Siblings (note only):

0 critical, 1 should and 4 suggestions are open at this head.

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.

Requesting changes: 0 critical, 1 should (two new terms are unpinned: business_income in the dependent's 461(l) limit, and the max_(0, ...) removed on UC in dependent_taxable_uc_agi; the report gives a case for each). All three fixes match § 85(c) and the 2020 worksheet, and on a tree merged with current main the #9558/#10317/#10408 guards, the ratchet and the PR's 45 cases pass. Full report in the comment above.

This branch has not been deployed

No deployments
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