Skip to content

Fix Vermont student loan interest, Utah's at-home parent credit and Wisconsin's homestead claimant - #10104

Merged
MaxGhenis merged 23 commits into
mainfrom
vt-ut-wi-statutory-fixes
Nov 10, 2026
Merged

MaxGhenis merged 23 commits into
mainfrom
vt-ut-wi-statutory-fixes

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

What changes

This PR fixes three statutory errors in Vermont, Utah and Wisconsin formulas, found while applying those states' rules to a filer who can be claimed as a dependent (#10044). They change results for households where nobody is claimed, so they go to main on their own rather than waiting on that stack.

The Vermont child tax credit fix that this PR used to carry moved to #10644, as a maintainer asked on 2026-10-27, and #10644 merged on 2026-11-08. Commit e553dc0 reverted it here.

Formula Rule now Was Source
vt_student_loan_interest_subtraction (new); gov.states.vt.tax.income.agi.subtractions; ...agi.student_loan_interest.income_limit and .in_effect (new) From 2022, Vermont subtracts the qualified education loan interest the filers paid, minus the federal student loan interest deduction, when federal AGI is at most $200,000 (joint) or $120,000 (single, head of household, married filing separately). § 5811(29)(B) sets no limit for a qualifying surviving spouse, so a surviving spouse gets no subtraction. IN-112 line 16c gives "All other filers" $120,000; this PR follows the statute and records the conflict in source_conflicts/candidates.yaml as us-vt:student-loan-interest-surviving-spouse-limit. There's no subtraction in 2021. The 2021 list keeps every other entry main has, vt_other_subtractions included. The raw interest was subtracted from 2021, deducting it a second time for anyone who took the federal deduction, with no income limit 32 V.S.A. § 5811(21)(B)(vi) and (29)(B); 2021, No. 138 (Adj. Sess.), §§ 5, 13(b) (Sec. 5 on PDF pages 4-5; Sec. 13(b), on PDF page 15, applies it to taxable years beginning on and after Jan. 1, 2022); 32 V.S.A. § 5822(a); 2025 Schedule IN-112, lines 16a–16c and instructions
ut_at_home_parent_credit_potential $100 per qualifying child per return when the claimant or the joint-filing spouse is an at-home parent $100 per child multiplied by the number of spouses who qualified Utah Code § 59-10-1005(2), versions effective 3/24/2022 and 5/6/2026, which read the same: $100 "for each qualifying child" when "the claimant or another claimant filing a joint individual income tax return with the claimant is an at-home parent"
wi_homestead_eligible One spouse, the claimant, must be 18 or older; 62 or older, disabled or with earned income; and not claimed as a dependent on another return unless 62 or older Age, disability and age 62 were tested across the head and spouse, and earned income across every member of the tax unit, dependents included. The dependency bar was never read. Wis. Stat. § 71.53(2): (c) (claimant under 18 at the close of the year) and (d) (claimant claimed as a dependent, unless 62 or older); § 71.54(1)(dm), (em) and (gm) (age, disability and earned income); § 71.52(1) (claimant) and § 71.52(1g) (earned income); 2025 Schedule H instructions, Steps 1 and 2a (p. 4), the earned income definition (section F #4, p. 7) and the note on questions 1c-1d (p. 10)

Vermont's federal offset. Under § 5811(21)(B)(vi), Vermont subtracts "the amount of interest paid by a qualified resident taxpayer during the taxable year on a qualified education loan" for attendance at an eligible educational institution. Section 5811(21)(B) subtracts its items "to the extent such income is included in federal adjusted gross income", and interest already deducted federally is not. Read with that lead-in and the no-double-deduction canon in CONTRIBUTING.md, the subtraction covers only the interest left after the federal deduction. The filer computes that amount on IN-112 lines 16a to 16c (16b: "Enter any student loan interest already deducted on federal Form 1040, Schedule 1, Line 21").

Examples (2025 unless noted)

  • Vermont, single, wages $10,000, interest paid $1,000: the federal deduction already takes the $1,000, so Vermont subtracts nothing more and Vermont AGI is $9,000 (was $8,000). With $4,000 of interest at $50,000 of wages, the federal deduction is capped at $2,500, so Vermont subtracts $1,500 and Vermont AGI is $46,000. Single at $125,000 of AGI: no subtraction. Single at exactly $120,000, or joint at exactly $200,000 (2022): the whole $3,000. In 2021: no subtraction.
  • Vermont, surviving spouse with a 10-year-old: at $110,000 of wages and $3,000 of interest, no federal deduction and no Vermont subtraction (the form would give $3,000); at $50,000 and $4,000, a $2,500 federal deduction and no Vermont subtraction (the form would give $1,500).
  • Vermont, 2021, $1,300 of other IN-112 subtractions (vt_other_subtractions): Vermont subtracts $1,300, as on main.
  • Utah, both spouses earning $3,000 or less, one newborn: $100 (was $200). With two children: $200 (was $400).
  • Wisconsin, a couple aged 30 where the only earner is claimed on a parent's return: not eligible (was eligible). A claimed claimant aged 62 or older stays eligible. An 18-year-old with no earnings married to a 17-year-old earner: not eligible (was eligible). A 40-year-old parent with no earnings whose 19-year-old dependent earns $3,000: not eligible, since the dependent can't be the claimant.

Readings

  1. Wisconsin homestead, earned income. Schedule H Step 1a lists "earned income" for "you (or your spouse, if married)" without the claimant condition it attaches to age and disability. The earned income definition is section F Implement Section 8 Project-Based Rental Assistance #4 (instructions p. 7). This PR follows the note on page 10: "The spouse that meets this definition must be the claimant." That matches the statute. Wisconsin applies the tests in §§ 71.53(2)(c) and (d) and 71.54(1)(em) and (gm) to the claimant, and counts as earned income only the claimant's own wages and net self-employment earnings (§ 71.52(1g)). The age route in (dm) also counts the claimant's spouse; the result is the same, because a spouse 62 or older can always be the claimant. When two members of a household can each qualify, "they may determine between them as to who the claimant is" (§ 71.52(1)). Under the household reading, a couple where only the claimed spouse earns would qualify.
  2. "Claimed" vs "can be claimed". Wisconsin bars a claimant who "was claimed as a dependent for federal income tax purposes by another person during the year to which the claim relates", unless 62 or older (§ 71.53(2)(d)). In Step 2a of Schedule H, the claimant answers whether someone claimed or will claim them. So the formula reads claimed_as_dependent_on_another_return. A dependent claimed on the same return isn't a filer, so the formula's filer guard keeps that dependent from being the claimant.
  3. Vermont, qualifying surviving spouse. Vermont sets the § 5811(29)(B) limit for four filing statuses only: "$120,000.00 if the individual's filing status is single, head of household, or married filing separately" and "$200,000.00 if the individual's filing status is married filing jointly". Wherever the chapter includes surviving spouses, the General Assembly names them (§ 5811(21)(C)(i) and (ii), § 5822(a)(1) and (3)). For example, it gave one standard deduction to filers "unmarried (other than surviving spouses or heads of households)" or filing separately (§ 5811(21)(C)(ii)(I)), and one rate schedule to "Married individuals filing joint returns and surviving spouses" (§ 5822(a)(1)). The act that added (29)(B) did the same: its Sec. 7 replaced "qualifying widow or widower" with "surviving spouse" in § 5830e(a)(1)'s list, which now reads "single, married filing separately, head of household, or surviving spouse". So a surviving spouse falls outside all four statuses, and outside the definition of a qualified resident taxpayer. The statute isn't silent, so the form's "All other filers" $120,000 has no gap to fill. income_limit.yaml gives SURVIVING_SPOUSE -.inf; vt_student_loan_interest_subtraction is its only reader, and its agi <= limit test is false for every surviving spouse. The conflict is candidate us-vt:student-loan-interest-surviving-spouse-limit (follows: statute, state: ruled); the parameter's comment is counted in KNOWN_UNMARKED until the marker lands in the follow-up PR.
  4. Wisconsin, age, disability and earned income as alternatives. The formula lets a claimant qualify by being 62 or older, by being disabled or by having earned income, as Schedule H Step 1a does and as main did. Section 71.54(1)(gm), read by itself, would deny a claimant with no earned income unless the claimant is both disabled and 62 or older. Read with (dm), which makes (em) and (gm) exceptions to its age bar, the three paragraphs are alternatives. The candidate us-wi:homestead-age-disability-earned-income-routes records this (follows: statute, state: open). The register step after merge records it as ruled. Unit tests 4 (aged 62, no earnings) and 6 (a disabled spouse aged 60, no earnings) in wi_homestead_eligible.yaml pin the reading.

Invariants

policyengine_us/tests/core/test_vt_ut_wi_statutory_invariants.py checks each one over Hypothesis-drawn households:

  1. Vermont, from 2022: subtraction + federal deduction = the filers' interest paid when AGI is within the statute's $200,000 or $120,000 limit. The model gives no subtraction above the limit or to any surviving spouse, and never a negative one. The property draws single, joint and surviving spouse returns in 2022, 2024 and 2026, and the YAML cases pin 2021.
  2. Utah: the credit doesn't change when a second spouse also qualifies as an at-home parent.
  3. Wisconsin: eligibility is the same whichever spouse is labelled head, and marking a spouse as claimed never makes a return eligible.

The file runs in the Rest Python group. At dea8148b78 it ran in Rest (Python shard 4), where its three tests took 0.31 s (Vermont), 0.32 s (Utah) and 3.15 s (Wisconsin). Locally (Python 3.14, 4 vCPU), the file took 73 s of wall time with import and model build before this round's change and 71 s after, with a 1,510 MB peak both times.

Tests

  • At dea8148b78, one process each: vt_student_loan_interest_subtraction.yaml: 16 passed. vt_other_subtractions.yaml: 3 passed. wi_homestead_eligible.yaml: 25 passed. ut_at_home_parent_credit.yaml: 5 passed. test_vt_ut_wi_statutory_invariants.py: 3 passed. test_source_conflicts.py: 103 passed.
  • Mutants, each run in its own process and then restored. The M5 to M9 rows ran at dea8148b78; M1, M2 and M4 ran at 6f203e8ba3 against the 15-case Vermont file; the other rows ran against the earlier 12-case file:
Mutant Changed YAML file Property test Killed by
R1 VT: subtract the whole interest 3 of 12 fail fails YAML and property
R2 VT: no income limit 4 of 12 fail fails YAML and property
R3 UT: one credit per qualifying spouse 2 of 5 fail fails YAML and property
R4 WI: no dependency bar 4 of 24 fail passes YAML
R5 WI: any tax unit member's earnings 6 of 24 fail passes YAML
R6 WI: earnings summed across both spouses 5 of 24 fail passes YAML
R7 VT: < instead of <= 2 of 12 fail (the exact $120,000 and $200,000 cases) passes YAML
R8 VT: SURVIVING_SPOUSE $200,000 2 of 12 fail (both surviving-spouse cases: 3,000 and 1,500 vs 0) fails YAML and property
R9 WI: 18-or-older tested across the couple 2 of 24 fail passes YAML
R10 VT: dependents' interest counted 1 of 12 fails (1,000 vs 0) passes YAML
O1 VT: SURVIVING_SPOUSE $120,000 (the form's reading) 2 of 12 fail (3,000 and 1,500 vs 0) fails (rerun at dea8148b78: fails at assert not sub[surviving].any()) YAML and property
O2 VT: no 2022 effective-date gate 1 of 12 fails (1,500 vs 0, 2021) passes YAML
M1 VT: HEAD_OF_HOUSEHOLD -.inf 1 of 15 fails (the head of household case at $120,000: 0 vs 3,000) passes YAML
M2 VT: SEPARATE -.inf 1 of 15 fails (the married filing separately case at $120,000: 0 vs 3,000) passes YAML
M4 VT: HEAD_OF_HOUSEHOLD $200,000 1 of 15 fails (the head of household case at $125,000: 3,000 vs 0) passes YAML
M5 VT: no vt_other_subtractions in the 2021 list vt_other_subtractions.yaml: 1 of 3 fails (Case 3, 2021: 0 vs 1,300) not run YAML
M6 VT: no vt_student_loan_interest_subtraction in the 2022 list 1 of 16 fails (the $4,000 case's vt_subtractions: 0 vs 1,500) not run YAML
M7 WI: no filer guard 1 of 25 fails (the 19-year-old dependent case: true vs false) not run YAML
M8 WI: (gm) read by itself (no earned income needs both disabled and 62 or older) 4 of 25 fail (unit tests 4 and 6, Cases 14 and 15) not run YAML
M9 VT: SEPARATE $200,000 1 of 16 fails (the married filing separately case at $125,000: 3,000 vs 0) not run YAML

None survives.

  • CI: the Pull request run at 721449e5a0 (42758648036) passed all 36 jobs. It tested this head merged into main at ee466893c3. 721449e5a0 changes only a note in source_conflicts/candidates.yaml.

Overlap with open PRs

Microsimulation impact

We estimate that the Vermont student loan interest fix raises Vermont income tax by $0.72m (0.05%) in 2026 on the default dataset, across 19 sampled tax units (11,687 weighted) and by at most $272 each. We compared main at ba9bfdea75 with this branch at dea8148b78. The commit after dea8148b78 changes only a note in the source-conflict backlog.

In Wisconsin, 5 sampled tax units (4,193 weighted) lose homestead eligibility. One of them, with a weight under 1, loses a $445 credit, so the weighted credit total falls by $1. No Utah output changes. Federal income tax and household benefits don't change.

Output Main total Change Records changed (weighted)
vt_income_tax $1.45bn +$717,276 19 (11,687)
wi_homestead_eligible (tax units) 437,127 −4,193 5 (4,193)
wi_homestead_credit $74.0m −$1 1 (under 1)
wi_income_tax $11.71bn +$1 1 (under 1)
ut_at_home_parent_credit_potential $443,594 $0 0 (0)
ut_income_tax $6.92bn $0 0 (0)
state_income_tax $577.3bn +$717,278 20 (11,687)
income_tax $2,595.3bn $0 0 (0)
household_net_income $17,198.7bn −$717,274 19 (10,416)
household_benefits $2,060.5bn $0 0 (0)

This PR adds vt_student_loan_interest_subtraction, so main has no total to compare.

impact: Vermont income tax +$0.72m (+0.05%) in 2026 (19 sampled tax units, 11,687 weighted); 5 sampled Wisconsin tax units (4,193 weighted) lose homestead eligibility, with $1 less credit in total; no Utah output changes
axiom: TheAxiomFoundation/rulespec-us#2029 queued (Vermont student loan interest; corpus re-pin and source review); TheAxiomFoundation/rulespec-us#2030 queued (Utah at-home parent credit; historical corpus ingestion and re-pin); TheAxiomFoundation/rulespec-us#2031 queued (Wisconsin homestead claimant; statutory-source verification and corpus re-pin). All await encoder approval.

🤖 Generated with Claude Code

MaxGhenis and others added 5 commits October 11, 2026 12:21
32 V.S.A. 5811(21)(B)(vi) (2022 Act 138, from 2022) subtracts the interest a
qualified resident taxpayer paid on a qualified education loan, for AGI up to
$200,000 joint or $120,000 otherwise (5811(29)(B)). Schedule IN-112 line 16
subtracts the interest paid (16a) less the amount "already deducted on
federal Form 1040, Schedule 1" (16b). The model subtracted the raw interest
from 2021, deducting it a second time for anyone who took the federal
deduction, with no income limit; the 2021 IN-112 has no such line.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
32 V.S.A. 5830f(b) reduces "the amount of the credit per child" by $20 for
each $1,000 of AGI over $125,000; the model reduced the total once, so two
young children at $150,000 got $1,500 instead of $1,000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Utah Code 59-10-1005(2) allows $100 "for each qualifying child if ... the
claimant or another claimant filing a joint individual income tax return with
the claimant is an at-home parent". The model multiplied the credit by the
number of spouses earning $3,000 or less; the existing two-parent case
asserted $400 for two children and now expects $200.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The Schedule H instructions put each condition on the claimant: 18 or older;
62 or older, disabled or with earned income ("The spouse that meets this
definition must be the claimant"); and not claimed as a dependent on another
person's federal return unless 62 or older (Wis. Stat. 71.53(2)(d); Step 2a).
Spouses choose the claimant. The model tested age and earnings across the
household and never read the dependency bar. The existing cases now use
person inputs.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 11, 2026
…split

The Vermont student loan subtraction, the Vermont child tax credit per-child
phase-out, Utah's at-home parent credit once per return and Wisconsin's
homestead claimant tests change results for households where nobody is
claimed, so they now go to main in #10104; this
branch keeps the base formulas there.

New cases: a filer who can be claimed but is not claimed (no Wisconsin $250
exemption; still a Rhode Island relief and Vermont renter credit claimant),
and a would-be claimant who meets the filing exception (South Carolina,
Utah, Vermont and West Virginia dependents survive). The property test now
draws claimability, actual claims and the filing exception per spouse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The subtraction list already omits the variable in 2021, but the variable
itself returned a positive amount when computed for 2021. An in_effect
parameter (false before 2022) makes it zero there. The Act 138 references now
cite Sec. 5 (the subtraction, pages 4-5) and Sec. 13 (effective retroactively
on January 1, 2022, page 15); the earlier reference named Sec. 4.

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

Copy link
Copy Markdown
Collaborator

@MaxGhenis — this draft contains the fix for #9996: 32 V.S.A. § 5830f(b) reduces "the amount of the credit per child", and the 2025 Vermont booklet's table reaches $0 per child at $174,001, but vt_ctc applies the $20-per-$1,000 reduction once per return. On main (4c65c82) a two-child case still gives 1,900 / 2,900 / 900 where the statute gives 1,800 / 2,700 / 0.

The draft does not link #9996 and bundles three other state fixes. Could you split the Vermont CTC change into its own PR that says "Fixes #9996", or add the link here? We will not open a parallel PR.

🤖 Posted by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

A maintainer asked for the per-child phase-out under 32 V.S.A. 5830f(b) as
a separate PR that fixes #9996; #10644 now carries it with its own tests.
This reverts 2f79c67 and drops the matching property and changelog text,
so this PR holds only the Vermont student loan, Utah and Wisconsin fixes.

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

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub, continuing an earlier session's work. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

@MaxGhenis MaxGhenis changed the title Fix Vermont student loan interest and CTC phase-out, Utah's at-home parent credit and Wisconsin's homestead claimant Fix Vermont student loan interest, Utah's at-home parent credit and Wisconsin's homestead claimant Nov 3, 2026
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 99ae1e525a242067b577de6533889ac6a397eff5

I continue an earlier session's work. That session pushed nothing here, so the starting point was the author's head, 7ae084b.

What the items asked for, and how it's resolved

  • Remove the Vermont CTC change with a normal commit. Commit e553dc02 reverts 2f79c67 (vt_ctc.py and vt_ctc.yaml now match main). It also drops the CTC property test_vt_ctc_phase_out_is_per_child and its docstring entry from policyengine_us/tests/core/test_vt_ut_wi_statutory_invariants.py, and removes the CTC clause from changelog.d/vt-ut-wi-statutory-fixes.fixed.md. Phase out Vermont's child tax credit per child #10644 carries the fix now (Fixes #9996). No CTC file is left in this PR's diff against main.
  • Still needed? Yes. Main at abc67b9 has no vt_student_loan_interest_subtraction, still lists raw student_loan_interest in Vermont's subtractions, and still has the per-parent Utah multiplier and the household-wide Wisconsin test. No open PR duplicates any of the three fixes. See Overlap below.
  • Merge main. Commit 99ae1e52 merges origin/main (abc67b9) with no conflicts.
  • Reviews. There are no reviews or inline comments. The only issue comment besides the pick-ups is the 2026-10-27 request to split out the CTC, which is now done.
  • Body. Rewritten without the CTC and without a local path in the axiom line. It carries impact: the hub will measure and axiom: (hub files pe-parity before landing). The title drops "and CTC phase-out".
  • Changelog. The fragment existed; I only edited it.

Fixes kept (unchanged by me)

  • VT: policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py:21-42, the parameters agi/student_loan_interest/{in_effect,income_limit}.yaml, and agi/subtractions.yaml (2022+ list).
  • UT: policyengine_us/variables/gov/states/ut/tax/income/credits/ut_at_home_parent_credit_potential.py:24-38.
  • WI: policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py:9-39.

Hand checks of the YAML cases, done from the rules as the PR states them:

  • VT: $4,000 of interest at $50,000 of wages. The federal deduction is capped at $2,500, so Vermont subtracts $1,500.
  • UT: two at-home spouses with one newborn get 1 × $100.
  • WI: a 30-year-old claimed earner isn't an eligible claimant; at 62 the dependency bar lifts.

Tests (this session, at the merged head)

  • Changed files: vt_student_loan_interest_subtraction.yaml 7 passed, ut_at_home_parent_credit.yaml 5 passed, wi_homestead_eligible.yaml 15 passed, test_vt_ut_wi_statutory_invariants.py 3 passed, code_health/test_source_conflicts.py 103 passed.
  • Dependents: vt_other_subtractions.yaml 2, vt_taxable_income.yaml 4, VT integration.yaml 3, wi_homestead_credit.yaml 4, wi_homestead_property_tax.yaml 1, wi_retirement_income_exclusion_elected.yaml 13, WI integration.yaml 1. All passed.
  • ruff format and ruff check on the edited test file: clean.

Mutants (each in its own process, then restored)

Mutant Failed
VT: subtract the whole interest (no federal offset) 3 of 7
VT: no income limit 2 of 7
UT: one credit per qualifying spouse 2 of 5
WI: no dependency bar 4 of 15
WI: any household member's earnings qualify 2 of 15

CI

The PR is a draft, so CI skips it. The Pull request run at 99ae1e5 concluded skipped (https://github.com/PolicyEngine/policyengine-us/actions/runs/41720633512), so it hasn't run.

Overlap with open PRs

Judgment calls and limits

  • I couldn't fetch any primary source. The network policy blocks legislature.vermont.gov, taxsim.nber.org, le.utah.gov, revenue.wi.gov and web.archive.org, so I couldn't recheck the original author's quotes. The rewritten body paraphrases and links the sources instead of quoting them.
    • One thing to check before landing: whether 32 V.S.A. § 5811(21)(B)(vi) itself limits the subtraction to interest not deducted federally, or only IN-112 line 16b does. If only the form does, that's a statute-vs-form question for CONTRIBUTING.md#source-conflicts. Since federal AGI already excludes the deducted interest, subtracting it again would double-count, so I expect the readings agree. I couldn't confirm that.
  • The two Wisconsin readings (page 10's "earning spouse must be the claimant" over the Step 1a household wording, and "claimed" over "claimable") stay as the author chose them. Both are listed under Readings in the body. Neither pits a statute against a form as I read them, so there's no # Source conflict marker; test_source_conflicts.py passes.
  • I left the WI and VT formulas themselves unchanged. My changes are the CTC removal, the merge, and the body and title.

Proposed follow-up

None.


Generated by Claude Code

@MaxGhenis
MaxGhenis marked this pull request as ready for review November 3, 2026 21:30
@MaxGhenis

MaxGhenis commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Superseded by the final review: #10104 (comment) (VERDICT: REQUEST_CHANGES at 99ae1e525a242067b577de6533889ac6a397eff5).


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: REQUEST_CHANGES
99ae1e5
METHODOLOGY_CHOICE: yes

The head hasn't moved since the hand-off: 99ae1e525a is still the head, and its last commit is the 20:15Z merge of main.

All three fixes are right on the hub's reading of the sources, and each defect reproduces at main. CI is green at the head. I'm asking for changes for three reasons:

  • The PR follows IN-112's $120,000 for a qualifying surviving spouse, where 32 V.S.A. § 5811(29)(B) names no amount, and it doesn't record that conflict as CONTRIBUTING.md requires.
  • Four mutants survive, including the hub's < and surviving-spouse mutants.
  • Two comments, and the body, state the law wrongly.

Each fix below is small, and I ran the full set on a scratch copy: the guard passes 103, the VT file 11 and the WI file 16.

Sources. This VM's network policy refused every source with a proxy 403 on CONNECT: legislature.vermont.gov, taxsim.nber.org, le.utah.gov, docs.legis.wisconsin.gov, revenue.wi.gov, govinfo.gov, uscode.house.gov, ecfr.gov and web.archive.org. Every legal reading below rests on the hub's reading of 2026-11-03, quoted in the review brief. I quote nothing from memory.

Findings

P2-1. Vermont's surviving-spouse limit follows the form over the statute, and the PR doesn't record the conflict

policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml:22-24 gives SURVIVING_SPOUSE $120,000, citing IN-112 line 16c's "All other filers".

On the hub's reading, § 5811(29)(B) defines a "qualified resident taxpayer" by two limits: (i) "$120,000.00 if the individual's filing status is single, head of household, or married filing separately", and (ii) "$200,000.00 if ... married filing jointly". The definition lists those statuses and no others. Read literally, a qualifying surviving spouse never meets it and gets no subtraction at any income. Line 16c gives the surviving spouse $120,000. The two texts give different answers, the canon that definitions control favours the statute, and the PR follows the form. That's a source conflict under CONTRIBUTING.md#source-conflicts.

Failure scenario: a 2025 surviving spouse with $110,000 of federal AGI and $3,000 of interest (none deducted federally, since MAGI is above the $100,000 single end) gets a $3,000 subtraction. A literal reading of (29)(B) gives $0. I ran this case (W5) at the head: vt_student_loan_interest_subtraction 3,000.

The brief asked for a # Source conflict marker and a register entry. CONTRIBUTING.md (step 1, and "The marker comes only in the follow-up PR") has the ruling PR add a source_conflicts/candidates.yaml item and no marker, because the guard rejects a marker whose id isn't in the pinned register. So this PR should add the item below, keep $120,000 until the hub rules, and leave the marker to the follow-up. Insert it after us-vt:snap-child-support-deduction:

- id: us-vt:student-loan-interest-surviving-spouse-limit
  program: income-tax
  years:
    from: 2022
    type: tax
  question: Does a qualifying surviving spouse get Vermont's student loan interest subtraction, when 32 V.S.A. §
    5811(29)(B) sets an income limit only for single, head of household, married filing separately and joint filers?
  sources:
  - kind: statute
    citation: 32 V.S.A. § 5811(21)(B)(vi), (29)(B)
    url: https://legislature.vermont.gov/statutes/section/32/151/05811
  - kind: form
    citation: 2025 Vermont Schedule IN-112, line 16c
    url: https://taxsim.nber.org/historical_state_tax_forms/VT/2025/Income-Booklet-2025.pdf#page=23
  readings:
    statute: (29)(B) defines a qualified resident taxpayer by a $120,000 limit for single, head of household and
      married filing separately filers and a $200,000 limit for joint filers. It names no amount for a qualifying
      surviving spouse, so a surviving spouse doesn't meet the definition and gets no subtraction.
    other: Line 16c applies $120,000 to "All other filers", a qualifying surviving spouse included.
  example: A 2025 surviving spouse with $110,000 of federal AGI and $3,000 of student loan interest, none deducted
    federally, subtracts $3,000 under line 16c and nothing under a literal reading of (29)(B).
  pe_us:
    follows: conflicting-source
    code:
    - policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml
    prs:
    - https://github.com/PolicyEngine/policyengine-us/pull/10104
  state: open
  note: The PR keeps the form's $120,000 until the hub rules. Code not yet on main.

Step 1 also asks for the YAML case that pins the reading: that's the surviving-spouse case in P2-2. I checked the item with uv run pytest -q -p no:cacheprovider policyengine_us/tests/code_health/test_source_conflicts.py on a scratch copy: 103 passed.

The federal offset is not a second conflict. (21)(B)'s "to the extent such income is included in federal adjusted gross income" qualifies "items of income". Interest paid isn't an item of income, so that clause has nothing to act on and doesn't produce the offset. Read alone, (vi) subtracts "the amount of interest paid". But CONTRIBUTING.md lists the canon "no double deduction without a clear statement", and (vi) has no clear statement that allows a second deduction of interest already kept out of federal AGI. Read with that canon, the statute gives the same result as lines 16a to 16c. That's one reading, so it needs no register entry. The body should credit the offset to the canon and the form, not to the statute's words (P2-4).

P2-2. Four mutants survive: Vermont's <= boundary, the surviving-spouse limit, the age test and dependents' interest

I reran the hand-off's five mutants, then the hub's three and two of my own, each in its own process on a scratch copy, and restored the code after each. git status was clean at the end.

Mutant Changed YAML file Property test Result
VT: subtract the whole interest 3 of 7 fail fails killed
VT: no income limit 2 of 7 fail fails killed
UT: one credit per qualifying spouse 2 of 5 fail fails killed
WI: no dependency bar 4 of 15 fail passes killed
WI: any household member's earnings 2 of 15 fail passes killed
WI: earnings summed across both spouses (hub) 2 of 15 fail passes killed by the two "only the claimed spouse has earned income" cases
VT: < instead of <= (hub) 7 of 7 pass passes survives
VT: SURVIVING_SPOUSE $200,000 (hub) 7 of 7 pass passes survives
WI: 18-or-older tested across the couple, not the claimant (mine) 15 of 15 pass passes survives
VT: dependents' interest counted (mine) 7 of 7 pass passes survives

The command per mutant was uv run python -m policyengine_core.scripts.policyengine_command test <changed yaml> -c policyengine_us, then uv run pytest -q -p no:cacheprovider policyengine_us/tests/core/test_vt_ut_wi_statutory_invariants.py -k <state>.

Failure scenarios that no test catches:

  • <: a single filer at exactly $120,000 of AGI loses the subtraction, against "equal to or less than".
  • $200,000: a surviving spouse at $150,000 keeps it.
  • Age across the couple: an 18-year-old with no earnings, married to a 17-year-old earner, counts as eligible. Main gives this couple eligible (W8 below), so this PR fixes it, but nothing pins the fix.
  • Dependents' interest: a dependent's interest flows into the parent's subtraction.

The property test can't catch the first two. It hard-codes the limit as np.where(joint, 200_000, 120_000), never draws a surviving spouse, and with integer wage draws and max_examples=3 it doesn't land on exactly $120,000.

Add these four cases at the end of policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml:

- name: 2025 single filer at exactly $120,000 of adjusted gross income keeps the Vermont student loan interest subtraction
  period: 2025
  input:
    people:
      filer:
        age: 30
        employment_income: 120_000
        student_loan_interest: 3_000
    tax_units:
      tax_unit:
        members: [filer]
    households:
      household:
        members: [filer]
        state_code: VT
  output:
    # 32 V.S.A. 5811(29)(B)(i): AGI "equal to or less than" $120,000. The
    # federal deduction is fully phased out above $100,000 of MAGI, so 16a $3,000
    # - 16b $0 = $3,000.
    adjusted_gross_income: 120_000
    student_loan_interest_ald: 0
    vt_student_loan_interest_subtraction: 3_000

- name: 2022 joint return at exactly $200,000 of adjusted gross income keeps the Vermont student loan interest subtraction
  period: 2022
  input:
    people:
      head:
        age: 30
        employment_income: 200_000
        student_loan_interest: 3_000
      spouse:
        age: 30
        is_tax_unit_spouse: true
    tax_units:
      tax_unit:
        members: [head, spouse]
        filing_status: JOINT
    households:
      household:
        members: [head, spouse]
        state_code: VT
  output:
    # 5811(29)(B)(ii): AGI "equal to or less than" $200,000 if married filing
    # jointly. The 2022 federal deduction ends at $175,000 of joint MAGI.
    adjusted_gross_income: 200_000
    student_loan_interest_ald: [0, 0]
    vt_student_loan_interest_subtraction: 3_000

- name: 2025 surviving spouse with $150,000 of adjusted gross income gets no Vermont student loan interest subtraction
  period: 2025
  input:
    people:
      head:
        age: 40
        employment_income: 150_000
        student_loan_interest: 3_000
      child:
        age: 10
    tax_units:
      tax_unit:
        members: [head, child]
        filing_status: SURVIVING_SPOUSE
    households:
      household:
        members: [head, child]
        state_code: VT
  output:
    # Schedule IN-112 line 16c: "All other filers, if AGI is greater than
    # $120,000, enter -0-." 5811(29)(B) names no amount for this status.
    vt_student_loan_interest_subtraction: 0

- name: 2025 interest paid by a dependent is not part of the filer's Vermont student loan interest subtraction
  period: 2025
  input:
    people:
      head:
        age: 50
        employment_income: 60_000
      student:
        age: 22
        is_full_time_student: true
        is_tax_unit_dependent: true
        student_loan_interest: 1_000
    tax_units:
      tax_unit:
        members: [head, student]
    households:
      household:
        members: [head, student]
        state_code: VT
  output:
    # 5811(21)(B)(vi): interest "paid by a qualified resident taxpayer"; the
    # dependent is not the taxpayer on this return.
    student_loan_interest_ald: [0, 0]
    vt_student_loan_interest_subtraction: 0

Add this case at the end of policyengine_us/tests/policy/baseline/gov/states/wi/tax/income/wi_homestead_eligible.yaml:

- name: "2025 joint return where the only earner is 17 and the 18-year-old spouse has no earned income: no Wisconsin homestead claimant"
  period: 2025
  input:
    people:
      head:
        age: 18
      spouse:
        age: 17
        employment_income: 5_000
        is_tax_unit_spouse: true
    tax_units:
      tax_unit:
        members: [head, spouse]
        filing_status: JOINT
        wi_homestead_income: 9_000
    households:
      household:
        members: [head, spouse]
        state_code: WI
  output:
    # Wis. Stat. 71.53(2)(c) bars a claimant under 18, and 71.54(1)(gm) bars a
    # claimant under 62 who is not disabled and has no earned income. Neither
    # spouse meets both alone.
    wi_homestead_eligible: false

All five pass at the head: 4 passed (VT) and 1 passed (WI). They kill the four survivors:

  • <: 2 of 4 VT cases fail ([0.] differs from 3000.0 at 2025 and 2022).
  • $200,000: 1 of 4 fails ([3000.] differs from 0.0).
  • Dependents' interest: 1 of 4 fails ([1000.] differs from 0.0).
  • Age across the couple: the WI case fails ([1.] differs from 0.0).

Merged into the files, VT passes 11 and WI 16.

P2-3. A code comment misstates § 5811(29)(B)

policyengine_us/variables/gov/states/vt/tax/income/adjusted_gross_income/subtractions/vt_student_loan_interest_subtraction.py:35-36 says AGI is "at most $200,000 if married filing jointly and $120,000 otherwise". The statute gives $120,000 only to single, head of household and married filing separately filers. "Otherwise" hides the surviving-spouse gap that P2-1 records. Replace the two lines with:

        # 5811(29)(B): a qualified resident taxpayer's adjusted gross income is
        # at most $120,000 if single, head of household or married filing
        # separately, and $200,000 if married filing jointly. It names no
        # amount for a qualifying surviving spouse; income_limit.yaml applies
        # IN-112 line 16c's $120,000 for "All other filers".

This wording avoids the guard's conflict phrases, so KNOWN_UNMARKED doesn't change. With it in place, test_source_conflicts.py passes 103 and ruff format --check reports the file already formatted.

P2-4. The body states several rules the sources don't support

Each item gives the text to replace and its replacement.

  • VT row, "Rule now": "when AGI is at most $200,000 (joint) or $120,000 (other filers)". Replace with: "when federal AGI is at most $200,000 (joint) or $120,000 (single, head of household, married filing separately). § 5811(29)(B) names no amount for a qualifying surviving spouse. The code follows IN-112 line 16c's $120,000 for "All other filers", a source conflict recorded in source_conflicts/candidates.yaml as us-vt:student-loan-interest-surviving-spouse-limit."
  • VT offset. Add: "§ 5811(21)(B)(vi) subtracts "the amount of interest paid by a qualified resident taxpayer during the taxable year on a qualified education loan". Read with the no-double-deduction canon in CONTRIBUTING.md, that is the interest left after the federal deduction, which is what IN-112 lines 16a to 16c compute ("Enter any student loan interest already deducted on federal Form 1040, Schedule 1, Line 21")."
  • VT effective date. The body cites "2022 Act 138, Sec. 5 (Sec. 13, p. 15, makes it effective January 1, 2022)". On the hub's reading, the § 5811 history note lists "2021, No. 138 (Adj. Sess.), §§ 5, 6, eff. Jan. 1, 2022". I couldn't open the act to check Sec. 13. Replace with: "2021, No. 138 (Adj. Sess.), §§ 5, 6, eff. Jan. 1, 2022 (the history note to § 5811)". The same applies to the in_effect.yaml and income_limit.yaml reference titles and the code comment at line 40.
  • WI row, source column. It cites only "Wis. Stat. § 71.53(2)(d)". Cite § 71.53(2)(c) ("under 18 years of age at the close of the year"), § 71.53(2)(d) (claimed as a dependent unless 62 or older), § 71.54(1)(gm) (no earned income, "For claims filed in 2018 and thereafter"), and § 71.52(1) and (1g), with links. The brief asked for a page link for each source.
  • WI "Was" column. "Age and earnings were tested across the household" is incomplete. Main tested age 18+ and 62+ across head and spouse, and earnings across every tax unit member, dependents included. Replace with: "Age, disability and age 62 were tested across the head and spouse, and earned income across every member of the tax unit, dependents included. The dependency bar was never read."
  • Readings, item 1. "This PR follows page 10, which matches the claimant-based conditions in Wis. Stat. § 71.53." The earned income condition sits in § 71.54(1)(gm), not § 71.53. Replace with: "This PR follows page 10 ("The spouse that meets this definition must be the claimant"). That matches the statute: § 71.53(2)(c) and (d) and § 71.54(1)(gm) each test "the claimant", § 71.52(1g) defines earned income as the claimant's own wages and net self-employment earnings, and § 71.52(1) lets a couple choose which of them is the claimant."
  • Readings, item 2. It can quote the statute: § 71.53(2)(d) bars a claimant who "was claimed as a dependent for federal income tax purposes by another person", which supports claimed_as_dependent_on_another_return over the claimable variable.
  • Utah row. It can quote (2): "$100 for each qualifying child if: (a) the claimant or another claimant filing a joint individual income tax return with the claimant is an at-home parent".
  • The hand-off's CI line is stale. It says CI skipped the draft. The PR is no longer a draft, and the Pull request run at this head (41722397999) concluded success: all 38 jobs succeeded, none were skipped or cancelled.

The impact: and axiom: lines are present. I left them as they are.

P3-1. The WI reference tuple cites no statute

policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py:9-14 cites the 2021 LFB paper and the Schedule H instructions. The comments at lines 23-26 cite only the instructions for the age and earned income tests. #10191's version of this file cites https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/53/2/c and https://docs.legis.wisconsin.gov/statutes/statutes/71/viii/54. Carry those into the tuple, and cite "Wis. Stat. 71.53(2)(c), 71.54(1)(gm)" in the comment at lines 23-24. I couldn't reach either URL to confirm the subchapter path.

Answers to the brief's questions

  • Vermont's AGI. The formula compares federal adjusted_gross_income with the limit, using <= for "equal to or less than". Line 16c's test decides a subtraction that produces Vermont AGI, so reading its "AGI" as Vermont AGI would be circular. Federal AGI is the only reading that works without circularity, and (21)(B) is itself framed as a decrease of "federal adjusted gross income". I couldn't open IN-111 to confirm that line 1 is federal AGI.
  • Utah.
    • (2) has the same wording in every version the hub read. The parameters start in 2021, so the per-return count p.amount * count_qualifying_children * has_at_home_parent is right in every year the model covers.
    • The $50,000 AGI test in (2)(b) is applied through defined_for = "ut_at_home_parent_credit_agi_eligible", on both ut_at_home_parent_credit_potential and ut_at_home_parent_credit. That variable checks federal adjusted_gross_income <= 50_000.
    • (1)(a)(iii) is measured by earned_income: employment income plus net self-employment income. That's net profit, not Schedule C gross income, so a Schedule C loss can offset wages. Main has the same gap (follow-up F1).
  • Wisconsin's earned income start. min_age, min_elderly_age, max_income and earnings_sources all start on 2021-01-01, so the model never reaches a tax year before 2017. No follow-up.
  • Wisconsin's claimant choice. tax_unit.any(filer & qualifies & (~claimed | elderly)) asks whether either spouse alone meets every condition, which matches § 71.52(1). I checked both of the brief's couples:
    • One spouse meets every condition: eligible whichever spouse is the head. The PR's paired "claimed spouse is the head/spouse" cases cover this, and so does the property test.
    • No one spouse meets them all: ineligible. W10 below, where the only earner is claimed and the other spouse has no earnings, is false at the head and true at main. W8 is false at the head and true at main.
  • The overlap with Leave dependents' income out of Wisconsin homestead credit household income #10191 is covered below.

Overlap with #10191 (head a953caf419)

git merge-tree --write-tree --name-only pr-10104 pr-10191 conflicts in exactly the two files the hand-off names, wi_homestead_eligible.py and wi_homestead_eligible.yaml.

This PR's formula is a superset of #10191's. #10191's eight new eligibility cases (Cases 8 to 15, the 208 lines it appends after line 95 of the YAML) all pass on this PR's head: 8 passed.

I ran a trial merge on a scratch copy, keeping this PR's side of both files and appending #10191's Cases 8 to 15:

Simpler order: this PR first. The build on #10191 may not edit either eligibility file. If this PR lands first and carries #10191's Cases 8 to 15 and its two statute URLs, #10191's merge of main resolves by taking main's side of both files whole. That needs no authored edit to either file, so it fits that build's orders.

If #10191 lands first instead, this PR's merge must keep:

Other open work. #10044 (draft) auto-merges in ut_at_home_parent_credit_potential.py. Its UT cases each have only one spouse at $3,000 or less, so the per-return change doesn't move them. #10595 is the only other open PR the overlap search returns. It touches only Vermont 2017 taxable income files, none of this PR's.

Checked and sound

  • Each defect reproduces at main (abc67b98) and is fixed at the head. I ran witness YAMLs at both heads:
    • VT single, $10,000 wages, $1,000 interest: vt_agi is 8,000 at main and 9,000 at the head.
    • UT, both spouses earning $3,000 or less, one newborn: ut_at_home_parent_credit_potential is 200 at main and 100 at the head.
    • WI W7, a 40-year-old with no earnings and a dependent 16-year-old earning $5,000: eligible at main, ineligible at the head.
    • WI W8, an 18-year-old with no earnings and a 17-year-old earning spouse: eligible at main, ineligible at the head.
    • WI W10, a claimed 30-year-old only earner with a spouse who has no earnings: eligible at main, ineligible at the head.
    • A self-employed WI claimant with no wages is eligible at both heads. earnings_sources includes self_employment_income, which matches § 71.52(1g).
  • Hand values, computed before reading the code that produces them:
    • VT $4,000 interest at $50,000 wages: federal cap $2,500, so $4,000 - $2,500 = $1,500.
    • VT joint at $195,000: $2,500 × (1 - 25,000/30,000) = $416.67 federal, AGI $194,583 ≤ $200,000, so $3,000 - $416.67 = $2,583.33.
    • VT single at $125,000 and joint at $205,000: over the limit, so $0. 2021: $0. The § 5811 history note dates the subtraction to Jan. 1, 2022.
    • UT: one at-home parent with two children gives $200, and two at-home parents give $200 as well. The rename of the old "2 eligible parents with 1 child" case to "1 eligible parent with 2 children" matches its inputs.
    • WI: each case's expected value follows § 71.53(2)(c) and (d) and § 71.54(1)(gm) on the hub's reading.
  • The changed test files pass at the head, one per process: VT 7, UT 5, WI 15, test_vt_ut_wi_statutory_invariants.py 3, test_source_conflicts.py 103.
  • Cost of the new Python file. Locally, alone: 75 s wall time including import and model build, of which about 12 s is test calls (WI 9.6 s, VT 2.1 s, UT 0.6 s), and 1.5 GB peak RSS for its process. The file runs in the core group of the rest-Python shards, which the Makefile records at 13.3 GB / 11:51 in CI run 38141803955. I didn't measure the file's marginal memory inside that group. The PR's CI run passed every Rest Python shard.
  • Properties. Each property asserts what the body claims. They're weak: max_examples=3 with derandomize=True makes each one a fixed sample, and the VT property hard-codes the limits (see P2-2).
  • Readers.
    • vt_student_loan_interest_subtraction is read only through gov.states.vt.tax.income.agi.subtractions, then vt_subtractions and vt_agi.
    • ut_at_home_parent_credit_potential is read only by ut_at_home_parent_credit, which gov.states.ut.tax.income.credits.non_refundable lists.
    • wi_homestead_eligible is read only as defined_for of wi_homestead_credit, which feeds gov.states.wi.tax.income.credits.refundable, retirement_income_exclusion_refundable and gov.states.household.state_property_tax_credits.
    • No reader gets worse. The old list item, the raw student_loan_interest, has no other Vermont reader.
  • Baseline. The diff can move baseline outputs:
    • Vermont AGI and tax rise for every Vermont filer with student loan interest that was deducted federally, for filers over the limits, and for every 2021 filer with interest.
    • Utah credits halve for couples where both spouses earn $3,000 or less.
    • Wisconsin eligibility falls for units where only a dependent or an under-18 spouse earns. claimed_as_dependent_on_another_return is an input that defaults to false, so the dependency bar moves little unless the microdata sets it.
    • The hub's population check should report the Vermont revenue change and the Utah and Wisconsin counts.
  • The CTC removal. git diff abc67b98 pr-10104 -- '*vt_ctc*' is empty, and Phase out Vermont's child tax credit per child #10644 carries the CTC fix with its own hand-off.
  • Reviews. There are no reviews or inline comments to answer.
  • Scope. Nothing under .github/ or policyengine_us/tests/policy/baseline/partners/, and a changelog fragment exists at changelog.d/vt-ut-wi-statutory-fixes.fixed.md.
  • What the author's brief asked for that the PR doesn't do: cite each source with a page link (the WI statute has none, P2-4), and record the source conflict (P2-1). Everything else it asked for is done.

Proposed follow-ups (main shares these)

  • F1. Utah's at-home parent test uses net earnings, not W-2 wages plus Schedule C gross income. ut_at_home_parent_credit_earned_income_eligible_person compares earned_income (wages plus net self-employment income) with $3,000, while (1)(a)(iii) counts W-2 wages plus Schedule C gross income. Witness, 2025: a head with $5,000 of wages and a $3,000 Schedule C loss, plus a newborn. The person comes out eligible ([1. 0.]) at both main and this head; the statute's test gives $5,000 > $3,000. The model also doesn't test (1)(a)(i) and (ii), full-time care of and a dependency claim for the parent's own child.
  • F2. Wisconsin earned income includes partnership_s_corp_income. § 71.52(1g) counts wages and "net earnings from self-employment". S corporation income isn't self-employment earnings, so an S-corp shareholder with no wages can qualify. Main has the same list. Check the list against the statute's definition.

What I couldn't check

  • None of the primary sources: every host was blocked (see the top). I relied on the hub's reading for all of them.
  • Act 138's Sec. 13 and its page numbers.
  • IN-111 line 1, the Schedule H page numbers, and the 71/viii path of the WI statute URLs.
  • IRC § 221's text: the code comment at lines 27-29 cites § 221(c) for the dependent bar on the federal deduction.
  • Population impact: I had no access to the microdata.
  • This file's marginal memory inside the core CI group.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

32 V.S.A. § 5811(29)(B) sets limits only for single, head of household,
married filing separately and joint filers, and the chapter names surviving
spouses wherever it includes them, so a surviving spouse is not a qualified
resident taxpayer. IN-112 line 16c's "All other filers" $120,000 has no gap
to fill. The limit for that status becomes -inf; the conflict is recorded as
the candidate us-vt:student-loan-interest-surviving-spouse-limit, and the
parameter's comment is counted in KNOWN_UNMARKED until the marker lands.

Also cite the effective date as the § 5811 history note gives it (2021,
No. 138 (Adj. Sess.), §§ 5, 6, eff. Jan. 1, 2022) and state (29)(B)'s
filing statuses exactly in the variable's comment.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JwDtRRMHxwVAQMx6f
The age, earned income and earned income definition tests come from Wis.
Stat. 71.53(2)(c), 71.54(1)(gm) and 71.52(1g); the reference tuple and the
comment cited only the LFB paper and the Schedule H instructions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JwDtRRMHxwVAQMx6f
Adds the cases no test covered: AGI exactly at the $120,000 and $200,000
limits ("equal to or less than"), two surviving spouses who get no
subtraction under 32 V.S.A. § 5811(29)(B) (the form would give $3,000 and
$1,500), a dependent's interest, and a Wisconsin couple where neither
spouse alone is both 18 or older and an earner.

Also carries #10191's eligibility Cases 8 to 15 unchanged, so that PR's
merge of main can take main's side of the eligibility file whole, and cites
the effective date as the § 5811 history note gives it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JwDtRRMHxwVAQMx6f
The test hard-coded the limit as $200,000 joint and $120,000 otherwise and
never drew a surviving spouse, so a surviving-spouse limit of $120,000 or
$200,000 passed it. It now draws single, joint and surviving spouse returns
and asserts a zero subtraction for every surviving spouse.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JwDtRRMHxwVAQMx6f
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: a35d9295a34a95ddd6b828eea6eaee8abb80c46d

This round answers the r1 review at 99ae1e525a, with the hub's ruling on the surviving spouse. I built on 99ae1e525a; no other commit had landed on the branch. Commits: 53d2ab7926 (surviving spouse, register item, comments), 6c61d40880 (WI statutes), 37fd90982d (cases, #10191's cases, effective-date wording), 8c307c64db (property test), a35d9295a3 (changelog fragment).

Ownership check. The PR's last commit (20:15Z) was inside the 6-hour window, but it is the previous round's own merge commit. That round posted its hand-off at 21:19Z, and the review was made at that head, so I treated no writer as active and picked up at 23:47Z. Judgment call.

Items

  1. Surviving spouse gets no subtraction. income_limit.yaml:22-26: SURVIVING_SPOUSE is -.inf from 2022. The loader accepts it (the MD contrib dependent exemption scales already use -.inf), so I didn't need a formula bar. The comment above that line names the conflict; the file is in KNOWN_UNMARKED with 1 (test_source_conflicts.py, in sorted order, before the WI homestead disqualified_loss_sources.yaml line). Readers: git grep finds one, vt_student_loan_interest_subtraction.py:42 (agi <= p.income_limit[filing_status]), which is false for any finite AGI, so every surviving spouse gets 0. The property test hard-codes its own limits, and now uses -np.inf for a surviving spouse. Nothing else reads gov.states.vt.tax.income.agi.student_loan_interest.

  2. Register item. source_conflicts/candidates.yaml, right after us-vt:snap-child-support-deduction (ids stay sorted): the review's item with follows: statute, state: ruled, the third source (32 V.S.A. § 5822(a)), and the brief's example and note word for word. test_source_conflicts.py: 103 passed. The ratchet counts 1 for income_limit.yaml and 0 for both changed .py files.

  3. Cases. vt_student_loan_interest_subtraction.yaml gains the review's four VT cases, minus its $150,000 surviving-spouse case, plus the two surviving-spouse cases. Hand values, worked before running and recorded in the comments:

    • Single at exactly $120,000 (2025): the federal deduction is 0 above $100,000 MAGI; AGI is $120,000 ≤ $120,000, so VT = $3,000 − $0 = $3,000.
    • Joint at exactly $200,000 (2022): the 2022 joint phase-out ends at $175,000, so federal is [0, 0]; AGI is $200,000 ≤ $200,000, so VT = $3,000.
    • Surviving spouse, $110,000 wages, $3,000 interest (2025): MAGI $110,000 > $100,000 (non-joint $85,000 start + $15,000 divisor), so federal is [0, 0] and AGI is $110,000. VT is 0 under (29)(B); the form would give $3,000.
    • Surviving spouse, $50,000 wages, $4,000 interest (2025): federal min($4,000, $2,500) = [2,500, 0] with no phase-out, AGI is $47,500, VT is 0; the form would give $1,500.
    • Dependent's $1,000 of interest: federal [0, 0] (the dependent is barred; the head paid none), VT 0.
    • WI, an 18-year-old with no earnings and a 17-year-old earner: neither spouse is both 18+ and an earner/62+/disabled, so false.

    The property test (test_vt_ut_wi_statutory_invariants.py:77-98, 102-129) now draws SINGLE, JOINT and SURVIVING_SPOUSE returns (the surviving spouse with a 10-year-old), sets the filing status through a new filing_statuses argument to _situation, and asserts not sub[surviving].any(). Runtime, measured on this VM, one process each: the VT property's call time went from 2.5 s to 3.1 s; the whole file took 77 s before and 84 s after in wall time (import and model build included), with peak RSS 1,495 MB before and 1,497 MB after. These are local measurements, not Linux CI.

  4. Mutants. Each ran in its own process: the state's YAML file, then pytest ... test_vt_ut_wi_statutory_invariants.py -k <state>. The code was restored with git checkout after each, and git status was clean after each.

Mutant Changed YAML file Property test Killed by
R1 VT: subtract the whole interest 3 of 12 fail fails YAML and property
R2 VT: no income limit 4 of 12 fail fails YAML and property
R3 UT: one credit per qualifying spouse 2 of 5 fail fails YAML and property
R4 WI: no dependency bar 4 of 24 fail passes YAML
R5 WI: any tax unit member's earnings 6 of 24 fail passes YAML
R6 WI: earnings summed across both spouses 5 of 24 fail passes YAML
R7 VT: < instead of <= 2 of 12 fail (the exact $120,000 and $200,000 cases) passes YAML
R8 VT: SURVIVING_SPOUSE $200,000 2 of 12 fail (both surviving-spouse cases: 3,000 and 1,500 vs 0) fails YAML and property
R9 WI: 18-or-older tested across the couple 2 of 24 fail passes YAML
R10 VT: dependents' interest counted 1 of 12 fails (1,000 vs 0) passes YAML
O1 VT: SURVIVING_SPOUSE $120,000 (the form's reading) 2 of 12 fail (3,000 and 1,500 vs 0) fails YAML and property
O2 VT: no 2022 effective-date gate 1 of 12 fails (1,500 vs 0, 2021) passes YAML

None survives.

The brief's own "$200,000" mutant is the same as the review's R8, so I ran it once and added O2 (no 2022 effective-date gate) as a 12th.
5. Code comment. vt_student_loan_interest_subtraction.py:35-39 is the brief's text, as a Python comment. The guard counts 0 for this file.
6. Wisconsin references. wi_homestead_eligible.py:14-16 adds the three statute URLs to the tuple; the comment at :26 now begins "Wis. Stat. 71.53(2)(c), 71.54(1)(gm):".
7. #10191 overlap. I appended #10191's lines 98-305 of wi_homestead_eligible.yaml (its note and Cases 8 to 15) to the end of this PR's file, byte for byte (cmp against git show a953caf419:... is identical). #10191's head has since moved to 5c3716b10b, but git diff a953caf419 5c3716b10b on that file is empty. The file passes 24 of 24 (15 old, the review's new case, and #10191's 8). The body says this PR carries them. I didn't touch #10191.
8. Body. I applied each P2-4 replacement, using the hub's VT "Rule now" text. The effective-date fix also went into the two parameter reference titles (income_limit.yaml:12, in_effect.yaml:10), the variable's reference comment and its comment at :43-44. I made the same wording change in three places the review didn't list: subtractions.yaml:9, and two VT YAML comments (lines 130 and 153-154). I replaced the old CI line with this head's run. impact: and axiom: are unchanged.
9. Runs (one file per process):

  • vt_student_loan_interest_subtraction.yaml: 12 passed.
  • ut_at_home_parent_credit.yaml: 5 passed.
  • wi_homestead_eligible.yaml: 24 passed.
  • test_vt_ut_wi_statutory_invariants.py: 3 passed.
  • test_source_conflicts.py: 103 passed.
  • ruff format --check and ruff check on the four changed Python files: ruff format --check reports all 4 already formatted, and ruff check reports "All checks passed!" for each (run on the committed contents).

CI

CI: the Pull request run at a35d9295a3 (41745174793) was still in progress at 01:17Z, 64 minutes after the push. 35 of 36 jobs had concluded success, and one, Contrib tests / Full Suite - Contrib (other-shard-3), started at 00:44Z, was still running. None had failed or been cancelled. Per the brief, I'm handing off with the run URL rather than waiting; the hub should read its final conclusion.

Judgment calls

  • Quotes. This VM's proxy refused every source host (legislature.vermont.gov, docs.legis.wisconsin.gov, revenue.wi.gov, taxsim.nber.org, le.utah.gov, web.archive.org; 403 on CONNECT). So the body quotes only what the brief quoted from the hub's 2026-11-03 reading. Where the review's replacement text quoted Utah § 59-10-1005(2), Wis. Stat. §§ 71.53(2)(c)/(d) and 71.54(1)(gm), § 5811(21)(B)(vi), IN-112 line 16b or Schedule H p. 10, I paraphrased with the citation instead, since I couldn't fetch them. The hub may want to restore the review's quotes after checking them.
  • Act 138 reference. The #page 4-5,15 page list came off both reference titles: I couldn't open the act to check §§ 5, 6 or Sec. 13. The href stays at #page=4, as before.
  • WI statute links. The body links §§ 71.53(2)(c), 71.54 and 71.52(1g), the three URLs the hub opened. §§ 71.53(2)(d) and 71.52(1) are cited without links rather than with guessed URLs.
  • Order in the WI file. The review's new case sits before Leave dependents' income out of Wisconsin homestead credit household income #10191's block, so the block stays a contiguous, unchanged tail.
  • No partner tests and nothing under .github/ were touched.

Proposed follow-ups

  • Mark us-vt:student-loan-interest-surviving-spouse-limit once registered. After this merges and rulespec-us has the entry: add # Source conflict us-vt:student-loan-interest-surviving-spouse-limit at income_limit.yaml's SURVIVING_SPOUSE line, drop its KNOWN_UNMARKED line, delete the candidate and refresh the pin.
  • The review's F1 (Utah at-home parent test uses net rather than gross Schedule C earnings) and F2 (Wisconsin earned income includes partnership_s_corp_income) still stand; main shares both.

Generated by Claude Code

@MaxGhenis

MaxGhenis commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Superseded by the final review: #10104 (comment)


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
a35d929
METHODOLOGY_CHOICE: yes

The head hasn't moved since the round 2 hand-off. This is a delta review of the five commits from 99ae1e525a (the round 1 review's head) to a35d9295a3: 53d2ab7926, 6c61d40880, 37fd90982d, 8c307c64db and a35d9295a3.

The round 2 changes are right on the hub's reading. A qualifying surviving spouse now gets no Vermont subtraction at any AGI, including 0 and negative AGI. The register item passes the guard. Every new case matches my hand values. The carried #10191 block is byte-identical to #10191's current head. CI is green: 36 of 36 jobs.

I'm approving because no finding changes a result. Two things should still land before or with the merge:

  • three Vermont cases, because no test pins the head of household or married filing separately limit, and my two mutants of them survive (P2-1);
  • body corrections: a stale CI line, and quotes the hub's reading supports (P2-2).

Sources. I couldn't reach any primary source from this VM. curl got a proxy 403 on CONNECT from legislature.vermont.gov, docs.legis.wisconsin.gov, le.utah.gov, taxsim.nber.org, revenue.wi.gov, tax.vermont.gov, govinfo.gov, uscode.house.gov and ecfr.gov. A second fetch route was refused for legislature.vermont.gov as outside the session's allowed domains. Every legal statement below rests on the hub's 2026-11-03 reading, quoted in the review brief. I quote nothing from memory.

Findings

P2-1. No test pins Vermont's head of household or married filing separately limit; two mutants survive

policyengine_us/parameters/gov/states/vt/tax/income/agi/student_loan_interest/income_limit.yaml:18-21 sets HEAD_OF_HOUSEHOLD and SEPARATE to $120,000. No YAML case pins either one. The one case that defaults to head of household, the dependent's-interest case, expects 0 at any limit. The property test draws only SINGLE, JOINT and SURVIVING_SPOUSE.

Mutant (mine) VT YAML (12 cases) Property Result
M1: HEAD_OF_HOUSEHOLD -.inf 12 passed passes survives
M2: SEPARATE -.inf 12 passed passes survives

Failure scenarios:

  • M1: a 2025 head of household with $119,000 of wages and $3,000 of interest would get 0 against § 5811(29)(B)(i)'s $120,000 limit.
  • M2: the same holds for a married filing separately filer.

Both scenarios give $3,000 at the head; I ran them as witnesses.

Add these three cases at the end of policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml:

- name: 2025 head of household at exactly $120,000 of adjusted gross income keeps the Vermont student loan interest subtraction
  period: 2025
  input:
    people:
      head:
        age: 40
        employment_income: 120_000
        student_loan_interest: 3_000
      child:
        age: 10
    tax_units:
      tax_unit:
        members: [head, child]
        filing_status: HEAD_OF_HOUSEHOLD
    households:
      household:
        members: [head, child]
        state_code: VT
  output:
    # 5811(29)(B)(i): $120,000 if head of household. The federal deduction is
    # fully phased out above $100,000 of MAGI, so 16a $3,000 - 16b $0 = $3,000.
    adjusted_gross_income: 120_000
    student_loan_interest_ald: [0, 0]
    vt_student_loan_interest_subtraction: 3_000

- name: 2025 head of household above $120,000 of adjusted gross income gets no Vermont student loan interest subtraction
  period: 2025
  input:
    people:
      head:
        age: 40
        employment_income: 125_000
        student_loan_interest: 3_000
      child:
        age: 10
    tax_units:
      tax_unit:
        members: [head, child]
        filing_status: HEAD_OF_HOUSEHOLD
    households:
      household:
        members: [head, child]
        state_code: VT
  output:
    # 5811(29)(B)(i): $125,000 is above the $120,000 head of household limit.
    vt_student_loan_interest_subtraction: 0

- name: 2025 married filing separately at exactly $120,000 of adjusted gross income keeps the Vermont student loan interest subtraction
  period: 2025
  input:
    people:
      head:
        age: 40
        employment_income: 120_000
        student_loan_interest: 3_000
    tax_units:
      tax_unit:
        members: [head]
        filing_status: SEPARATE
    households:
      household:
        members: [head]
        state_code: VT
  output:
    # 5811(29)(B)(i): $120,000 if married filing separately. No federal
    # deduction, so 16a $3,000 - 16b $0 = $3,000.
    adjusted_gross_income: 120_000
    student_loan_interest_ald: 0
    vt_student_loan_interest_subtraction: 3_000

I ran the three cases alone, once at the head and once under each mutant, one process per run, on a scratch worktree:

Run Result
Head 3 passed
M1 1 failed
M2 1 failed
M4: HEAD_OF_HOUSEHOLD $200,000 1 failed

Appended to the VT file, the cases give 15 passed at the head. The command was uv run python -m policyengine_core.scripts.policyengine_command test <file> -c policyengine_us.

P2-2. The PR body: a stale CI line, and quotes it can carry

The impact: and axiom: lines are present, and I left them alone. Each item below gives the text to replace and its replacement.

  • CI line (Tests section). "the Pull request run at a35d9295a3 (41745174793) was still in progress at 01:17Z ... one ... was still running." The run has finished. Replace with: "CI: the Pull request run at a35d9295a3 (41745174793) concluded success: all 36 jobs succeeded, none skipped or cancelled." The hand-off comment has the same stale line.
  • Overlap section. "Apply the RI, SC, UT, VT, WV and WI rules for a filer who can be claimed as a dependent #10044 (draft)" is stale: Apply the RI, SC, UT, VT, WV and WI rules for a filer who can be claimed as a dependent #10044 is open and no longer a draft. Replace "(draft)" with nothing. I reran git merge-tree --write-tree --name-only pr-10104 pr-10044 against its current head ce5880d884, and it's still clean.
  • Quotes the body can carry. The fix session paraphrased the sources because it couldn't reach them. The hub's reading quotes each text below, so the body can quote them:
    • VT, "Vermont's federal offset". Replace "§ 5811(21)(B)(vi) subtracts the interest a qualified resident taxpayer paid during the taxable year on a qualified education loan" with: "§ 5811(21)(B)(vi) subtracts "the amount of interest paid by a qualified resident taxpayer during the taxable year on a qualified education loan for the costs of attendance at an eligible educational institution"". Replace "(16b takes the student loan interest already deducted on federal Schedule 1)" with: "(16b: "Enter any student loan interest already deducted on federal Form 1040, Schedule 1, Line 21")".
    • VT, Readings item 3. After "gives limits only for single, head of household, married filing separately and joint filers", add: "("$120,000.00 if the individual's filing status is single, head of household, or married filing separately; or (ii) $200,000.00 if the individual's filing status is married filing jointly")". After "§ 5822(a)(1) and (3)", add: "(for example, (21)(C)(ii)(I): "unmarried (other than surviving spouses or heads of households)"; § 5822(a)(1): "Married individuals filing joint returns and surviving spouses")".
    • Utah row. Replace the source cell's paraphrase with: "§ 59-10-1005(2): "$100 for each qualifying child if: (a) the claimant or another claimant filing a joint individual income tax return with the claimant is an at-home parent"".
    • WI, Readings item 1. Replace "This PR follows page 10, which says the spouse who meets the definition must be the claimant." with: "This PR follows page 10: "The spouse that meets this definition must be the claimant."" Replace "§ 71.52(1) lets a couple choose which of them is the claimant" with: "§ 71.52(1): "When 2 individuals of a household are able to meet the qualifications for a claimant, they may determine between them as to who the claimant is."".
    • WI, Readings item 2. Replace "§ 71.53(2)(d) bars a claimant who was claimed as a dependent for federal income tax purposes by another person" with: "§ 71.53(2)(d) bars a claimant who "was claimed as a dependent for federal income tax purposes by another person during the year to which the claim relates", unless 62 or older".
  • Invariants item 1 says the property test checks that the subtraction is zero in 2021. The property's fixed 2021 example draws no interest at all (P3-1), so 2021 is pinned only by the two YAML cases. Replace "The subtraction is zero above the limit, in 2021 and for every surviving spouse" with: "The subtraction is zero above the limit and for every surviving spouse (the YAML cases pin 2021)".

Every other statement in the body matches what I ran or read:

P3-1. The VT property test's 2021 branch tests nothing

policyengine_us/tests/core/test_vt_ut_wi_statutory_invariants.py:101-129 runs three fixed examples: max_examples=3 with derandomize=True. I recorded every drawn household. The 2021 example is Hypothesis's minimal one: eight SINGLE units with $0 of wages and $0 of interest. So assert not sub.any() for year < 2022 checks nothing. O2 (no 2022 effective-date gate) passes the property; only the YAML 2021 case kills it.

The surviving-spouse draw does reach the statute's assertion:

  • 8 of the 26 drawn units are surviving spouses, in the 2022 and 2026 examples.
  • Exactly one of them separates the statute from the form's $120,000: a 2026 unit with AGI −$1,979, $3,972 paid and $2,500 deducted federally.
  • One 2022 unit at $125,881 separates it from the $200,000 mutant.

That's enough to kill R8 and O1, but thin. No change is required. If the file changes again, drop 2021 from the property's sampled_from years, or draw interest from st.integers(1, 6_000); the YAML already pins 2021.

The property also repeats the parameter's -np.inf. With assert not sub[surviving].any() already stated, within = (agi <= np.where(statuses == "JOINT", 200_000, 120_000)) & ~surviving would assert the statute without copying the parameter's encoding.

P3-2. Follow-up, not this PR's defect: § 71.54(1)(gm)'s exemption, read literally

wi_homestead_eligible.py:26-29 now cites "Wis. Stat. 71.53(2)(c), 71.54(1)(gm)" for the rule that the claimant "must be 62 or older, disabled or have earned income". The hub's quote of (gm) bars a claimant with no earned income "with regard to a claimant who is not disabled or who is under the age of 62". Read literally, that bars any claimant who is not disabled, or who is under 62. So it exempts only a claimant who is both disabled and 62 or older. The code, like main, exempts a claimant who is either one. Schedule H Step 1a supports the code.

The hub's quote has an ellipsis, and I couldn't open the statute. Before the comment's citation stands, the hub should read the full text of (gm). If the literal reading holds, it's a statute-versus-instructions conflict to record as a candidate. Main has the same formula.

The checks the brief asked for

  • The -.inf limit.

    • The loader accepts it. The changed files pass at the head, and 176 parameter files already use -.inf, including baseline Medicaid and CHIP income limits.

    • git grep -n "agi.student_loan_interest" finds one reader, vt_student_loan_interest_subtraction.py:22. Its test agi <= p.income_limit[filing_status] (line 42) is false for every finite AGI.

    • Witnesses at the head (2025 surviving spouse):

      Witness Head Round 1 head 99ae1e525a Main's vt_subtractions
      AGI exactly 0 ($2,500 wages, $4,000 interest, $2,500 federal) 0 $1,500 $4,000
      Negative AGI ($3,000 wages, −$10,000 self-employment, $4,000 interest) 0 $1,500 not run
      $110,000 wages, $3,000 interest 0 $3,000 $3,000

      A single filer at AGI 0, run as a control, gives $1,500 at the head.

    • Only an AGI of exactly −∞ would pass the test, and no input the model takes produces one.

  • The register item (source_conflicts/candidates.yaml:9967-10000).

    • It has every field test_candidate_fields requires.
    • follows: statute is in FOLLOWS and state: ruled is in STATES. The file holds 60 other ruled items with follows: statute.
    • It has three sources: two statutes and a form, all https.
    • Its example matches the $110,000 case.
    • Its ids stay sorted, and it adds no marker, which follows CONTRIBUTING.md step 1.
    • KNOWN_UNMARKED gains income_limit.yaml: 1, in sorted order. The guard counts the parameter's comment ("The 2025 IN-112 line 16c instructions say ...") and nothing in either changed .py file.
    • test_source_conflicts.py: 103 passed.
  • The cases, by hand before running:

    • Single at exactly $120,000 (2025): MAGI is above $100,000, so federal is $0; $120,000 ≤ $120,000, so $3,000.
    • Joint at exactly $200,000 (2022): the 2022 joint phase-out ends at $175,000, so federal is $0, and the subtraction is $3,000.
    • Surviving spouse, $110,000 and $3,000: the non-joint phase-out ($85,000 to $100,000) gives federal $0 and AGI $110,000. The statute gives 0; the form would give $3,000.
    • Surviving spouse, $50,000 and $4,000: federal min($4,000, $2,500) = $2,500, so AGI is $47,500. The statute gives 0; the form would give $1,500.
    • Dependent's $1,000 of interest: the head paid none, so 0.
    • WI, an 18-year-old with no earnings and a 17-year-old earning $5,000: neither spouse is both 18 or older and either 62 or older, disabled or an earner, so false.

    Each matches the file.

  • Runs at the head, one per process:

    File Result
    vt_student_loan_interest_subtraction.yaml 12 passed
    ut_at_home_parent_credit.yaml 5 passed
    wi_homestead_eligible.yaml 24 passed
    test_vt_ut_wi_statutory_invariants.py 3 passed in 28 s (89 s wall time with import)
    test_source_conflicts.py 103 passed

    ruff format --check reports the 5 changed Python files already formatted, and ruff check passes. hypothesis comes with the dev extra (uv sync --extra dev).

  • The property test. The new filing_statuses argument sets each tax unit's filing_status, and the draw reaches surviving spouses (P3-1). The assertion assert not sub[surviving].any() is the statute's rule. Under O1 the property fails on exactly that line: Hypothesis shrinks to a 2022 example with a $1 subtraction.

  • Mutants. Each ran in its own process on a scratch worktree. I removed .hypothesis before each run, so no saved example carried over. After each, I ran git checkout -- ., and git status was clean at the end. Per mutant I ran the state's YAML file, then the property file.

    Mutant YAML Property Killed by
    R7 VT < 2 of 12 fail ($120,000 and $200,000 boundary cases) passes YAML
    R8 VT surviving spouse $200,000 2 of 12 fail (3,000 and 1,500 vs 0) fails YAML and property
    O1 VT surviving spouse $120,000 2 of 12 fail (3,000 and 1,500 vs 0) fails YAML and property
    O2 VT no 2022 gate 1 of 12 fails (1,500 vs 0, 2021) passes YAML
    R4 WI no dependency bar 4 of 24 fail passes YAML
    R9 WI 18-or-older across the couple 2 of 24 fail passes YAML
    M1 VT head of household -.inf (mine) 12 pass passes survives: P2-1
    M2 VT separate -.inf (mine) 12 pass passes survives: P2-1
    M3 WI age-62 dependency exception across the couple (mine) 24 pass passes equivalent: a spouse 62 or older already qualifies alone, so no result can change

    The six reruns match the hand-off's table.

  • Overlap with Leave dependents' income out of Wisconsin homestead credit household income #10191. Its head is now dbe35eb8bc, past the 5c3716b10b the brief names. The two new commits touch only income/sources.yaml and wi_homestead_income.yaml.

  • Other open work.

  • CI. Pull request run 41745174793 at a35d9295a3 concluded success at 01:18Z. All 36 jobs succeeded, and none were skipped or cancelled.

  • Reviews. The PR has no reviews or inline comments.

  • Scope. The PR changes 13 files, none under .github/ or policyengine_us/tests/policy/baseline/partners/. The changelog fragment remains at the top level of changelog.d/.

Checked and sound

  • Each round 2 item from the brief is done: the -.inf limit (1), the register item with all three sources and the hub's note (2), the five cases (3), twelve mutants with none surviving (4), the code comment (5), the WI references and comment (6), Leave dependents' income out of Wisconsin homestead credit household income #10191's cases (7) and the body edits (8). The parameter's description now says "no surviving spouse qualifies". The effective-date wording changed in both reference titles, subtractions.yaml:9, the variable's comment and two YAML comments.
  • What the brief asked for that the PR doesn't fully do. Item 8 asked for each of the round 1 review's body replacements. The fix session couldn't fetch the sources, so it paraphrased the review's quotes (P2-2 gives them back). Everything else is done.
  • Readers.
    • income_limit.yaml and in_effect.yaml are read only by vt_student_loan_interest_subtraction.
    • That variable is read through gov.states.vt.tax.income.agi.subtractions, then vt_subtractions and vt_agi.
    • The WI change since round 1 is references and comments only.
    • No reader gets worse.
  • Baseline. Compared with round 1, the only result this round moves is Vermont surviving spouses with student loan interest: their subtraction falls to 0, at round 1 the remaining interest up to the $120,000 limit. Compared with main, those filers had subtracted all of their interest with no income limit. The hub's population check should count Vermont surviving-spouse returns with student loan interest. I expect the count to be small, but I couldn't measure it.

What I couldn't check

  • Every primary source: the hosts were refused (see above). In particular: the full text of § 71.54(1)(gm) (P3-2); whether § 5822(a) and § 5811(21)(C) are the only places the chapter names surviving spouses; Act 138's page for §§ 5 and 6 (the link stays at #page=4); IN-111 line 1; and IRC § 221's phase-out amounts. For the federal amounts I relied on the model's own outputs.
  • Population impact: I have no access to the microdata.
  • The property file's marginal memory inside its CI group. Locally, the file takes 89 s wall time with import and model build.

Follow-ups (main shares these)

  • The round 1 review's F1 (Utah's at-home parent test uses net rather than gross Schedule C earnings) and F2 (Wisconsin earned income includes partnership_s_corp_income) still stand.
  • P3-2: read § 71.54(1)(gm) in full, and record a candidate if its literal exemption (disabled and 62 or older) conflicts with Schedule H Step 1a.
  • After merge: the marker for us-vt:student-loan-interest-surviving-spouse-limit (CONTRIBUTING.md steps 2 to 4), as the hand-off proposes.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

…mits

No test pinned the $120,000 HEAD_OF_HOUSEHOLD or SEPARATE limit, so the
round 2 review's mutants setting either to -inf, or head of household to
$200,000, passed. Three 2025 cases at and above the limit now kill each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6FqQp8Bf92tRiedzTm
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 6f203e8ba3a28d6817b81f2996182ac8a89b3d3b

One commit on top of a35d9295a3: 6f203e8ba3 adds three YAML cases only. No one else pushed to the branch while I worked. git merge-tree with origin/main (0315452583) is clean, so I merged nothing. I left P3-1 and P3-2 for the hub, as the brief said.

What the round 2 review asked for, and what I did

  1. P2-1: the head of household and separate limits.

    • I appended three cases to policyengine_us/tests/policy/baseline/gov/states/vt/tax/income/agi/subtractions/vt_student_loan_interest_subtraction.yaml, using the brief's names and comments and the review's inputs and outputs:

      • HoH at $120,000, :290-314;
      • HoH above $120,000, :316-336;
      • married filing separately at $120,000, :338-360.
    • The file passes at this head: 15 passed.

    • Each mutant ran in its own process. I edited income_limit.yaml in place, cleared .hypothesis, ran the VT YAML and then the property file, and restored with git checkout after each.

      Mutant VT YAML Property Failing case
      M1 HEAD_OF_HOUSEHOLD -.inf 1 of 15 fail 3 passed HoH at $120,000: 0 vs 3,000
      M2 SEPARATE -.inf 1 of 15 fail 3 passed MFS at $120,000: 0 vs 3,000
      M4 HEAD_OF_HOUSEHOLD $200,000 1 of 15 fail 3 passed HoH at $125,000: 3,000 vs 0
    • I added all three rows to the body's mutant table. "None survives." still holds.

    • Hand check: AGI is $120,000 in each case. The non-joint federal phase-out ends at $100,000 of MAGI, and an MFS filer gets no federal deduction, so 16b is $0. $120,000 ≤ $120,000 gives the full $3,000, and $125,000 > $120,000 gives 0.

  2. P2-2: the body. I made each replacement in place with the brief's text: Vermont's federal offset, the Utah source cell, Readings items 1, 2 and 3's first two sentences, and Invariants item 1.

  3. Runs at 6f203e8ba3, one process each:

    File Result
    vt_student_loan_interest_subtraction.yaml 15 passed
    ut_at_home_parent_credit.yaml 5 passed
    wi_homestead_eligible.yaml 24 passed
    test_vt_ut_wi_statutory_invariants.py 3 passed, .hypothesis cleared first
    test_source_conflicts.py 103 passed

    No Python file changed, so ruff had nothing to run on.

CI

Pull request run 41947821257 at 6f203e8ba3 hadn't finished 60 minutes after the 01:20Z push. At 02:21Z, 34 of 36 jobs had concluded success. Contrib tests / Full Suite - Contrib (other-shard-1) and (other-shard-3), both started at 01:51Z, were still running. None had failed or been cancelled.

The body's CI line still describes run 41745174793 at a35d9295a3 as in progress. Item 8 applies only once a run finishes, so I left it for the hub. When this run passes, it should read: "Pull request run 41947821257 passed all 36 jobs at 6f203e8ba3."

Judgment calls

  • I couldn't verify the new quotations. From this session, legislature.vermont.gov, docs.legis.wisconsin.gov, le.utah.gov, revenue.wi.gov and web.archive.org all fail to resolve or connect. The quotations in the offset paragraph, the Utah cell and Readings 1–3 are the brief's text, taken from the hub's reading. The hub should confirm them against the sources before landing.
  • I kept the old mutant rows' "of 12" counts as they were, instead of changing them to 15 without a rerun.

Proposed follow-up

  • Read Wis. Stat. § 71.54(1)(gm) in full for the homestead earned-income exemption. The round 2 review's P3-2 says the literal text may exempt only a claimant who is both disabled and 62 or older. The code and Schedule H Step 1a exempt either one. If the literal reading holds, record a source-conflict candidate.

Generated by Claude Code

@DTrim99

DTrim99 commented Nov 5, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Fix Vermont student loan interest, Utah's at-home parent credit and Wisconsin's homestead claimant

Summary

I read each provision's law first and then compared the code. All three fixes match the law:

  • Vermont. 32 V.S.A. § 5811(21)(B)(vi) and (29), added by 2021, No. 138 (Adj. Sess.) § 5 and effective for taxable years beginning on and after Jan. 1, 2022 (§ 13(b)). The subtraction is the interest paid minus the federal deduction (IN-112 lines 16a–16c), with the $120,000 / $200,000 federal AGI limits.
  • Utah. Utah Code § 59-10-1005(2) pays "$100 for each qualifying child" when "the claimant or another claimant filing a joint individual income tax return with the claimant is an at-home parent", so the credit is paid once per child per return.
  • Wisconsin. §§ 71.52(1), (1g) and 71.53(2)(c), (d): one claimant must meet the age, earned income and dependency conditions personally.

The surviving-spouse reading for Vermont is well supported and recorded as a candidate.

Four things block approval:

  • Critical. When main was merged in, vt_other_subtractions was dropped from Vermont's 2021 subtraction list. Vermont 2021 returns with that input now lose the subtraction.
  • Should address.
    • Two new pieces aren't pinned by any test: the Vermont list entry, and the Wisconsin filer guard. I confirmed both by mutation.
    • The Wisconsin age/disability/earned income reading follows Schedule H over the literal text of § 71.54(1)(gm). The PR doesn't record it, and the code comment cites (gm) for it.
    • The Act 138 references cite the wrong section and a page that doesn't show the values.

CI is green: 36 of 36 checks at 6f203e8ba3. The axiom line is valid.

Critical (must fix)

C1. Vermont 2021 no longer subtracts vt_other_subtractions, because the 2021 block was rebuilt during the merge with main.
policyengine_us/parameters/gov/states/vt/tax/income/agi/subtractions.yaml:3-8

  • What changed. Since Support state-specific other additions and subtractions for remaining 30 states (Phase 2) #10302 (merged 2026-10-21, before this branch's merge base), main's list has vt_other_subtractions from 2021-01-01. This PR's new 2021 block lists us_govt_interest, vt_medical_expense_deduction, vt_capital_gains_exclusion, vt_retirement_income_exemption and salt_refund_income. It omits vt_other_subtractions, which now appears only in the 2022 block at line 18. The diff hides this: the old - vt_other_subtractions line survives as unchanged context, but under the new 2022 key.

  • Witness at the head (one process, head tree):

    Case Expected (main) Head
    2021, VT, wages $50,000, vt_other_subtractions $1,300: vt_subtractions $1,300 $0
    2024 control, same inputs $1,300 $1,300

    vt_other_subtractions stands for Schedule IN-112 Part I subtractions that PolicyEngine doesn't compute separately. The 2021 IN-112 allows those subtractions (Part I, lines 7–14), so 2021 Vermont AGI is overstated for every return with that input. 2021 is a claimed year: the PR changes the 2021 list and adds 2021 cases.

  • Fix. Add - vt_other_subtractions to the end of the 2021 block. Pin it with a 2021 case, for example in vt_other_subtractions.yaml: period 2021, state_code: VT, vt_other_subtractions: 1_300 → vt_subtractions: 1_300. That file's two existing cases are both 2024, which is why CI missed this.

Should address

S1. No test pins the new vt_student_loan_interest_subtraction entry in Vermont's 2022 subtraction list.
subtractions.yaml:14

  • Evidence. In 2022 and later, every case asserts the subtraction variable directly. The one 2022+ case that checks vt_agi has a subtraction of $0. The property test reads only vt_student_loan_interest_subtraction, and no other Vermont test uses student_loan_interest.

  • Mutation. I deleted the line in the head tree. All 15 Vermont cases still passed. My witness then gave vt_subtractions $0 instead of $1,500.

  • Fix. Add these outputs to the second case (2025, wages $50,000, interest $4,000; lines 24–42):

    • vt_subtractions: 1_500
    • vt_agi: 46_000 ($50,000 − $2,500 federal = $47,500 federal AGI; minus $1,500 = $46,000)

    Both values pass at the head; I ran them.

S2. No test pins the Wisconsin filer guard. Case 8 was meant to pin it, but its dependent is 17.
policyengine_us/variables/gov/states/wi/tax/income/credits/homestead/wi_homestead_eligible.py:37

  • The guard. eligible_claimant = filer & qualifies & (~claimed | elderly). Without filer, an adult dependent in the tax unit could qualify the return. claimed_as_dependent_on_another_return is false for a dependent claimed on this same return, so the dependency bar doesn't stop them.
  • Why Case 8 doesn't pin it. Case 8 ("a dependent's wages do not meet the claimant's earned income test") uses a 17-year-old dependent. The 18-or-older test rejects her whatever filer does.
  • Mutation. With filer & deleted, the PR's tests still pass: all 24 wi_homestead_eligible.yaml cases, all 4 wi_homestead_credit.yaml cases, and the property test, which draws only head/spouse couples.
  • Witness (2025): a single parent aged 40 with no earnings, $1,000 of property tax, and a 19-year-old full-time student dependent earning $3,000.
    • At the head: wi_homestead_eligible is false.
    • Under the mutant: true.
  • The law. The student is "claimed as a dependent for federal income tax purposes by another person" and is under 62 (§ 71.53(2)(d)). Earned income is the claimant's own (§ 71.52(1g)). So the head's answer, false, is right.
  • Fix. Add the witness as a case, or raise Case 8's dependent to 19. The case's comment and expectation stay the same.

S3. The PR follows Schedule H over the literal text of § 71.54(1)(gm) for the 62-or-older / disabled / earned income routes. That reading isn't recorded, and the comment cites (gm) alone for it.
wi_homestead_eligible.py:26-29

The comment says "Wis. Stat. 71.53(2)(c), 71.54(1)(gm): the claimant must be 18 or older and must be 62 or older, disabled or have earned income." The statute (§ 71.54(1)) says:

  • (dm): "Except as provided in subds. 5. and 7., [pars. (em) and (gm)] ... no credit may be allowed under this paragraph [subsection] unless the claimant or the claimant's spouse is over the age of 61 at the close of the year to which the claim relates."
  • (em): "... no credit may be allowed under this paragraph [subsection] unless the claimant is disabled."
  • (gm): "... with regard to a claimant who is not disabled or who is under the age of 62 at the close of the year to which the claim relates, no credit may be allowed under this paragraph [subsection] if the claimant had no earned income ..."
  • The Legislative Reference Bureau notes under each paragraph say the cross-references are wrong and "Corrective legislation is pending."

How the readings differ:

  • Literal (gm). It bars a claimant 62 or older who isn't disabled and has no earned income. It also bars a disabled claimant under 62 with no earned income.
  • 2025 Schedule H, Step 1a (p. 4). It gives three alternative routes: "Are age 62 or older ...", "Are disabled ...", "Have earned income".
  • The code. It follows Schedule H (as main did), which is the reasonable whole-text reading. CONTRIBUTING.md#source-conflicts still requires recording it: "When a PR takes such a reading, or finds one already in the code, the conflict goes into the register". This PR rewrites the formula and attaches the rule to (gm).
  • Already identified. The PR's own rulespec-us#2031 states the conflict: "Read alone, (gm)'s 'or' would bar a claimant of 62 or older who isn't disabled and has no earned income ..."

Fix:

  • Register. Add a source_conflicts/candidates.yaml item, for example us-wi:homestead-age-disability-earned-income-routes.
    • Sources: § 71.54(1)(dm)–(gm) (statute) and the 2025 Schedule H instructions, Step 1a (instructions).
    • follows: the reading the code takes. That's agency, or statute if you rest it on the whole-text canon.
    • code: wi_homestead_eligible.py.
  • Comment. Cite (dm), (em) and (gm) together. Add a KNOWN_UNMARKED line if the reworded comment trips the ratchet.
  • Reference tuple. It links only 71/viii/53/2/c. Link § 71.53(2) so (d), the dependency bar, is covered.

S4. The Act 138 references name the wrong section and use a page that doesn't show the values.
in_effect.yaml:10-11, income_limit.yaml:12-13, and the variable's reference tuple at vt_student_loan_interest_subtraction.py:11-13

  • Wrong section. The titles cite "2021, No. 138 (Adj. Sess.), §§ 5, 6" at #page=4. In the Act, Sec. 5 amends § 5811 to add (21)(B)(vi) (PDF page 4) and (29) (PDF page 5). Sec. 6 amends (21)(B)(iv), the retirement income exclusion, which has nothing to do with student loans.
  • Pages that don't show the values. The $120,000 / $200,000 limits are on PDF page 5. The effective date ("shall take effect retroactively on January 1, 2022 and shall apply to taxable years beginning on and after January 1, 2022") is in Sec. 13(b) on PDF page 15. Page 4 shows neither.
  • Fix. Follow the CLAUDE.md multi-page rule:
    • Titles: "2021, No. 138 (Adj. Sess.), §§ 5, 13(b) #page 4-5,15" (quoted, href #page=4). In income_limit.yaml, the shorter "... § 5 #page 4-5" is enough.
    • Variable reference tuple: put # PDF pages 4-5, 15 above the href.

Suggestions

  1. Utah reference version (ut_at_home_parent_credit_potential.py:13). The new link is the version effective 5/6/2026. For 2022–2025, the governing text is the version effective 3/24/2022 (C59-10-S1005_2022032420220324.pdf#page=1). Subsection (2) reads the same in both, but cite the earlier version for the claimed years.
  2. Vermont sources.
    • tax.vermont.gov served Income-Booklet-2025.pdf to a plain request, byte-identical to the NBER copy. The official URL could replace the archive link, and the body's note that the site refuses downloads can go.
    • The federal offset also has a textual basis in § 5811(21)(B)'s lead-in, "(to the extent such income is included in federal adjusted gross income)". It's worth citing next to the no-double-deduction canon.
  3. Surviving-spouse candidate. The same Act, in Sec. 7 (PDF page 5), amends § 5830e(a)(1) to "single, married filing separately, head of household, or qualifying widow or widower surviving spouse". Two sections after (29)(B), it names surviving spouses where it includes them. That's strong support for the statute reading and worth adding as a source.
  4. Property test.
    • The 2021 Vermont draw is vacuous: Hypothesis's minimal example has no interest.
    • limit copies the parameter's -np.inf.
    • Cost is fine: about 1.7 s in Rest (Python shard 4). Per CLAUDE.md "Test design and CI cost", say in the body which CI group runs it and what it costs.
  5. Married filing separately above the limit. A married filing separately case above $120,000 would kill a SEPARATE: 200_000 mutant, which currently survives. This one is optional.
  6. Follow-ups that main shares, outside this PR.
    • Utah earned income. The at-home parent test uses earned_income. § 59-10-1005(1)(a)(iii) counts "total wages, tips, and other compensation listed on all of the parent's federal Forms W-2" plus "the gross income listed on the parent's federal Form 1040 Schedule C".
    • Wisconsin earned income. earnings_sources includes partnership_s_corp_income and gross self-employment income. § 71.52(1g) counts wages plus net earnings from self-employment after the § 164(f) deduction.
    • Wisconsin credit amount. The credit uses a continuous 80%. § 71.55(9) makes Table B determining, and Table B gives $804 for $1,000 of taxes (2025 Schedule H instructions, p. 26). The four new wi_homestead_credit: 800 expectations pin the approximation.
  7. PR body.
    • The earned income definition is section F Implement Section 8 Project-Based Rental Assistance #4 (instructions p. 7). Page 10 holds the note "The spouse that meets this definition must be the claimant."
    • "Wisconsin applies each test ... to the claimant" overlooks that (dm) reaches "the claimant or the claimant's spouse". It's result-equivalent here, because a spouse 62 or older always qualifies as the claimant.

Verified correct

  • Vermont law.
    • § 5811(21)(B)(vi) and (29) match the current published text and Act 138 Sec. 5.
    • The limits are inclusive ("equal to or less than"), and § 13(b) starts the subtraction in 2022.
    • The 2021 Schedule IN-112 has no student loan line. The 2022 instructions (lines 15a–15c) and 2025 instructions (lines 16a–16c, booklet PDF pages 23 and 33) subtract interest paid minus "Student loan interest already deducted on federal Form 1040, Schedule 1, Line 21".
    • The surviving-spouse reading (−∞ limit) has a candidate with all required fields, and KNOWN_UNMARKED has the matching line.
  • Vermont code.
    • Only head and spouse interest counts, and the dependent case pins that.
    • student_loan_interest_ald is head/spouse-only and capped per return, so the offset never goes negative.
    • in_effect is pinned by the 2021 $4,000 case, and every filing-status limit except married filing separately above the limit is pinned at and above the limit.
  • Utah. § 59-10-1005(2) (versions effective 3/24/2022 and 5/6/2026) pays per qualifying child once per return. has_at_home_parent = add(...) > 0 gives exactly that, and both two-parent cases pin it ($400→$200, $200→$100).
  • Wisconsin.
    • § 71.52(1) lets the couple choose the claimant, so tax_unit.any(...) over head and spouse is right.
    • § 71.53(2)(c) (18+) and § 71.53(2)(d), the dependency bar with its 62-or-older exception, are applied per claimant using claimed_as_dependent_on_another_return, an actual claim, which matches Step 2a ("Were you claimed or will you be claimed").
    • § 71.52(1g) counts the claimant's own earned income, and § 71.53(1)(c) allows one claimant per household.
    • (dm)'s "claimant or the claimant's spouse" age route gives the same result, since a spouse 62 or older always qualifies as the claimant.
    • Every branch except filer (S2) is pinned.
  • Vectorization and entities.
    • All three formulas are array-safe: tax_unit.sum, add, max_, enum indexing on federal filing_status, tax_unit.any, add(person, ...).
    • No new state-gated int or enum key, so the NH Add New Hampshire state supplementation for 2026 #10156 pattern doesn't apply.
    • Periods are YEAR throughout.
  • Housekeeping.
    • The changelog fragment vt-ut-wi-statutory-fixes.fixed.md is at the top level.
    • Tests are in variable-named YAML files.
    • No partner test files changed.
    • The property file is in the established tests/core Hypothesis pattern and costs about 1.7 s in CI.

Hand-checks (each matches the file):

  1. VT 2025, single, wages $10,000, interest $1,000: federal min($1,000, $2,500) = $1,000, with MAGI under $85,000 so no phase-out. AGI is $9,000, so the subtraction is $1,000 − $1,000 = $0 and vt_agi is $9,000.
  2. VT 2025, single, $50,000 / $4,000: federal $2,500. AGI $47,500 ≤ $120,000, so the subtraction is $4,000 − $2,500 = $1,500.
  3. VT 2025, joint, $195,000 / $3,000: federal $2,500 × (1 − $25,000 / $30,000) = $416.67. AGI $194,583.33 ≤ $200,000, so the subtraction is $3,000 − $416.67 = $2,583.33.
  4. VT 2022, joint at exactly $200,000: the 2022 joint phase-out ends at $175,000, so federal is $0. $200,000 ≤ $200,000, so the subtraction is $3,000.
  5. VT 2025, surviving spouse, $50,000 / $4,000: federal $2,500 (non-joint phase-out $85,000–$100,000), so AGI is $47,500. The limit is −∞, so the subtraction is $0.
  6. VT 2025, married filing separately at $120,000: federal $0 (§ 221(e)(2)). $120,000 ≤ $120,000, so the subtraction is $3,000.
  7. VT 2021, $50,000 / $4,000: federal $2,500. in_effect is false, so the subtraction is $0 and vt_subtractions is $0.
  8. UT 2025, joint, wages $2,000 and $1,000, one newborn: AGI $3,000 ≤ $50,000, both spouses ≤ $3,000. $100 × 1 child × (any at-home parent) = $100 (main gave $200).
  9. UT 2022, two eligible parents and two children: $100 × 2 = $200 (was $400).
  10. WI 2025, 18-year-old with no earnings and 17-year-old earning $5,000: the head is 18 but has no route; the spouse earns but is 17. So false (main: true).
  11. WI 2025, joint, both 30, $500 each, the head claimed: the spouse qualifies. Household income $1,000 ≤ $8,060, so the credit is 0.8 × ($500 + $500) = $800.
  12. WI 2025, both claimed, both 62: the 62-or-older exception applies, so eligible, $800.
  13. WI 2024, Case 8: the 40-year-old's $6,000 is unemployment, which isn't earned income; the 17-year-old dependent isn't a filer. So false.
  14. WI 2024, Case 14: the spouse is 65, so she qualifies through age. Household income is $9,000 of Social Security, under $24,680, so true.

Axiom line assessment

axiom: TheAxiomFoundation/rulespec-us#2029 queued ...; #2030 queued ...; #2031 queued ... is valid.

CI status

All 36 checks pass at 6f203e8ba3 (run 41947821257), rechecked just now. No failures, nothing pending. The new property file ran in Rest (Python shard 4) in about 1.7 s.

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.

Requesting changes: 1 critical (the merge with main rebuilt Vermont's 2021 subtraction list without vt_other_subtractions, which main has had since #10302; a 2021 filer's subtractions drop from $1,300 to $0; re-add it and pin a 2021 case), 4 should (the new VT student loan entry and the WI filer guard are unpinned; WI follows Schedule H's three routes over 71.54(1)(gm) without a record; the Act 138 page references are off). All three fixes otherwise match the law. Full report in the comment above.

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

Bring the branch up to current main so the review's fixes are checked
against it. The merge is clean.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
The merge with main put vt_other_subtractions only in the new 2022
block, so 2021 returns lost the subtraction main allows from 2021. Add
it back to the 2021 block and pin it with a 2021 case. The 2025
booklet link now points at tax.vermont.gov, which serves the same file.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
No case checked that the subtraction reaches vt_subtractions and
vt_agi, so dropping it from the 2022 list passed every test. The $4,000
case now checks both. A married filing separately case above $120,000
pins that limit from above.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
Sec. 6 of Act 138 amends the retirement exclusion, and page 4 shows
neither the limits nor the start date. Cite Sec. 5 (pages 4-5) and
Sec. 13(b) (page 15) instead. Link the booklet on tax.vermont.gov,
quote the (21)(B) lead-in, and add Sec. 7 to the surviving spouse
candidate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
Case 8's dependent is 17, so the age test rejected that dependent
whatever the filer guard did, and deleting the guard passed every case.
A 19-year-old dependent who earns now pins it: the parent claims the
student, so the student can't be the claimant.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
The formula lets age 62, disability or earned income qualify a claimant,
as Schedule H Step 1a does, while 71.54(1)(gm) read by itself would deny
a claimant with no earned income unless disabled and 62 or older. Record
the reading as a candidate, cite (dm), (em) and (gm) together, and link
71.53(2) so the dependency bar's paragraph is covered.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
The only statute link was the version effective 5/6/2026. The version
effective 3/24/2022 governs the earlier claimed years, and its
subsection (2) reads the same.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
The 2021 draw was Hypothesis's minimal example with no interest, so it
tested nothing; the YAML cases pin 2021. The limit also copied the
parameter's -inf. Assert the $200,000 and $120,000 limits and the
surviving spouse rule as the statute states them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01N2tyHP3hjAvuftmiMnzT62
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
dea8148

This round fixes DTrim99's critical item and all four should-address items, and does six of the seven suggestions. The seventh (shared follow-ups) becomes three proposed issues. The head was 6f203e8ba3 when I started, and no one else pushed during the round. I pushed once.

Commits since 6f203e8ba3:

  • 2dd1e70cca merges main (clean).
  • ea6c5af2e1 restores vt_other_subtractions in the 2021 list (C1).
  • cc13aef636 pins the 2022 list entry (S1) and the married filing separately limit from above (suggestion 5).
  • 92dc46da4a fixes the Act 138 references (S4) and does suggestions 2 and 3.
  • ea0f8f0183 pins the Wisconsin filer guard (S2).
  • dea2c568ad records the Wisconsin reading (S3).
  • 0a3099202a adds the Utah 2022 statute version (suggestion 1).
  • dea8148b78 changes the Vermont property test (suggestion 4).

1. Main

  • The earlier merge. git show --remerge-diff 99ae1e525a is empty, so git merged it without a manual resolution. Between its merge base f2dac929f5 and its parents, only subtractions.yaml changed on both sides. Main had added vt_other_subtractions after salt_refund_income; git put that line after the PR's new 2022 block, so the 2021 block lost it.
  • Every list that merge rebuilt. subtractions.yaml holds two lists. The 2022 block matches main's list, with student_loan_interest replaced by vt_student_loan_interest_subtraction. The 2021 block lacked vt_other_subtractions (C1) and student_loan_interest; the PR drops the latter on purpose, since Act 138 § 13(b) starts the subtraction in 2022. The reference list kept all of main's entries. No other file changed on both sides.
  • This round's merge. GitHub reported no conflict, but the review's critical item is a failure on the merged tree, so I merged main (ba9bfdea75) with a merge commit at 2dd1e70cca. It was clean. Two lists changed on both sides: candidates.yaml and KNOWN_UNMARKED in test_source_conflicts.py. git diff origin/main 2dd1e70cca shows only this PR's own additions in both. The Vermont candidate and its KNOWN_UNMARKED line stay in sorted order.
  • Main's test the review names. vt_other_subtractions.yaml: 3 passed at the final head (main's two 2024 cases and the new 2021 case).

2. Findings

I reproduced each finding at the merged head before changing code.

  • C1. My 2021 witness (vt_other_subtractions $1,300) gave vt_subtractions 0 against 1,300; the 2024 control gave 1,300. Fix: subtractions.yaml:9. Pin: vt_other_subtractions.yaml:16 (Case 3, 2021: vt_subtractions 1,300).

  • S1. Deleting the 2022 list line passed every case; my witness then gave vt_subtractions 0 against 1,500. Fix: vt_student_loan_interest_subtraction.yaml:44-45 adds vt_subtractions: 1_500 and vt_agi: 46_000 to the $4,000 case.

  • S2. The witness gives false at the head and true with filer & deleted. It needs is_tax_unit_dependent: true on the student: without it, the model treats a member 18 or older as the spouse. Fix: a new case at wi_homestead_eligible.yaml:413. I added a case rather than raising Case 8's dependent to 19, so the carried Leave dependents' income out of Wisconsin homestead credit household income #10191 block stays byte-identical to Leave dependents' income out of Wisconsin homestead credit household income #10191's head 4c7110c3b5.

  • S3. Neither candidates.yaml nor the pinned register had a Wisconsin homestead item. Fixes:

    • candidate us-wi:homestead-age-disability-earned-income-routes at candidates.yaml:11481 (see Rulings);
    • the comment at wi_homestead_eligible.py:26-30 now cites (dm), (em) and (gm) together;
    • the reference at wi_homestead_eligible.py:15 now links § 71.53(2);
    • the reworded comment doesn't trip the ratchet, so KNOWN_UNMARKED needs no line (test_source_conflicts.py: 103 passed).

    No result changes, so no new case can fail without the fix. Main's unit tests 4 and 6 and Cases 14 and 15 pin the reading the code takes (mutant M8 below).

  • S4. I read Act 138 from legislature.vermont.gov:

    • Sec. 5 adds (21)(B)(vi) on PDF page 4 and (29) on page 5.
    • Sec. 6 amends (21)(B)(iv), the retirement exclusion.
    • Sec. 13(b), on page 15, applies "Secs. 1–8" to "taxable years beginning on and after January 1, 2022".

    Fixes: in_effect.yaml:10 ("§§ 5, 13(b) #page 4-5,15"), income_limit.yaml:12-13 ("§§ 5, 13(b) #page 5,15", href #page=5), and the variable's tuple at vt_student_loan_interest_subtraction.py:12-14 (# PDF pages 4-5, 15). The effective-date comment at vt_student_loan_interest_subtraction.py:45-47 now cites § 13(b). No result changes.

  • Suggestion 1. ut_at_home_parent_credit_potential.py:13-16 cites the versions effective 3/24/2022 and 5/6/2026. I fetched both PDFs, and subsection (2) is identical in each.

  • Suggestion 2. tax.vermont.gov served Income-Booklet-2025.pdf, byte-identical to the NBER copy (sha256 109b0c88...23f1). The official link replaces NBER's in subtractions.yaml:25, income_limit.yaml:15, vt_student_loan_interest_subtraction.py:17 and the Vermont candidate's form source. The body's note on refused downloads is gone. The (21)(B) lead-in quote is at vt_student_loan_interest_subtraction.py:23-26 and in the body.

  • Suggestion 3. The Vermont candidate gains Act 138 §§ 5, 7 as a source (candidates.yaml:11320-11322) and a note sentence. Current § 5830e(a)(1) reads "single, married filing separately, head of household, or surviving spouse".

  • Suggestion 4. The property's years are now 2022, 2024 and 2026 (test_vt_ut_wi_statutory_invariants.py:103). It asserts the statute's limits directly (lines 118-121), and the docstring says the YAML pins 2021.

    • The three derandomized examples are a 2022 minimal example (eight single units with zeros), a 2026 example (20 units: 8 surviving spouses, 7 single, 5 joint) and a 2022 example (8 units, 2 surviving spouses).
    • The minimal example is still vacuous. The other two carry the property.
    • O1 still fails the property, at assert not sub[surviving].any().
    • CI group: Rest Python. At 6f203e8ba3 it ran in Rest (Python shard 4), and its tests took 0.92 s, 0.36 s and 0.36 s.
    • Locally, the file took 73 s of wall time before the change and 71 s after, with a 1,510 MB peak each time (Python 3.14, 4 vCPU; local figures, not Linux CI).
  • Suggestion 5. A married filing separately case at $125,000 (vt_student_loan_interest_subtraction.yaml:365) kills the SEPARATE: 200_000 mutant (M9).

  • Suggestion 7. The body's Readings item 1 now cites section F Implement Section 8 Project-Based Rental Assistance #4 (p. 7) for the definition and the page 10 note, and says (dm)'s age route also counts the spouse.

3. Rulings for the hub

  1. us-wi:homestead-age-disability-earned-income-routes: follows: statute, state: open.
    • The code treats age 62 or older, disability and earned income as alternatives, as Schedule H Step 1a does and as main did.
    • I propose statute on the whole-text canon: (dm) makes (em) and (gm) exceptions to its age bar. The review offered agency too, but the guard's FOLLOWS set has no agency. If you'd rather call it conflicting-source (Step 1a over (gm) read alone), change follows and keep the readings.
    • years.from is 2021, the first year of the model's homestead parameters, because I couldn't read when (gm) starts.
    • Sources read. The 2025 Schedule H instructions, pages 4, 7, 10 and 26, from revenue.wi.gov.
    • Not read. Wis. Stat. 71.52, 71.53 and 71.54. docs.legis.wisconsin.gov reset every connection and returned 503 to a second fetch route. web.archive.org was refused, and Justia and FindLaw returned 403.
    • So the candidate's account of (dm), (em) and (gm) rests on DTrim99's quotes, and so do the Wisconsin statute quotes earlier rounds put in the body. Please read (dm)-(gm) and their notes before ruling.
  2. Reversals. None. The Vermont candidate keeps its reading and ruling and gains a source. No reading in the body, candidates.yaml or an earlier review changes.

4. Axiom line

DTrim99 found it valid, so I kept it. Drafts for the hub to file:

5. Hand computations

Each matches the file.

  • C1, 2021: vt_other_subtractions $1,300 is the only subtraction, so vt_subtractions is $1,300.
  • S1, 2025 single, $50,000 and $4,000: the federal deduction is min($4,000, $2,500) = $2,500, so federal AGI is $47,500. That's within $120,000, so the subtraction is $4,000 − $2,500 = $1,500 and Vermont AGI is $47,500 − $1,500 = $46,000.
  • S2, 2025: the parent is 40, not disabled and has no earnings, so no route. The student is a dependent on this return, so not a filer, and is barred as claimed by another person. So false, and the credit is 0.
  • Suggestion 5, 2025 married filing separately, $125,000 and $3,000: the model gives no federal deduction, as the existing $120,000 case expects. $125,000 is above $120,000, so the subtraction is 0.

6. Mutants

Each ran in its own process at the final head against the named file and was restored with git checkout. git status was clean after each.

Mutant Undoes File Result Killed by
M5: drop vt_other_subtractions from the 2021 list C1 vt_other_subtractions.yaml 1 of 3 fail (0 vs 1,300) Case 3, 2021
M6: drop vt_student_loan_interest_subtraction from the 2022 list S1 vt_student_loan_interest_subtraction.yaml 1 of 16 fail (vt_subtractions 0 vs 1,500) the $4,000 case
M7: drop filer & S2 wi_homestead_eligible.yaml 1 of 25 fail (true vs false) the 19-year-old dependent case
M8: (gm) read by itself: (elderly & disabled) | (earnings > 0) S3's reading wi_homestead_eligible.yaml 4 of 25 fail unit tests 4 and 6, Cases 14 and 15
M9: SEPARATE $200,000 suggestion 5 vt_student_loan_interest_subtraction.yaml 1 of 16 fail (3,000 vs 0) the separate case at $125,000
O1: SURVIVING_SPOUSE $120,000 suggestion 4 kept its strength test_vt_ut_wi_statutory_invariants.py fails the surviving spouse assertion

S4 and suggestions 1-3 change references, comments and the register only, so no mutant applies.

7. Tests and CI

At the final head, one process each:

File Result
vt_other_subtractions.yaml 3 passed
vt_student_loan_interest_subtraction.yaml 16 passed
wi_homestead_eligible.yaml 25 passed
ut_at_home_parent_credit.yaml 5 passed
test_vt_ut_wi_statutory_invariants.py 3 passed
test_source_conflicts.py 103 passed

ruff format left the changed Python files unchanged, and ruff check passes.

CI: the Pull request run at dea8148b78, 42642861775, was in progress when I posted this. I didn't wait for it, as the brief says. No job had been skipped or cancelled yet.

8. Body

Every section now describes dea8148b78:

impact: the hub will measure is now impact: pending a comparison at the final head. The axiom: line is unchanged.

9. Judgment calls

10. Proposed follow-up issues

  1. Use W-2 wages and Schedule C gross income in Utah's at-home parent earnings test. § 59-10-1005(1)(a)(iii) sums W-2 wages, tips and compensation and "the gross income listed on the parent's federal Form 1040 Schedule C". ut_at_home_parent_credit_earned_income_eligible_person compares the person's earned_income with the $3,000 limit instead.
  2. Match Wisconsin homestead earned income to § 71.52(1g). Schedule H definition Implement Section 8 Project-Based Rental Assistance #4 counts "Wages, salaries, tips, other employee compensation, and net earnings from self-employment". The earnings_sources list also counts partnership_s_corp_income and self_employment_income. DTrim99 reads § 71.52(1g) as wages plus net earnings from self-employment after the § 164(f) deduction; check each source against it.
  3. Compute the Wisconsin homestead credit from Table B. The model multiplies allowable taxes by a flat 0.8 (rate.yaml), while 2025 Table B (instructions p. 26) gives $804 for $1,000 to $1,010 of taxes. The four wi_homestead_credit: 800 expectations in this PR pin the flat rate.

After this PR merges, the two candidates need the register steps in CONTRIBUTING.md (2 to 4).

DISMISSAL MAP

  • C1 (2021 list lost vt_other_subtractions): FIXED at subtractions.yaml:9; pinned by vt_other_subtractions.yaml:16; M5 killed.
  • S1 (2022 list entry unpinned): FIXED at vt_student_loan_interest_subtraction.yaml:44-45; M6 killed.
  • S2 (filer guard unpinned): FIXED with a new case at wi_homestead_eligible.yaml:413; M7 killed. Case 8 is unchanged on purpose.
  • S3 ((gm) reading unrecorded): FIXED at candidates.yaml:11481, wi_homestead_eligible.py:26-30 and :15. No KNOWN_UNMARKED line is needed. The ruling is for the hub (Rulings 1).
  • S4 (Act 138 section and pages): FIXED at in_effect.yaml:10, income_limit.yaml:12-13, vt_student_loan_interest_subtraction.py:12-14 and :45-47.
  • Suggestion 1 (Utah version): DONE at ut_at_home_parent_credit_potential.py:13-16.
  • Suggestion 2 (official booklet; (21)(B) lead-in): DONE at subtractions.yaml:25, income_limit.yaml:15, vt_student_loan_interest_subtraction.py:17 and :23-26, the Vermont candidate, and the body.
  • Suggestion 3 (Sec. 7 source): DONE at candidates.yaml:11320-11322 and in body Readings 3.
  • Suggestion 4 (property 2021 draw, -np.inf, CI cost): DONE at test_vt_ut_wi_statutory_invariants.py:103 and :118-121 and in the body's Invariants.
  • Suggestion 5 (separate filer above the limit): DONE at vt_student_loan_interest_subtraction.yaml:365; M9 killed.
  • Suggestion 6 (follow-ups main shares): FOLLOW-UP, proposed issues 1 to 3 above.
  • Suggestion 7 (body: definition page; (dm) spouse): DONE in body Readings 1.
  • Axiom assessment (refresh the issues' head SHA; add the S1 and S2 cases): drafted in section 4 for the hub to file.
  • Branch status (Leave dependents' income out of Wisconsin homestead credit household income #10191 overlap; Vermont child tax credit in Phase out Vermont's child tax credit per child #10644): the body says Phase out Vermont's child tax credit per child #10644 merged on 2026-11-08, and Leave dependents' income out of Wisconsin homestead credit household income #10191's block still matches its head.

Generated by Claude Code

@DTrim99

DTrim99 commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Fix Vermont student loan interest, Utah's at-home parent credit and Wisconsin's homestead claimant

Summary

Round 1 (head 6f203e8ba3) asked for changes: 1 critical, 4 should-address and 7 suggestions. The author has since pushed seven commits and one clean merge of main, which brings the head to dea8148b78. All five blocking findings are fixed:

  • C1. vt_other_subtractions is back in Vermont's 2021 list, and a new 2021 case pins it.
  • S1. The 2022 list entry is pinned through vt_subtractions and vt_agi.
  • S2. The Wisconsin filer guard is pinned by a case with a 19-year-old dependent who has earnings.
  • S3. The Wisconsin reading of (dm), (em) and (gm) is recorded as a candidate, and the comment and reference are corrected.
  • S4. The Act 138 references now cite §§ 5 and 13(b) on the right pages, in the CLAUDE.md format.

For each fix that changes behavior, deleting the fix makes a test fail. Six of the seven suggestions are done; the seventh became three proposed follow-up issues.

No formula changed this round. In the three variables, the commits change only comments and references. The one behavior change is the C1 restore, which puts back main's behavior.

The fixes introduce no new problems:

CI passes all 36 checks at the head.

Three suggestions remain, none blocking:

  • refresh the three queued rulespec-us issues to the new head;
  • optionally widen the new candidate's years;
  • file the proposed follow-up issues.

Prior findings

Finding Status Evidence
C1. The 2021 Vermont list lost vt_other_subtractions in the merge with main Fixed subtractions.yaml:9 restores it. The 2021 block now equals main's list minus student_loan_interest; the PR drops that entry on purpose, since Act 138 § 13(b) starts the subtraction in 2022. vt_other_subtractions.yaml Case 3 (2021: vt_other_subtractions $1,300 gives vt_subtractions $1,300) pins it. That input is the case's only one, so deleting the list line gives $0 and the case fails.
S1. No test pinned the 2022 entry for vt_student_loan_interest_subtraction Fixed vt_student_loan_interest_subtraction.yaml:44-45 adds vt_subtractions: 1_500 and vt_agi: 46_000 to the $4,000 case. No other entry in the list is nonzero for that filer, so deleting the line gives vt_subtractions $0 and the case fails.
S2. No test pinned the Wisconsin filer guard (Case 8's dependent is 17) Fixed A new case at wi_homestead_eligible.yaml:413 covers a parent aged 40 with no earnings who claims a 19-year-old full-time student earning $3,000. Without filer &, the student qualifies: 19 ≥ 18, $3,000 > 0, and claimed_as_dependent_on_another_return is an input that defaults to false. Household income of $3,000 is below $24,680, so the mutant gives eligible / $800 against the expected false / $0. The law is § 71.53(2)(d), which I re-read today: "The claimant was claimed as a dependent for federal income tax purposes by another person ... but this limitation shall not apply if the claimant was 62 years of age or older". Adding a new case instead of editing Case 8 keeps the block carried from #10191 byte-identical, which is a sensible choice.
S3. The Schedule H reading of § 71.54(1)(dm)-(gm) wasn't recorded, and the comment cited (gm) alone Fixed Candidate us-wi:homestead-age-disability-earned-income-routes (candidates.yaml:11481) has statute and instructions sources, both readings, an example, the code path and the PR, with follows: statute and state: open. statute is a fair label: the guard's FOLLOWS set has no agency value, and the whole-text reading is the one the code takes. open leaves the ruling to the project lead. The comment at wi_homestead_eligible.py:27-31 now cites (dm), (em) and (gm) together, and the reference tuple links § 71.53(2). The reworded comment doesn't match the ratchet pattern, and test_source_conflicts.py passes in CI. The reading itself is pinned: under (gm) read alone, unit tests 4 (2021, head aged 62, no earnings) and 6 (2022, disabled spouse aged 60, no earnings) both turn false.
S4. The Act 138 references cited the wrong section and page Fixed in_effect.yaml:10: "2021, No. 138 (Adj. Sess.), §§ 5, 13(b) #page 4-5,15", href #page=4. income_limit.yaml:12-13: "... §§ 5, 13(b) #page 5,15", href #page=5; page 5 shows the limits and page 15 the start date. The variable tuple at vt_student_loan_interest_subtraction.py:12-14 has # PDF pages 4-5, 15 above the href. All three follow the CLAUDE.md multi-page rule. The effective-date comment at lines 45-47 now cites § 13(b).
Suggestion 1: Utah 2022 statute version Done ut_at_home_parent_credit_potential.py:13-16 cites the 3/24/2022 and 5/6/2026 versions. The 2022 PDF resolves.
Suggestion 2: official booklet; the (21)(B) lead-in Done The tax.vermont.gov booklet link resolves and replaces the NBER copy in all four places. The lead-in "to the extent such income is included in federal adjusted gross income" is quoted at vt_student_loan_interest_subtraction.py:23-26.
Suggestion 3: Act 138 Sec. 7 for the surviving spouse Done The Vermont candidate gains the source "§§ 5, 7 (32 V.S.A. §§ 5811(29), 5830e(a)(1))" at #page=5, plus a note sentence.
Suggestion 4: property test Done Draws are now 2022, 2024 and 2026 (line 103). The limits are asserted directly (lines 119-121), and the docstring says the YAML pins 2021. The body gives the CI group (Rest Python, shard 4) and its cost. The minimal example is still all zeros, as the author says; the other two examples carry the property.
Suggestion 5: married filing separately above the limit Done vt_student_loan_interest_subtraction.yaml:365 kills a SEPARATE: 200_000 mutant (3,000 vs 0).
Suggestion 6: follow-ups that main shares Deferred, reasonably The hand-off drafts three issues: the Utah W-2 / Schedule C earnings test, Wisconsin earned income under § 71.52(1g), and Wisconsin Table B. They aren't filed yet; see Suggestion 3 below.
Suggestion 7: PR body Done Readings 1 cites section F #4 (p. 7) and the p. 10 note, and says (dm)'s age route also counts the spouse.

New findings

Critical: none.

Should address: none.

Suggestions

  1. Refresh the three queued rulespec-us issues. Nebraska state income tax #2029, Nebraska state income tax #2030 and Make survey-enhance a dev dependency #2031 still name head 6f203e8ba3.

    The hand-off drafts the right comments, but none has been posted (no comments; last updated 2026-11-05 04:53 UTC). This isn't blocking: no formula changed since 6f203e8ba3, so each issue still describes the head's behavior exactly. Post the drafts, which add:

  2. Candidate years (optional). The hand-off says the author couldn't read when (gm) starts. I read § 71.54(1) at docs.legis.wisconsin.gov today. Each of (dm), (em) and (gm) applies "for claims filed in 2018 and thereafter and based on property taxes accrued or rent constituting property taxes accrued during the previous year". So the conflict dates from tax year 2017.

    • Either set years.from: 2017, or keep 2021 and say in the note that the model starts there.
    • The candidate's quotations, and those in Make survey-enhance a dev dependency #2031, match the published text, including the notes "The correct cross-references are shown in brackets. Corrective legislation is pending." The project lead can rely on them when ruling.
  3. File the three follow-up issues proposed in the hand-off (Suggestion 6), so the shared Utah and Wisconsin gaps are tracked once this merges.

Verified

Hand-checks of every new or changed expectation

  1. VT 2021, Case 3: vt_other_subtractions $1,300 is the only list input, so vt_subtractions = max(0, $1,300) = $1,300.
  2. VT 2025, single, wages $50,000, interest $4,000:
    • the federal deduction is min($4,000, $2,500) = $2,500, with MAGI under $85,000, so no phase-out;
    • federal AGI is $47,500, within $120,000, so the subtraction is $4,000 − $2,500 = $1,500;
    • the only list entry is the student loan subtraction, so vt_subtractions is $1,500;
    • vt_agi is $47,500 − $1,500 = $46,000.
  3. VT 2025, married filing separately, wages $125,000, interest $3,000:
    • § 221(e)(2) gives no federal deduction to a separate filer, so AGI is $125,000 and the federal deduction is $0;
    • $125,000 is above $120,000, so the subtraction is $0.
  4. WI 2025, a parent aged 40 with $1,000 of property tax and a 19-year-old dependent student earning $3,000:
    • the parent is under 62, not disabled and has no earned income, so has no route;
    • the student is a dependent on this return, so not a filer, and § 71.53(2)(d) bars a claimant claimed by another person;
    • so the household is not eligible, and the credit is $0.
  5. Property test, 2022, 2024 and 2026: the assertions use $200,000 for joint and $120,000 otherwise, exclude surviving spouses from within, and require zero for every surviving spouse, as § 5811(29)(B) reads.

Law and sources

  • I re-read Wis. Stat. § 71.54(1)(dm), (em) and (gm), and § 71.53(2)(c) and (d), at docs.legis.wisconsin.gov.
  • The new links resolve: the Utah 2022 statute PDF, the tax.vermont.gov 2025 booklet and § 71.53(2).

Scope of the fix commits

  • In the three variables, the commits change only comments and references.
  • The behavior changes are the 2021 list restore and new tests.
  • The merge 2dd1e70cca has no hand-resolved blocks: --remerge-diff is empty.

Main-side guards

Housekeeping

  • Tests stay in variable-named YAML files.
  • The changelog fragment is unchanged, at the top level and still accurate.

Axiom

axiom: TheAxiomFoundation/rulespec-us#2029 queued ...; #2030 queued ...; #2031 queued ... is still valid.

  • All three issues are OPEN, labelled pe-parity and dispatch-ready.
  • The behavior they describe for each provision is unchanged at dea8148b78.
  • The stale head SHA and case counts are Suggestion 1. They aren't blocking, because this round's fixes add tests, references and register entries but don't change what the modules must encode.

CI

All 36 checks pass at dea8148b78 (Pull request run 42642861775, rechecked just now). That includes the full baseline and contrib state shards, Rest Python shards 1 to 9, Household API Partners and PR validation. Nothing failed or is pending.

The run's merge ref included main through 3e5a1dff6b. The four later main commits don't touch this PR's files except candidates.yaml, where they edit separate Montana entries:

Branch

Verdict: APPROVE

@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.

Round 2: the critical (VT 2021 subtraction) and all four shoulds are fixed and pinned; the Wisconsin age/disability/earned-income reading is recorded as a candidate; CI is green. Three optional suggestions remain (refresh rulespec-us #2029-#2031, candidate start year, follow-up issues).

…tudent loan note

Act 138's Sec. 7 replaced "qualifying widow or widower" with "surviving spouse" in 32 V.S.A. 5830e(a)(1)'s list of filing statuses. The backlog note for us-vt:student-loan-interest-surviving-spouse-limit said that the section put surviving spouses in that list.

Co-Authored-By: Claude <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit 4a3ddef into main Nov 10, 2026
36 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10104 at 721449e5a0e69fb82ceb0bb092bb7336f193803e (squash): Fix Vermont student loan interest, Utah's at-home parent credit and Wisconsin's homestead claimant.

  • Reviews:
    • An independent review requested changes at 99ae1e525a, and a second approved a35d9295a3.
    • DTrim99 requested changes at 6f203e8ba3. A merge of main had dropped vt_other_subtractions from Vermont's 2021 list, so a 2021 filer's $1,300 subtraction fell to $0, and both earlier reviews had missed it. DTrim99 also asked for cases that pin the 2022 list entry and the Wisconsin filer guard, a record of the Wisconsin three-route reading, and corrected Act 138 references. Seven commits answer each, and DTrim99 approved dea8148b78.
    • An independent review of the changes since a35d9295a3 agreed at dea8148b78. It recomputed each new expected value, reproduced the merge of main byte for byte, and read Act 138, the Wisconsin statutes and both versions of the Utah statute.
    • 721449e5a0 applies that review's two-line correction to the Vermont backlog note: Act 138's Sec. 7 renamed the surviving spouse status in § 5830e(a)(1).
  • Gates checked live at merge:
    • gh pr checks exits 0;
    • the latest Pull request run at the head, 42758648036, passed and tested this head merged into main at ee466893c3;
    • MERGEABLE;
    • not a draft;
    • no reviewer's latest review requests changes;
    • the semantic-overlap guard: since ee466893c3, main has taken Limit self-employed plan contributions to the Pub. 560 maximum deduction #10121, which changed a Utah test file and source_conflicts/candidates.yaml. On this head merged with main at 3906867678, the PR's six changed test files, the Utah, Vermont and Wisconsin test folders (573, 1,179 and 621 cases), the guard test and the register test pass;
    • the merge pins the head.
  • Impact (default dataset, 2026, dea8148b78 against main at ba9bfdea75): we estimate that the Vermont fix raises Vermont income tax by $0.72m across 19 sampled tax units (11,687 weighted). In Wisconsin, 5 sampled tax units (4,193 weighted) lose homestead eligibility. No Utah output changes.
  • Next: Leave dependents' income out of Wisconsin homestead credit household income #10191 takes main's side of wi_homestead_eligible.py and its YAML file when it next merges main. The register step records the Wisconsin entry as ruled.
  • Axiom: TheAxiomFoundation/rulespec-us#2029, #2030 and #2031 queued, each now with a comment for this head.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis deleted the vt-ut-wi-statutory-fixes branch November 10, 2026 06:16
MaxGhenis added a commit that referenced this pull request Nov 10, 2026
#10104 rewrote Wisconsin's homestead claimant test, so this merge takes main's wi_homestead_eligible.py and wi_homestead_eligible.yaml whole: main's formula adds the filer guard and the dependency bar, and main's YAML already carries this branch's Cases 8 to 15 unchanged. The merge keeps this branch's household income, property tax and disqualified-loss changes, its sources.yaml list and its tests.

Co-Authored-By: Claude <noreply@anthropic.com>
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.

4 participants