Skip to content

Leave dependents' capital income out of state lines on the filers' return - #9894

Open
MaxGhenis wants to merge 14 commits into
mainfrom
dependent-capital-income-state-lines
Open

MaxGhenis wants to merge 14 commits into
mainfrom
dependent-capital-income-state-lines

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 6, 2026 •

Copy link
Copy Markdown
Contributor

What was wrong

irs_gross_income multiplies every income source by ~is_tax_unit_dependent, so a tax unit dependent's interest, dividends and capital gains never enter the filer's federal AGI or taxable income. The dependent reports them on their own return (2025 Form 8814, line 4; the model does not implement the Form 8814 election, 26 U.S.C. 1(g)(7)).

Many state lines that adjust the filers' federal AGI or taxable income summed a capital-income input over every tax unit member (add(tax_unit, ...) or adds = [...]). So a child's dividends, gains or interest moved the parents' state tax.

The two reported cases, on main:

TY2025 Without the child's income With it, main With it, this PR
ND. Head: wages 50,000, qualified dividends 1,000. Child: qualified dividends 5,000. nd_qdiv_subtraction 400 2,400 400
VT. Head: wages 150,000, long-term gain 10,000. Child: long-term gain 100,000, all VT-eligible. vt_percentage_capital_gains_exclusion 0 40,000 0
VT vt_capital_gains_exclusion 5,000 40,000 5,000
VT vt_income_tax (federal AGI 160,000 in every column) 6,623.20 4,800.00 6,623.20

How the leaks were found

  1. Static scan. Every add, aggr and adds/subtracts aggregator over a tax unit in variables/gov/states, with parameter-driven source lists resolved over all their dated values. 50 tax-unit sums include a person-level capital-income input. Child-care and TANF spm_unit sums count family income by design and are out of scope.

  2. Household sweep (every state, TY2021–2026). Five filer profiles: single worker at 60,000, a 70-year-old with Social Security and a pension, a 400,000 earner, an 82-year-old, and a 22,000 earner. Each has a dependent child aged 15 with 50,000 (300,000 for long-term gains) of one input:

    • qualified or other dividends;
    • long- or short-term gains;
    • collectibles gains;
    • capital gain distributions;
    • taxable, tax-exempt or U.S. government interest;
    • VT-eligible gains;
    • or wages, as a control.

    The sweep compares the filer's state_income_tax with and without the child's income, alongside federal AGI and federal tax. It ran on Use the filers' Schedule D for the tax on net capital gain #9855's head so the federal Schedule D leak Use the filers' Schedule D for the tax on net capital gain #9855 fixes stays out. A drill-down then listed every state variable that moved. Every change below was found this way. The same sweep, re-run on Use the filers' Schedule D for the tax on net capital gain #9855's head plus this PR, leaves only the cases under "Not in this PR".

What changes

All fixes count only the head and spouse, using tax_unit_non_dep_add for tax-unit sums and a dependent mask for per-person lines. The last four rows are corrections that also reach tax units without dependents; a no-dependent household sweep at this head (9 profiles, every state, 2021, 2022 and 2025) moved state_income_tax only through them.

State Dependent's income that moved the filer's tax Where
Most states U.S. government interest us_govt_interest, which most states subtract. It was adds = ["us_govt_interest_person"] over every member; tax-unit lists (incl. #9699's) add it as is. In the sweep a dependent's U.S. government interest moved the filer's tax in 38 states; after this PR it moves none except AL and IA before 2023 (structural), KY (#9699), and the MN, NM and OK credit paths under "Not in this PR".
VA U.S. government interest, plus the rest of the list va_subtractions (whole list) and va_subtractions_person (dependents get none). The list's other person items are already head/spouse-masked or AGI-prorated; a dependent's unemployment, railroad benefits and state tax refunds also stop being subtracted.
DC, DE, OH U.S. government interest dc_income_subtractions, de_subtractions, oh_deductions (per-person, summed into the filers' state AGI). Only the dependent's U.S. government interest is dropped; see "Not in this PR".
ND Qualified dividends; long-term gains; a short-term loss nd_qdiv_subtraction, nd_ltcg_subtraction
VT VT-eligible gains; non-Vermont municipal interest vt_percentage_capital_gains_exclusion, vt_additions
AZ Long-term gains; a short-term loss wiped the filer's subtraction az_long_term_capital_gains_subtraction
SC Gains sc_net_capital_gain_deduction
WI Gains and losses; tax-exempt interest wi_capital_gain_loss_subtraction, wi_capital_loss, wi_additions
MD Gains, triggering the 2% surtax md_capital_gains_surtax
WA Long-term gains (capital gains excise tax) wa_capital_gains_tax, wa_millionaires_tax_base_income
MT Gains (2024+ rates); 2021–23 capital gain credit, interest exemption shares, U.S. government interest and Social Security worksheet mt_capital_gains_tax_joint, ..._applicable_threshold_joint, mt_regular_income_tax_joint, mt_capital_gain_credit, mt_interest_exemption_person, mt_agi_joint, mt_taxable_social_security_joint
MA Dividends, interest and gains in Parts A and C. Part B starts from filer-only gross income, so a child's Part A income came off the parent's Part B. ma_part_a_gross_income, ma_part_a_agi, ma_part_c_gross_income, the four Part A dividend and exemption lines, the bank interest exemption
IL Tax-exempt interest il_base_income_additions
NH (through 2024) Interest and dividends nh_taxable_income
IA (2021–22) Tax-exempt interest and benefits in the Social Security worksheet ia_reportable_social_security
MN Tax-exempt interest and benefits in the Social Security worksheet mn_social_security_subtraction
UT Tax-exempt interest in the Social Security and retirement credits and the child tax credit ut_ss_benefits_credit_max, ut_retirement_credit_max, ut_ctc_potential
WV Tax-exempt interest in the low-income family credit wv_low_income_family_tax_credit_agi
ME Tax-exempt interest in the sales and property tax fairness credits me_sales_and_property_tax_fairness_credit_income
PA Tax-exempt interest in Tax Forgiveness eligibility income pa_eligibility_income
CA Gains and losses in the young child tax credit's net-loss test (FTB 3514 line 23b) ca_yctc, which now reads the filers' own filer_loss_limited_net_capital_gains and Schedule D line 16 (filer_schedule_d_lines)
GA Capital losses, which reduced an eligible filer's countable gains in the retirement exclusion ga_retirement_exclusion_countable_capital_gains, which now reads filer_loss_limited_net_capital_gains_person
DE Capital losses, which reduced the filer's eligible retirement income in the pension exclusion The pension exclusion's income_sources.yaml, which now lists filer_loss_limited_net_capital_gains_person
MI Interest, dividends and gains in the senior subtraction mi_interest_dividends_capital_gains_deduction
GA, DE Gains and losses in each person's loss-limited capital gains New filer_loss_limited_net_capital_gains_person: each head's or spouse's own gains and losses plus capital gain distributions, with the filers' net loss limited and shared in proportion. A dependent has none and takes no part in the loss limit, unlike loss_limited_net_capital_gains_person.
IA (2021–22) No dependent needed. The worksheet now adds line 2, half of the benefits. Witness, 2022, no dependents: age 70, Social Security 30,000, pension 20,000. ia_reportable_social_security 0 → 5,000; ia_income_tax 0.00 → 190.93 (main → this PR). The line changes in 2025 too, where it moved no tax in the sweep ia_reportable_social_security
VA No dependent needed. The National Guard subtraction is capped for each person rather than each return, and a dependent's pay no longer counts. Witness, 2025, no dependents: a couple aged 40, each with wages of 40,000 that include 6,000 of Guard pay (military_service_income, which the model doesn't add to federal AGI). va_national_guard_subtraction 5,500 → 11,000; va_income_tax 2,655.55 → 2,339.30 va_national_guard_subtraction, va_national_guard_subtraction_person
ND No dependent needed. The long-term gain subtraction is floored at zero and counts capital gain distributions. Witness, 2025, no dependents: wages 80,000, own long-term loss 3,000. nd_ltcg_subtraction −1,200 → 0. nd_income_tax stays 249.11, because nd_subtractions floors its total at zero; the floor moves tax only when other ND subtractions absorb the negative amount. Distributions do move tax: 2022, wages 60,000 and distributions 5,000 give nd_ltcg_subtraction 0 → 2,000 and state_income_tax 319.14 → 278.34 nd_ltcg_subtraction
VT No dependent needed. Eligible gains are limited to the filers' net capital gain less their 28% rate and unrecaptured section 1250 gains (Schedule IN-153 line 15), and the Form 4952 election's share on gains comes off them. Witness, 2022, no dependents: wages 60,000, long-term gain 10,000, all VT-eligible, short-term loss 8,000. vt_percentage_capital_gains_exclusion 4,000 → 800; state_income_tax 1,732.12 → 1,864.12 vt_percentage_capital_gains_exclusion

Where one of these formulas also summed a dependent's other amounts on the same line, they get the same rule:

  • Social Security benefits in the IA, MN and MT worksheets. A child's survivor benefits are the child's.
  • Student loan interest in MN's worksheet.
  • MT's other additions and subtractions in the joint AGI pool.

Three fixes in formulas edited anyway:

  • ND nd_ltcg_subtraction floors at zero and includes capital gain distributions. The Form ND-1 line 6 worksheet says "If zero or less, stop here; no exclusion is allowed", and puts distributions without Schedule D on line 3. Before, a filer's net long-term loss made a negative subtraction.
  • UT ut_retirement_credit_max counts only the head and spouse as claimants. An elderly dependent no longer adds a credit.
  • WV wv_low_income_family_tax_credit_agi now cites W. Va. Code 11-21-22a(d), the definition it implements, instead of 11-21-12(b).

Legal basis

Each line adjusts the individual's own federal AGI or taxable income, or the claimant's and spouse's own income. I read these sources this session:

  • ND. N.D.C.C. 57-38-30.3(2) defines North Dakota taxable income as "the federal taxable income of an individual". It is reduced by interest on U.S. obligations ((a)) and by forty percent of "the taxpayer's net long-term capital gain" ((d)(1)) and of qualified dividends ((d)(2)).
    • The task cited 57-38-01.33, which is an expired machinery credit.
    • 2025 ND-1 booklet, line 13: "multiply the qualified dividends from Form 1040 or 1040-SR, line 3a, by 40 percent". The line 6 worksheet takes Schedule D lines 15 and 16 (pp. 14–15).
  • VT. 32 V.S.A. 5811(21): "in the case of an individual, federal adjusted gross income"
    • increased by "interest income from non-Vermont state and local obligations" ((A)(i));
    • decreased, "to the extent such income is included in federal adjusted gross income", by income from U.S. government obligations ((B)(i));
    • and decreased by 40% of gain on "assets held by the taxpayer for more than three years" ((B)(ii); Schedule IN-153).
  • IA. 2022 IA 1040 instructions, Social Security worksheet:
    • line 1 "Box 5 of form(s) SSA-1099. If you filed a joint federal return, enter the totals for both spouses";
    • line 6 "Enter the amount from your federal 1040, line 2a".
  • ME. 36 M.R.S. 5213-A(1)(B) and 5219-KK(1)(D): "federal adjusted gross income increased by ... (2) Interest received to the extent not included in federal adjusted gross income".
  • WV. W. Va. Code 11-21-22a(d): "the federal adjusted gross income plus any applicable increasing West Virginia modifications plus any tax exempt interest income reported on the federal tax return".
  • PA. Personal Income Tax Guide, Tax Forgiveness: "For a single claimant, only the claimant's eligibility income is considered". A married claimant counts "both the claimant and the claimant's spouse". A dependent child claims forgiveness separately.
  • Virginia. Virginia lets a filer subtract "the wages or salaries received by any person" for service in the Virginia National Guard (Va. Code 58.1-322.02(8)). Each person can subtract up to the lesser of 39 days' pay and $3,000 ($5,500 from 2023). The model caps the subtraction for each person rather than each return, so a couple who both serve can subtract up to twice the cap.
  • Other states. A read-only research lane checked the statutes for the remaining states: AZ 43-1022, SC 12-6-1150(A), WI 71.05(6), MD TB-58, WA RCW 82.87.040/.120, MT 15-30-2103 and former 15-30-2301, MA c.62 §2, IL 35 ILCS 5/203(a)(2)(A), NH Rev 906.02, MN 290.0132 subd. 26 (via IRC 86(b)(2)), UT 59-10-1019/1042/1047, and VA 58.1-322.02.
    • Each takes the taxpayer's own amounts ("included in federal adjusted gross income", "of the taxpayer", or spouses only).
    • None lets a parent include a child's income except through the federal Form 8814 election.

Not in this PR

Tests

  • 63 new or renamed YAML cases in 43 changed files (at this head, against the merge base with main).
    • 47 cases put an amount, mostly capital income, on a tax unit dependent and expect the head-and-spouse value. Three of them are main's mt_agi_indiv cases, renamed. An earlier round found that each of its 35 such cases gives a different value on main; the later ones weren't rerun there.
    • 9 twin cases show the same household without the dependent's income gets the same value.
    • 7 cases cover the corrections that need no dependent: the ND floor and distributions (2), VT netting and the Form 4952 share (3), and VA's per-person cap and its allocation (2).
    • Every expected value was checked by hand against the law (for example 40% of the filer's 1,000 = 400; 25%, 44% and 30% of the filer's 10,000 in AZ, SC and WI; 2% of 10,000 = 200 for the MD surtax).
  • Property test policyengine_us/tests/core/test_dependent_capital_income_state_tax_invariants.py (Hypothesis, 5 derandomized examples of 10–30 ND and VT tax units, each run with and without its dependents' capital income).
    • nd_income_tax, vt_income_tax, federal AGI and federal taxable income match between the twins.
    • nd_qdiv_subtraction, nd_ltcg_subtraction (with its zero floor and distributions), vt_percentage_capital_gains_exclusion, vt_additions and us_govt_interest equal an independent reference over the filers' own amounts.
    • On main Hypothesis shrinks to a dependent with $1 of qualified dividends, giving a filer subtraction of $0.40.
    • One documented restriction keeps an out-of-scope path out of the draws: filers earn at least $80,000, so no EITC is at stake.
    • Cost (local, this session's Linux VM with 4 vCPU and no other test running; not a CI measurement): 18.1 s of test time (29.0 s as pytest reports it, collection included) in an 88.5 s process, about 37 s of which is the model load the core group already pays; 1.25 GiB peak RSS.
  • Merged-head runs (2026-11-03): the PR's changed YAML files and the files of every variable a merge conflict touched; the hand-off comment lists the counts. CI runs the full suites.

Microsim impact

Not run here; the hub measures impact.

Invariants

For every tax unit, and any capital income its dependents have, as checked by the property test, the YAML twins and the sweep:

  • These state lines equal their values with the dependents' capital income set to zero. So does the state income tax, except in the states under "Not in this PR". There, a dependent's capital income still moves the filers' tax by the same amount as on main: in AR, MS and NJ, in IA before 2023, and in NM through its capital gains deduction.
  • Federal AGI and taxable income are unchanged by this PR.
  • Tax units without dependents get what they got before, except for five corrections that reach them too. In Iowa's 2021–22 reportable Social Security worksheet, the model now adds line 2, half of the benefits. Virginia now caps its National Guard subtraction for each person rather than each return. The model now floors North Dakota's long-term gain subtraction at zero, and counts capital gain distributions in it. Vermont now limits eligible gains to the filers' net capital gain, less the Form 4952 election's share, before its percentage exclusion. tax_unit_non_dep_add equals add when no member is a dependent.

Axiom note: TheAxiomFoundation/rulespec-us#1424 is queued (extended with the state capital-income cases: TheAxiomFoundation/rulespec-us#1424 (comment)).

  • Where rulespec-us encodes these lines, the inputs are the return's or the individual's own amounts:
    • us-nd:policies/income_tax/resident_core_liability_pipeline (N.D.C.C. 57-38-30.3(2)(d); inputs nd_pit_2026_federal_net_long_term_capital_gain and nd_pit_2026_allocated_qualified_dividends_taxed_below_regular_rates; companion test federal capital loss offset precedes north dakota allocation cap).
    • us-wa:statutes/82/82.87/82/87/040#washington_capital_gains_excise_tax (entity Person).
  • So no module pools a dependent's income. Vermont's 32 V.S.A. 5811(21) is not encoded: us-vt:policies/income_tax/pilot_liability_pipeline takes completed normal tax.
  • The missing piece is 26 U.S.C. 1(g)(7), which California integration test #1424 queues.

Merge with main (2026-11-03)

Main has moved since October. 22 files conflicted:

  • PA pa_eligibility_income and MT mt_agi_joint: main had already left dependents out (Wire military retirement pay and survivors into federal and state tax (#10181) #10182, Fix AR, MT and NM income tax gaps found in TAXSIM comparison sweep #9585), so the merge takes main's version.
  • MA: main routes Schedule D long-term amounts through the new ma_long_term_capital_gains (Count Form 4797 gains and losses on Massachusetts Schedule D #9991). That variable now sums the head and spouse only, which keeps this PR's rule for Part A, Part C and the loss-against-dividends lines.
  • MN, VT, VA, MT credit and Social Security joint: both sides' lines kept. Main's 2020 charitable deduction, other additions and subtractions, pension exemption and capital gain credit floor, alongside this PR's dependent masks.
  • MT mt_agi_indiv: main (Make state income taxes independent of which spouse is labelled head #9981) moves dependents' amounts to the higher-income spouse's column, as a modelling convention. This PR keeps them off both columns, since they are on the dependent's own return. Main's two head-label cases stay as head-independence checks, with the dependent's amounts left off.
  • Tests: both sides' cases kept.
  • CI fix: the October run failed one Vermont case, which supplied VT-eligible gains with no federal long-term gains. The PR's netting cap (IN-153 line 15) then zeroed them. The case now supplies the gains; the expected value is unchanged.
  • After the merge:
    • Three Social Security worksheet comments say "takes" rather than "uses", which the source-conflict guard reads as describing a conflict.
    • mt_dependent_exemptions_person.yaml expects 1,914.57. An adult dependent's net 1,000 gain no longer gives the filers the 2% Montana capital gain credit: 1,894.57 + 20.

impact: the hub will measure
axiom: (hub files pe-parity before landing)


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Overlap with #9898, which fixes the Maryland capital gains surtax base. It changes md_capital_gains_surtax to 2% of IRC 1222(11) net capital gain (via a new md_net_capital_gain), tests federal AGI against the threshold, and leaves dependents out of every input, as this PR does.

@MaxGhenis
MaxGhenis force-pushed the dependent-capital-income-state-lines branch 2 times, most recently from 7e433ee to b195db5 Compare October 12, 2026 11:55
MaxGhenis and others added 9 commits October 12, 2026 08:57
…turn

A tax unit dependent's interest, dividends and capital gains are on the
dependent's own return, and federal adjusted gross income (irs_gross_income)
leaves them out. State lines that adjust the filers' federal AGI or taxable
income summed them over every member, so a child's dividends, gains or
interest moved the parents' state tax.

Count only the head and spouse (tax_unit_non_dep_add, or a dependent mask on
per-person lines) in AZ, DC, DE, IA, IL, MA, MD, ME, MN, MT, ND, NH, OH, PA,
SC, UT, VA, VT, WA, WI and WV, and in us_govt_interest, which most states
subtract.

In formulas edited anyway: North Dakota's net long-term capital gain
exclusion is floored at zero and includes capital gain distributions (Form
ND-1 line 6 worksheet); Utah's retirement credit counts only the head and
spouse as claimants; the West Virginia low-income family credit cites W. Va.
Code 11-21-22a(d).

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

The Montana and Utah Social Security credit cases now carry the exact
half-cent amounts (1,765.725, 1,847.725 and 259.475) instead of values
rounded past the 0.001 margin.

In two existing Utah retirement credit cases person2 (age 64) is a tax unit
dependent, so their tax-exempt interest is on their own return and no longer
enters the modified AGI: 750 instead of 737.5, and 675 instead of 662.5.

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

From the independent review of the first commit:

- Alabama AGI sums every member's interest, so it now also subtracts
  dependents_us_govt_interest (new, next to us_govt_interest, for state
  income definitions that count dependents' income). Without it the
  filer-only us_govt_interest left a dependent's U.S. government interest
  taxed (parent wages 100,000, child 5,000 of Treasury interest: AL AGI
  105,000 instead of 100,000).
- Virginia's National Guard subtraction leaves a dependent's pay out of both
  the cap and the per-person shares, so the filer keeps the full amount.
- Michigan's senior interest, dividends and capital gains deduction counts
  only the head's and spouse's income (MCL 206.30(1)(p): "included in
  adjusted gross income").
- Missouri's capital gains subtraction takes a dependent's gains out of
  net_capital_gains, and its per-person split allocates among the head and
  spouse only.

The Utah retirement credit case with SINGLE status now names its roles
(one head, two dependents), and the property test drops the Vermont
restriction now that #9855 is on main.

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

From the second independent review:

- Iowa's reportable Social Security worksheet adds one-half of the benefits
  (line 2) before the base amount, as both the 2021 and 2022 IA 1040
  worksheets do. The first test's 4,000 came from the formula; the
  worksheet gives 14,000.
- Missouri subtracts the filers' positive Form 1040 line 7a
  (filer_loss_limited_net_capital_gains), so capital gain distributions
  count, a loss nets against them, and a supplied tax unit amount less the
  dependents' gains is kept and goes to the head.
- Vermont's percentage exclusion limits eligible gains to the filers' net
  gain (Schedule IN-153 line 15 starts from Part I line 4). The eligible
  gains input is now documented as part of long_term_capital_gains, and two
  cases that set it alone now set both.
- California's young child tax credit measures the filers' own net loss, so
  a child's capital loss no longer takes the parent's credit.
- Virginia caps National Guard pay per person (Va. Code 58.1-322.02(8),
  "received by any person"), so two qualifying spouses subtract up to
  $5,500 each.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…E and MT lines

From the third independent review:

- Vermont's percentage exclusion nets eligible gains against Schedule
  IN-153 Part I line 4 built from the filers' Schedule D lines 15, 16, 18
  and 19, before the Form 4952 election, then takes off the election's
  share on gains (lines 5-7 and 16: line 4g x 4e / (4b + 4e)). A 100,000
  eligible gain with 100,000 of qualified dividends and a 100,000 election
  now gives 20,000, as the form does.
- Virginia's Guard subtraction defaults to each filer's capped pay summed,
  and the per-person split shares the unit amount by capped pay, so a
  supplied unit amount (such as the 39-day limit) is allocated again.
- New filer_loss_limited_net_capital_gains_person allocates the filers'
  loss-limited gains among the head and spouse only. Georgia's retirement
  exclusion and Delaware's pension exclusion use it, so a dependent's loss
  no longer reduces an eligible filer's gains (IT-511: "Income or losses
  should be allocated to the person who owns the item").
- Montana's separate-return AGI no longer adds a dependent's Montana amounts
  to the head's, as the joint branch already leaves them out; the two cases
  that expected that allocation now expect the head's own amounts.
- The Montana Social Security worksheet cases use wages instead of a
  pension, whose Montana exemption the worksheet does not apply.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis force-pushed the dependent-capital-income-state-lines branch from a0c05af to fa64576 Compare October 12, 2026 12:57
MaxGhenis added a commit that referenced this pull request Oct 13, 2026
…gelog

- ca_agi_subtractions: tax-unit-level items such as us_govt_interest are added
  as they are (#9894 makes that one filer-only).
- WI Schedule SB worksheet is on file page 3 in 2021 and page 2 in 2022; the
  RI reference is the individual 2022 Schedule M, not the RI-1041's.
- The changelog also names California's state tax refund subtraction.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis added a commit that referenced this pull request Oct 18, 2026
…and and Wisconsin filer returns (#10187)

* Leave dependents' unemployment compensation off CA, RI and WI filer returns

Federal AGI leaves out a tax unit dependent's income (irs_gross_income masks
dependents), but California's AGI subtractions, Rhode Island's AGI additions
and Wisconsin's unemployment compensation subtraction summed person-level
unemployment compensation over every tax unit member, so a dependent's
compensation moved the filers' state AGI. Sum only the head and spouse with
tax_unit_non_dep_add.

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

* Test that a dependent's unemployment compensation stays off CA, RI and WI returns

CA Schedule CA column B, RI's 2020 IRC 85(c) add-back and WI's Schedule SB
unemployment worksheet all start from the filers' own federal return. The new
cases fail on main (7 of 12) and pass with the fix.

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

* Cite the forms and statutes for CA, RI and WI on the filers' own return

CA: 2024 Schedule CA (540) column A takes the filers' federal amounts; the
540 booklet gives a dependent their own filing requirement (pages 4, 13).
RI: RIGL 44-30-12(b)(6) and the 2020 Schedule M line 2g and instructions.
WI: Wis. Stat. 71.05(6)(b)8., the 2024 Schedule SB worksheet line 1, and the
2024 Form 1 dependent filing requirement.

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

* Add invariants test for dependents' unemployment compensation in CA, RI and WI

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

* Address review: scope the CA comment, fix reference pages, widen changelog

- ca_agi_subtractions: tax-unit-level items such as us_govt_interest are added
  as they are (#9894 makes that one filer-only).
- WI Schedule SB worksheet is on file page 3 in 2021 and page 2 in 2022; the
  RI reference is the individual 2022 Schedule M, not the RI-1041's.
- The changelog also names California's state tax refund subtraction.

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

* Cover California dependents' state tax refunds in the invariants test

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

* Reflow the invariants test docstring

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
@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

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I continue an earlier session's work: it picked this PR up on 2026-11-02 and stopped before pushing anything. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

Resolve 22 conflicts by keeping both sides' intent:
- MA: main routes Schedule D long-term amounts through
  ma_long_term_capital_gains; make that sum the head and spouse only.
- MN, VT, VA, MT: keep main's new lines (the 2020 charitable deduction,
  other additions and subtractions, the pension exemption, the floor on
  the capital gain credit) and this branch's dependent masks.
- PA and mt_agi_joint: main already leaves dependents out; take main.
- mt_agi_indiv: keep this branch's rule that a dependent's amounts are on
  the dependent's own return, rather than main's convention of moving
  them to the higher-income spouse. Main's two head-label cases stay as
  head-independence checks with the dependent's amounts left off.
- Tests: keep both sides' cases.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6GA1FXMB3qVPzGgvWQ
…ains are part of

CI failed on 'VT percentage exclusion beats flat when eligible gains are
high': this branch caps VT-eligible gains at the filers' Schedule D net
gain (IN-153 line 15), and the case supplied eligible gains with no
federal long-term gains, so the net gain was zero. Eligible gains are a
subset of the filer's long-term gains, so both cases now supply them; the
expected values are unchanged.

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

Copy link
Copy Markdown
Contributor Author

Picking this up for the US hub. I continue the earlier sessions' work on this PR; they pushed nothing. I'll post a hand-off comment here when it's ready.


Generated by Claude Code

…na tax

- test_source_conflicts counts 'the worksheet uses' as a comment about a
  conflict between sources. The IA, MN and MT Social Security comments
  describe no conflict, so they now say 'takes'.
- mt_dependent_exemptions_person: an adult dependent's net 1,000 gain is on
  the dependent's own return, so the filers lose the 2% Montana capital
  gain credit main gave them on it: 1,894.57 + 20 = 1,914.57.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: f692e33256cb8d18bc41bf770996cf260b17ebb2

What the item asked, and how I resolved it

Tests (merged head, one file per process, three at a time)

66 YAML files and 400 cases passed, none failed:

  • every YAML file this PR changes;
  • the test file of each variable a conflict touched;
  • MA, MT and VA integration files.

Highlights:

  • mt_agi_indiv 5, mt_agi_joint 3, mt_capital_gain_credit 4, mt_taxable_social_security_joint 2, MT integration 22;
  • ma_long_term_capital_gains 6, ma_part_a_agi 2, ma_part_c_gross_income 2, integration_ma 9;
  • mn_social_security_subtraction 14;
  • va_subtractions 1, va_subtractions_person 4, VA tax integration 8;
  • vt_additions 1, vt_capital_gains_exclusion 8, vt_percentage_capital_gains_exclusion 10;
  • pa_eligibility_income 4, md_capital_gains_surtax 12, nd_ltcg_subtraction 6, nd_qdiv_subtraction 2, wa_capital_gains_tax 10, us_govt_interest 1.

The property test test_dependent_capital_income_state_tax_invariants.py passed (2). I ran no full state suites; CI is the evidence for those.

Hand computations

  • ND (2025): head wages 50,000, qualified dividends 1,000; child qualified dividends 5,000. nd_qdiv_subtraction = 40% × 1,000 = 400 (main 2,400).
  • MT mt_agi_indiv (2021, labels swapped):
    • spouse 10,000 + 500 − 3,000 = 7,500;
    • head 2,000 + 300 − 700 = 1,600;
    • the dependent's 2,000 + 300 − 700 stays off: [7,500, 1,600, 0] (main 9,100 on the spouse).
  • VT exclusion case: flat min(12,600, 5,000) = 5,000; percentage 40% × 12,600 = 5,040; cap 40% × 12,700 = 5,080. Result 5,040.

Mutants (each in its own process, code restored after)

Mutant Result
mt_agi_indiv back to main's move_dependent_amounts_to_filer caught: 5 of 5 fail, each by the dependent's amount
ma_long_term_capital_gains summed over every member (add) caught: 1 of 2 ma_part_c_gross_income.yaml fails
vt_additions summed over every member caught: 1 of 1 fails

CI

Pull request run at f692e33256: https://github.com/PolicyEngine/policyengine-us/actions/runs/41739892815

At 00:32Z on 2026-11-04, 61 minutes after the push, the run was still in_progress. 35 jobs had passed and none had failed. One job was still running: Contrib tests / Full Suite - Contrib (other-shard-3).

The passed jobs include both shards that failed at ec4ad4c834: Python shard 6 and Baseline states-shard-4. They also include every Baseline and Python shard, Microsimulation, PR validation, Household API Partners, and the other contrib shards. Per the brief I'm handing off without waiting further. The hub should read the run's final conclusion.

Judgment calls

  • mt_agi_indiv: this PR's rule over main's convention (above). Main's own helper says dependents mostly report on their own returns. Moving their amounts to a spouse kept old behaviour while Make state income taxes independent of which spouse is labelled head #9981 made the result independent of which spouse is labelled head. Zeroing them keeps that independence.
  • The Vermont fix changes test inputs, not expected values or code. The PR's IN-153 netting is the rule. The old case's inputs were inconsistent with it.
  • Sources not refetched. This session's network policy blocked the state tax sites, the legislatures and web.archive.org, so I couldn't recheck the PR's quotes (N.D.C.C. 57-38-30.3, 32 V.S.A. 5811(21) and the others). The PR's earlier sessions read them. A reviewer with web access should spot-check the ND and VT quotes, which carry the reported cases.

Proposed follow-ups


Generated by Claude Code

@MaxGhenis

MaxGhenis commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Reviewing head f692e33256cb8d18bc41bf770996cf260b17ebb2 (unchanged since the hand-off).

So far:

  • The defects reproduce at origin/main (25c4253483), and the head fixes them. ND nd_qdiv_subtraction is 2,400 on main and 400 at the head. VT vt_percentage_capital_gains_exclusion is 40,000 on main and 0 at the head, and vt_income_tax is 4,800.00 on main and 6,623.20 at the head. MD md_capital_gains_surtax is 1,200 on main and 200 at the head. ND nd_ltcg_subtraction with a long-term loss of 3,000 is −1,200 on main and 0 at the head.
  • In an all-state sweep (2022 and 2025; head with 60,000 of wages; child with 50,000 of U.S. government interest, qualified dividends or long-term gains), the child's income still moves the filer's state tax at the head only in AR, MS, NJ, NM and IA-2022. Each move equals main's. Every other state gives the no-child value. (An earlier version of this line said the head never raises tax above main's. That was wrong: where main wrongly cut the filer's tax for the child's income, the head restores the higher no-child value.)
  • CI: Pull request run 41739892815 at the head concluded success, 36 of 36 jobs.
  • Draft finding (P2): the body's "Not in this PR" item and the hand-off's second proposed follow-up say eitc_relevant_investment_income counts a dependent's investment income. At this head it already sums only the head and spouse, through tax_unit_non_dep_add. Count only the head's and spouse's income in the EITC investment income test (26 U.S.C. 32(i)) #9883 fixed this, and it merged 2026-10-07.

Final review posted below.


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: REQUEST_CHANGES
f692e33
METHODOLOGY_CHOICE: yes

The head has not moved since the hand-off (f692e33256). The code is sound: the defects reproduce on main and are fixed, every changed YAML file passes, and 8 of 8 mutants are killed. I ask for changes only because the PR body states an invariant that is false. The hub would rely on it to scope the population check, and the body leaves out three baseline changes that reach households with no dependents. All the fixes are to text.

Findings

P1. The body says households without dependents are unchanged, but three changes move them

Where. PR body, "Invariants": "Tax units without dependents get exactly what they got before. tax_unit_non_dep_add equals add when no member is a dependent." The "What changes" table also leaves out these three changes; only the changelog fragment names them.

Failure scenario. The hub reads the invariant and limits its population check to tax units with dependents. These three changes then go unmeasured.

Witness. I ran scripts/noDep.py, a one-adult or couple household with no dependents, once against the head and once against main (PYTHONPATH=<main worktree>):

Case (no dependents) main head
IA 2022: age 70, Social Security 30,000, pension 20,000. ia_reportable_social_security / ia_income_tax 0 / 0.00 5,000 / 190.93
VA 2025: couple, each with wages 40,000 and Guard pay 6,000. va_national_guard_subtraction / va_income_tax 5,500 / 2,207.05 11,000 / 1,890.80
ND 2025: wages 80,000, own long-term loss 3,000. nd_ltcg_subtraction −1,200 0
  • Iowa. ia_reportable_social_security.py:26-31 adds worksheet line 2, half of the benefits, which main left out. The existing test ia_reportable_social_security.yaml "unit test 1" moves from 4,000 to 14,000. I think the fix is right: provisional income with half the benefits is 15,000 + 20,000 = 35,000. That exceeds the 25,000 base by 10,000, and half of 10,000 is 5,000. But it is a baseline change for every Iowa beneficiary in 2021–22, whether or not they have dependents.
  • Virginia. va_national_guard_subtraction.py:15-21 moves the cap from the tax unit to each person, so a two-earner Guard couple doubles its subtraction.
  • North Dakota. nd_ltcg_subtraction.py:39 floors the subtraction at zero. A filer's own net long-term loss no longer adds a negative subtraction. That is right under the line 6 worksheet, but it is not a dependent fix.

Replacement for the body's Invariants bullet:

  • Tax units without dependents get what they got before, except for three corrections that also reach them: Iowa's 2021–22 reportable Social Security worksheet now includes line 2, half of the benefits; Virginia's National Guard subtraction is capped per person rather than per return; and North Dakota's long-term gain subtraction is floored at zero. tax_unit_non_dep_add equals add when no member is a dependent.

Add three rows to "What changes" for these, and rows for the other lines the changelog names but the table omits: CA ca_yctc, GA ga_retirement_exclusion_countable_capital_gains, the DE pension exclusion income_sources.yaml, MI mi_interest_dividends_capital_gains_deduction, and the new filer_loss_limited_net_capital_gains_person.

P2. The body and hand-off say the EITC investment income test still counts dependents, but main fixed it in #9883

Where. PR body, "Not in this PR": "eitc_relevant_investment_income counts a dependent's investment income." The hand-off's second proposed follow-up says the same.

Evidence. At this head, policyengine_us/variables/gov/irs/credits/earned_income/eitc_relevant_investment_income.py:80-106 sums tax_unit_non_dep_add(...) and masks distributions with ~is_tax_unit_dependent. Its documentation says "A tax unit dependent's interest, dividends, rents, passive income and capital gains are on the dependent's own return". #9883, "Count only the head's and spouse's income in the EITC investment income test", merged on 2026-10-07: gh api repos/PolicyEngine/policyengine-us/pulls/9883 --jq .merged_at returns 2026-10-07T20:38:55Z. My sweep agrees: at the head, a child's 50,000 of dividends moves the filer's tax only in the structural AR, MS and NJ cases (and IA in 2022), by the same amount as on main.

Replacement. Delete the "Federal EITC investment-income test" bullet from "Not in this PR", and drop the EITC follow-up. The property test's sentence "whose investment income test (eitc_relevant_investment_income) is not changed here" stays true and can stay.

P2. The Virginia per-person Guard cap is a new legal reading, and the body cites nothing for it

Where. va_national_guard_subtraction.py:15-18 and va_national_guard_subtraction_person.py. The code comment quotes Va. Code 58.1-322.02(8): "the wages or salaries received by any person".

This changes the result for married couples with no dependents (see the P1 table). The PR body neither mentions it nor cites the statute. I couldn't reach law.lis.virginia.gov from this VM, so I couldn't check the quote. Before landing, the body should cite the subdivision with a link, and someone with access should confirm that the cap applies per person.

P3. A test docstring still lists Montana under the dependent-moving convention

Where. policyengine_us/tests/core/test_state_tax_head_spouse_swap_invariance.py:11-15. It says Arkansas, Delaware, Iowa, Mississippi "and Montana" now put dependents' income "on the column of the spouse with the greater income ... (move_dependent_amounts_to_filer)". After this PR, mt_agi_indiv drops dependents' amounts instead. The test still passes, because swap invariance holds either way.

Replacement for lines 12-15:

  • Arkansas, Delaware, Iowa and Mississippi put the dependents' income the model counts on the head's column before taxing each column separately. It now goes on the column of the spouse with the greater income of their own, and an exact tie splits it (move_dependent_amounts_to_filer). Montana leaves the dependents' amounts off both columns, as they are on the dependent's own return.

P3. mt_agi_indiv.py still cites the convention it no longer uses

Where. mt_agi_indiv.py:13-17 keeps main's comment "Borrowed for dependents' income: parents filing separately report a child's income on the return of the parent with the greater taxable income", with the 26 U.S.C. 1(g)(5)(B) and Form 8814 URLs. The formula at lines 49-53 no longer borrows that rule. Delete those three comment lines and the two URLs, or replace them with # Form 8814 election (not modeled): a dependent's income is otherwise on their own return above the i8814 URL.

P3. The body's test description is stale

  • "46 YAML cases in 33 files" doesn't match the head. The diff changes 43 YAML test files and adds 63 - name: entries; some of those renamed main's Montana cases.
  • "VT filers have at least $50,000 of their own long-term gain" doesn't match the test. test_dependent_capital_income_state_tax_invariants.py:91 draws every person's long-term gain from −30,000 to 300,000. Use the filers' Schedule D for the tax on net capital gain #9855 has merged, so the restriction is gone; delete that sub-bullet.
  • The cost figures are labelled local macOS. My run on this Linux VM: 2 passed, 20.6 s of test time, 67 s for the process (while other test processes shared the CPU), peak RSS 1,275 MB.

Follow-ups (identical on main, not this PR's defect)

  • ND dividends elected as investment income. N.D.C.C. 57-38-30.3(2)(d)(2), as the hub quoted it, subtracts qualified dividends "only if taxed at a federal income tax rate that is lower than the regular federal income tax rates". nd_qdiv_subtraction takes 40% of every filer qualified dividend. Witness (scripts/nd4952.py): ND 2025, wages 100,000, qualified dividends 10,000, all 10,000 elected on Form 4952. nd_qdiv_subtraction is 4,000 on main and at the head, and adjusted_net_capital_gain is 0. The statute governs over the form's "line 3a × 40%".
  • VT Form 4952 allocation differs between the two options. The flat $5,000 path reads federal adjusted_net_capital_gain, which takes an election from gain before dividends. The percentage path, at vt_percentage_capital_gains_exclusion.py:47-66, allocates it pro rata (IN-153 lines 5-7). Without an election both cap eligible gains at max(0, min(Sch. D 15, 16) − 28% gain − unrecaptured §1250), so they net the same way.
  • NM nm_net_capital_gains_deduction still pools every member's gains. In the sweep, a child's 50,000 long-term gain moves NM tax by −973 (2022) and −108 (2025) on main and at the head alike. The hand-off's proposal belongs outside this PR: NM is a separate statute with its own MFS pooling, and Split the New Mexico net capital gains deduction between separate filers #9689, Fix AR, MT and NM income tax gaps found in TAXSIM comparison sweep #9585 and Require Arkansas joint filing with a negative spouse income and use tax table bands #9696 have merged.

Checked and sound

Premise. irs_gross_income.py:15-19 multiplies every source by ~is_tax_unit_dependent, so federal AGI leaves out a dependent's income. filer_schedule_d_lines and adjusted_net_capital_gain are filer-only too. The state lines this PR changes now match that base.

Defects reproduce on main and are fixed (scripts/cases.py, 2025, head vs main):

Case main head
ND: head wages 50,000, qualified dividends 1,000; child qualified dividends 5,000. nd_qdiv_subtraction 2,400 400
VT: head wages 150,000, long-term gain 10,000; child 100,000 eligible. vt_percentage_capital_gains_exclusion 40,000 0
Same VT case. vt_capital_gains_exclusion 40,000 5,000
Same VT case. vt_income_tax 4,800.00 6,623.20
MD: head wages 400,000, long-term gain 10,000; child 50,000 long-term gain. md_capital_gains_surtax 1,200 200

All-state sweep (scripts/sweep.py; 2022 and 2025; head with wages 60,000 and a child aged 15). The child had 50,000 of U.S. government interest, qualified dividends or long-term gains. Comparing state_income_tax with and without the child's income:

  • On main the child's income moves the filer's tax in 40 states in 2022 and 38 in 2025.
  • At the head it moves only AR, MS, NJ and NM, plus IA in 2022 (the structural cases and NM's gains). Each move matches main's to the dollar. In every other state the head gives the no-child value, and the no-child values are identical on main and at the head.
  • So no state's tax moves further from the no-child value at the head than on main. Where the head's tax is higher than main's, main had cut the filer's tax for the child's income. Nothing this PR makes reachable gets worse, including the per-person us_govt_interest_person lists in AL, AR, MS and NJ.

North Dakota against the hub's quote of 57-38-30.3(2)(d)(1). "The excess of the taxpayer's net long-term capital gain ... over the net short-term capital loss" is LT when ST ≥ 0 and LT + ST when ST < 0, with no exclusion when that is zero or less. max_(0, min_(LT, LT + ST)) at nd_ltcg_subtraction.py:39 gives exactly that, the whole clause, and matches the worksheet's "smaller of line 1 or line 2". Runs:

Long-term Short-term Distributions Result Hand value
5,000 −2,000 1,200 40% × 3,000
5,000 +2,000 2,000 40% × 5,000
1,000 −3,000 0
−3,000 0
700 280 40% × 700

Main gives −800 and −1,200 for the two loss cases, and 0 for the distributions. The code comment quotes only "the taxpayer's net long-term capital gain", but the formula applies the full clause.

Vermont against the hub's quote of 5811(21)(B)(ii). "Adjusted net capital gain income as defined in 26 U.S.C. § 1(h) reduced by ... qualified dividend income" is the Schedule D net capital gain less 28% gain and unrecaptured §1250 gain. The cap at vt_percentage_capital_gains_exclusion.py:39-43, min(line 15, line 16) − those two, is that amount, and the flat path's adjusted_net_capital_gain − filer qualified dividends nets the same way without an election. Limiting eligible gains to it is a reading of the form (IN-153 line 15), hence METHODOLOGY_CHOICE yes. Other readings this PR takes:

Hand values.

Case Computation Result Test
ND 40% × 1,000 400 nd_qdiv_subtraction.yaml passes
MT mt_agi_indiv, labels swapped spouse 10,000 + 500 − 3,000 = 7,500; head 2,000 + 300 − 700 = 1,600; dependent 0 [7,500, 1,600, 0] passes
VT exclusion flat min(12,600, 5,000) = 5,000; percentage 40% × min(12,600, net gain 12,600) = 5,040; cap 40% × 12,700 = 5,080 5,040 passes

For the VT case, adding long_term_capital_gains: 12_600 makes the inputs consistent: eligible gains are part of long-term gains. The expected values rightly stay. For MT's mt_dependent_exemptions_person.yaml, the 2022 credit rate is 0.02 (parameters/.../credits/capital_gain), and the adult dependent's net gain is 3,000 − 2,000 = 1,000. The removed credit is 20, so 1,894.57 + 20 = 1,914.57. Main's 1,894.57 came from the leak this PR fixes.

The merge (fef18d4b1d).

  • mt_agi_indiv.py:49-53 now agrees with main's own mt_agi_joint.py:17-25, which masks additions and subtractions with is_tax_unit_head_or_spouse.
  • Main's four mt_agi_indiv.yaml cases keep their inputs and check head-label independence (cases 1 and 3 give the same [7,500, 1,600, 0]). Only the dependent's share changes.
  • Main's tie-split behaviour for Montana is gone on purpose. move_dependent_amounts_to_filer keeps its coverage for AR, DE, IA and MS in test_state_tax_head_spouse_swap_invariance.py and ms_agi.yaml.
  • ma_long_term_capital_gains.py:26-33 is tax_unit_non_dep_add over main's three Count Form 4797 gains and losses on Massachusetts Schedule D #9991 inputs (long_term_capital_gains, non_sch_d_capital_gains, other_net_gain). All four readers pick it up.
  • The other resolutions keep both sides' lines, as the hand-off lists (VT, MN, VA, MT credit and Social Security).

Tests.

  • All 43 changed YAML files pass: 244 cases, 0 failed, one policyengine_core test process per file, 4 at a time.
  • The property test passes (2). Run against main's code from a copy, it fails on nd_income_tax with a dependent who has 989 of qualified dividends, so it detects the defect.
  • The new ND, VT and MD cases each give a different value on main (MD shown above).

Mutants, each in its own process on a scratch worktree, restored after each one (git status --porcelain empty):

Mutant Result Killing case(s)
M1 mt_agi_indiv back to move_dependent_amounts_to_filer 5 of 5 fail mt_agi_indiv.yaml
M2 ma_long_term_capital_gains with add 1 of 2 fails ma_part_c_gross_income.yaml "A dependent's long-term gains are on the dependent's own return"
M3 vt_additions with add 1 of 1 fails vt_additions.yaml
M4 (mine) ND floor removed 1 of 6 fails "No exclusion when the filers have a net long-term capital loss"
M5 (mine) ND distributions dropped 1 of 6 fails "Capital gain distributions reported without Schedule D count"
M6 (mine) VT netting cap removed 2 of 10 fail the two netting cases in vt_percentage_capital_gains_exclusion.yaml
M7 (mine) us_govt_interest with add 1 of 1 fails us_govt_interest.yaml
M8 (mine) mt_capital_gain_credit mask removed 1 of 4 fails "A dependent's gains give no credit on this return"

The #9898 overlap. #9898 is still open (head 3427c3acd0). It shares only md_capital_gains_surtax.py and md_capital_gains_surtax.yaml with this PR. I ran this PR's MD case on #9898's head and it passed, giving 200. On main it fails, giving 1,200 (see above). #9898's md_net_capital_gain.py:47-63 masks dependents too. So whichever lands second can take #9898's two files without losing this case's behaviour.

CI. Pull request run 41739892815 at f692e33256 concluded success, with all 36 jobs success. Nothing was cancelled or skipped.

Scope.

Body lines. impact: the hub will measure and axiom: (hub files pe-parity before landing) are both present. The diff moves baseline outputs, for households with dependents and for the three no-dependent cases in P1. So the population check should cover the IA 2021–22 Social Security recipients, VA two-earner Guard couples, and ND filers with a net long-term loss, as well as tax units with dependents' capital income.

What I couldn't check

  • Primary sources. This VM's proxy refused ndlegis.gov, legislature.vermont.gov, tax.nd.gov, tax.vermont.gov, govinfo.gov, uscode.house.gov, ecfr.gov and irs.gov. Fetches returned 403 or failed to resolve. So I checked ND and VT only against the text the review brief quotes. I couldn't check the 2025 ND-1 booklet, Schedule IN-153 (the line 15 cap and the line 5-7 pro rata Form 4952 allocation), Iowa's 2022 worksheet, Va. Code 58.1-322.02(8), or the other statutes the body lists (AZ, SC, WI, MD, WA, MT, MA, IL, NH, MN, UT, ME, WV, PA).
  • Microsimulation impact on population data; the hub measures it.
  • The hand-off's local timings and logs, which I didn't rerun beyond the runs above.

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

The swap-invariance docstring still listed Montana with the states that move
the dependents' income to the spouse with more income of their own; mt_agi_indiv
now leaves those amounts off both columns. Its reference comment still cited
the 26 U.S.C. 1(g)(5)(B) rule it no longer borrows, so it now names only the
Form 8814 election, which the model does not implement.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 10cdebc3eb2f65f146616c7419ce15f24d4b8d68 (one commit on top of f692e33256; nobody else pushed to the branch while I worked, and main merges cleanly, so I made no merge commit). Code behaviour is unchanged: the commit touches only a test docstring and a reference comment. Everything else is in the body, which I PATCHed and read back; the readback matches what I sent.

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

# Item Resolution
5 P1: the invariant Changed from the hub's text; see the first judgment call. The bullet now reads "…except for five corrections that reach them too…". I kept the hub's sentences for Iowa, Virginia and North Dakota's floor, and added that ND now counts capital gain distributions and that Vermont now limits eligible gains to the filers' net capital gain less the Form 4952 share. A no-dependent sweep (below) shows both of those move state_income_tax for units with no dependents. "What changes" gains nine rows, for CA ca_yctc, GA, DE income_sources.yaml, MI, the new filer_loss_limited_net_capital_gains_person, and IA, VA, ND and VT marked No dependent needed, with witnesses. Its intro sentence now says the last four rows also reach units without dependents.
6 P2: EITC Deleted the "Federal EITC investment-income test" bullet. #9883 merged at 2026-10-07T20:38:55Z, and at this head eitc_relevant_investment_income.py sums tax_unit_non_dep_add(...) and masks distributions with ~is_tax_unit_dependent.
7 P2: Virginia's source Added the hub's bullet to "Legal basis", before "Other states". I fetched Va. Code 58.1-322.02 from law.lis.virginia.gov this session. Subdivision 8 opens "The wages or salaries received by any person for active and inactive service in the National Guard of the Commonwealth of Virginia", with the 39-day / $3,000 limit before 2023 and $5,500 from 2023, as the bullet says. The cap.yaml values are 3,000 (2021) and 5,500 (2023).
8 P3: swap-invariance docstring test_state_tax_head_spouse_swap_invariance.py:12-17: the hub's text, wrapped at the docstring's 78 columns. move_dependent_amounts_to_filer (model_api.py:58) is called by AR, DE, IA and MS only (ar_agi_indiv, de_agi_indiv, ia_net_income, ms_agi), and mt_agi_indiv returns ~is_dependent * …. The docstring's item 3 already excludes Montana.
9 P3: mt_agi_indiv.py Lines 13-16 (the three comment lines and the 26 U.S.C. 1(g)(5)(B) URL) are replaced by # Form 8814 election (not modeled): otherwise a dependent reports their income on their own return. at line 13, above the i8814 URL.
10 P3: Tests text Changes below the table.
11 Runs All pass; see below.

Item 10 in detail:

  • Counts, at this head against the merge base abc67b98de: 43 changed YAML test files, 63 added - name: lines and 3 removed. The removed ones are the three mt_agi_indiv cases that were renamed. The 63 split into:
    • 47 that put an amount on a dependent (3 of them the renamed Montana cases);
    • 9 twins;
    • 7 for the no-dependent corrections: ND 2, VT 3, VA 2.
  • Earlier claim. The body's old line said each of 35 cases "gives a different value on main". I kept that as the earlier round's finding and said the later cases weren't rerun there.
  • VT sub-bullet deleted. That made "Two documented restrictions" false, so it now reads "One documented restriction".
  • Cost line is my run, below.

The no-dependent sweep behind item 5

I ran 9 profiles, every one with no dependents, in each of 50 states plus DC, for 2021, 2022 and 2025. The profiles cover:

  • a net gain after a short-term loss with VT-eligible gains;
  • capital gain distributions;
  • an own long-term loss;
  • a two-earner Guard couple;
  • a retiree with Social Security and a pension;
  • retiree couples with losses against gains;
  • a §1250 gain plus a Form 4952 election;
  • a limited net loss with distributions.

"Main" is the merge base abc67b98de. I compared state_income_tax and every variable the PR changes, each tree in its own process.

state_income_tax changed in 14 of 1,377 tax units, all in four states:

State Profile 2021 2022 2025
IA age 70, Social Security 30,000, pension 20,000 0 → 199.15 0 → 190.93 unchanged (ia_reportable_social_security 0 → 5,000 but no tax)
ND wages 60,000, distributions 5,000 339.04 → 298.24 319.14 → 278.34 15.11 → 0
VT wages 60,000, LT gain 10,000 (all eligible), ST loss 8,000 1,790.93 → 1,922.93 1,732.12 → 1,864.12 1,509.18 → 1,576.18
VT wages 100,000, LT gain 20,000 (all eligible), unrecaptured §1250 6,000, Form 4952 election 3,000, qualified dividends 3,000 5,603.92 → 5,831.92 5,512.12 → 5,740.12 5,129.80 → 5,327.80
VA couple, wages 40,000 and Guard pay 6,000 each 2,788.05 → 2,615.55 2,485.55 → 2,313.05 2,655.55 → 2,339.30
  • ND floor. It moved nd_ltcg_subtraction for the four loss profiles (for example −1,200 → 0) but no tax. nd_subtractions floors its total at zero, and those profiles had no other positive ND subtraction with tax at stake.
  • Other states. No other state's tax moved, including GA, DE, MI and CA for these profiles.
  • Not covered. I didn't sweep collectibles (28% rate) gains, or CA filers with an EITC-qualifying child who isn't a dependent.

Witnesses in the new rows

I reran the review's three witnesses at this head and at the merge base:

Witness Variable Main → head
IA 2022 ia_reportable_social_security 0 → 5,000
ia_income_tax 0.00 → 190.93
ND 2025 nd_ltcg_subtraction −1,200 → 0
nd_income_tax 249.11 both ways
VA 2025, couple aged 40 va_national_guard_subtraction 5,500 → 11,000
va_income_tax 2,655.55 → 2,339.30

IA and ND match the review exactly. For VA, the subtraction and the 316.25 change match the review, but my tax levels are 448.50 higher than its 2,207.05 → 1,890.80, a 7,800 difference in Virginia taxable income. The review didn't list all its inputs, so I couldn't find the difference. The row uses my values and states my inputs.

Tests at the head (one process per file, at most three at a time)

File Result
test_state_tax_head_spouse_swap_invariance.py 8 passed (130.2 s pytest, 264 s wall, 2.85 GiB peak RSS)
test_dependent_capital_income_state_tax_invariants.py 2 passed
test_source_conflicts.py (new comment text) 103 passed
ia_reportable_social_security.yaml 4 passed
va_national_guard_subtraction.yaml 7 passed
nd_ltcg_subtraction.yaml 6 passed
mt_agi_indiv.yaml 5 passed
mt_taxable_social_security_joint.yaml 2 passed
mt_capital_gains_tax_applicable_threshold_joint.yaml 5 passed
mt_capital_gains_tax_joint.yaml 12 passed
mt_capital_gain_credit.yaml 4 passed
mt_interest_exemption_person.yaml 4 passed
mt_regular_income_tax_joint.yaml 3 passed
mt_dependent_exemptions_person.yaml (changed test) 28 passed
mt_agi_joint.yaml (changed test) 3 passed

No va_national_guard_subtraction_person.yaml exists; the person variable is asserted in va_national_guard_subtraction.yaml.

Property file cost (the body's new line). I ran it alone on this session's Linux VM, with 4 vCPU and no other test running:

  • 2 passed;
  • 18.1 s in the two test calls, and 29.0 s as pytest reports it;
  • an 88.5 s process, of which import policyengine_us alone took 37.4 s in a separate process;
  • 1,276 MiB peak RSS.

ruff format --check and ruff check pass on both changed Python files.

Mutants: none. This round changes no code, only a docstring, a comment and the body.

CI

Not finished at the 60-minute mark; the runners were congested. This is the only run at this head: the Pull request run 41861379473 at 10cdebc3eb.

At 16:48Z, 60 minutes after my 15:47Z push, the run was in_progress:

  • 34 of 36 jobs had succeeded: all eleven Baseline shards (the four state shards included), Household API Partners, PR validation, Package and compatibility, Quick Feedback, Microsimulation, the Rest Python planning and check jobs, all nine Rest Python shards, and seven Contrib shards.
  • 2 were still running: Contrib other-shard-1 and other-shard-3.
  • None had failed or been cancelled.

The hub needs to read the run's conclusion.

Judgment calls

  1. The invariant says five corrections, not three. The hub's text was "except for three corrections", but the sweep shows ND distributions and VT netting (with the Form 4952 share) also change state_income_tax for units with no dependents. The changelog's own "Also …" sentence names the VT netting too. Pasting "three" would have left the P1 defect in place, so I kept the hub's three sentences and added the two clauses and the count. If you'd rather keep "three", the VT and ND rows still show the extra cases.
  2. VT and ND rows. These weren't in the hub's list, but they are the lines behind judgment call 1, and the changelog names the VT netting.
  3. VA witness. It uses my values (above).
  4. Things I left alone.
    • The changelog fragment doesn't mention that ND now counts distributions; I didn't edit it.
    • The Invariants' first bullet says "the state income tax" equals its value without the dependents' capital income. That isn't true in AL, AR, MS, NJ and IA before 2023, which "Not in this PR" lists as structural. I left it, since my change didn't touch it.

Proposed follow-up

Virginia National Guard subtraction: model the rank limit and the 39-day pay limit. Va. Code 58.1-322.02(8) limits the subtraction to "the amount of income derived from 39 calendar days of such service" (or the dollar cap), and to ranks O3 and below before 2023 and O6 and below from 2023. The model caps only at $3,000 / $5,500 per person, and has no rank or service-day input.


Generated by Claude Code

@MaxGhenis

MaxGhenis commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Round 2 review of head 10cdebc3eb2f65f146616c7419ce15f24d4b8d68 (the head has not moved). Findings so far:

  • CI. Pull request run 41861379473 at 10cdebc3eb concluded success, with all 36 jobs success.
  • Delta since f692e33256. One commit: the swap-invariance docstring and the mt_agi_indiv.py reference comment. No formula changed.
  • "Five" corrections. At the head and the merge base abc67b98de, the ND distributions rows (2021 339.04 → 298.24, 2022 319.14 → 278.34, 2025 15.11 → 0) and the VT netting rows (6 values) reproduce to the cent. Five is right.
  • VA witness. Round 1's 2,207.05 → 1,890.80 reproduces with one spouse aged 65, which gives Virginia's age deduction. The session's 2,655.55 → 2,339.30 reproduces with both spouses aged 40. military_service_income never enters federal AGI, so the row's wording needs fixing (draft finding).
  • Invariants' first bullet. At the head, a dependent's 50,000 of capital income still moves 2025 state tax in AR, MS and NJ, NM (gains) and IA 2022, the same as on main. The bullet needs that exception (draft finding).
  • Sources. The proxy refuses law.lis.virginia.gov and every other primary source I tried, so I couldn't check Va. Code 58.1-322.02(8).

Test runs and mutants are still running.


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
10cdebc
METHODOLOGY_CHOICE: yes

Round 2, a delta review against the round 1 review at f692e33256. The head has not moved since the hand-off. The only commit since round 1, 10cdebc3eb, changes the swap-invariance test docstring and one reference comment in mt_agi_indiv.py. No formula changed. CI is green, every test file I ran passes, and 5 of 5 new mutants are killed.

"Five" is right. Every sweep row and witness in the new "What changes" rows reproduces to the cent at the head and at the merge base. Three text fixes remain, none of them in code. I approve on the understanding that the hub applies them before landing:

  • the invariants' first bullet is false as written (P2);
  • the Virginia row's inputs read as 46,000 of income each when the run had 40,000 (P3);
  • the changelog fragment leaves out North Dakota's distributions (P3).

Findings

P2. The invariants' first bullet says state income tax ignores dependents' capital income, but five states still move

Where. PR body, "Invariants", first bullet: "These state lines and the state income tax equal their values with the dependents' capital income set to zero."

Failure scenario. The hub checks the population run against this bullet. Zeroing dependents' capital income then still moves state tax in AR, MS, NJ, NM and pre-2023 IA, and nothing in the invariant says to expect that. The body lists these states under "Not in this PR", but the invariant reads "For every tax unit".

Witness (scripts/dep.py): a head with wages 60,000 and a dependent aged 15 who has 50,000 of one input. The table compares state_income_tax without and with the child's income. Main (merge base abc67b98de) and the head give identical rows.

State, year Child's input Without With Change
AR 2025 qualified dividends 1,737.00 4,015.67 +2,278.67
AR 2025 long-term gains 1,737.00 2,713.00 +976.00
MS 2025 any of the three 1,632.40 3,832.40 +2,200.00
NJ 2025 any of the three 988.75 3,164.38 +2,175.63
NM 2025 long-term gains 941.12 833.62 −107.50
IA 2022 any of the three 2,514.30 6,487.05 +3,972.75
AL, IA, ND, VT, MD 2025 any of the three 0

The three inputs are qualified dividends, long-term gains and taxable interest. So the bullet needs the exception. AL doesn't move for capital income in this case, although "Not in this PR" lists it as structural.

Replacement:

  • These state lines equal their values with the dependents' capital income set to zero. So does the state income tax, except in the cases "Not in this PR" lists. At this head, a dependent's capital income still moves the filers' tax in AR, MS and NJ, in IA before 2023 (all structural), and in NM through its capital gains deduction, by the same amount as on main.

P3. The Virginia witness doesn't say that its wages include the Guard pay

Where. PR body, "What changes", VA row: "a couple aged 40, each with wages 40,000 and Guard pay 6,000. va_national_guard_subtraction 5,500 → 11,000; va_income_tax 2,655.55 → 2,339.30".

What I found. military_service_income is a memo input. Nothing adds it to federal AGI: its only readers are state subtractions and benefit income lists. So the row's household has federal AGI 80,000, not 92,000, and wages 40,000 that include the 6,000 give the same values. A reader who enters 46,000 of wages each gets different numbers.

Where round 1's 448.50 came from. Round 1's 2,207.05 → 1,890.80 reproduces exactly when one spouse is 65 and the other 40. Virginia's age deduction then takes 7,800 off Virginia taxable income, and 7,800 × 5.75% = 448.50. The session's 2,655.55 → 2,339.30 reproduces with both spouses aged 40 (scripts/va.py, scripts/va2.py; base, then head):

Ages Wages each va_taxable_income va_income_tax
40 / 40 40,000 55,140 → 49,640 2,655.55 → 2,339.30
65 / 40 40,000 47,340 → 41,840 2,207.05 → 1,890.80
40 / 40 34,000 43,140 → 37,640 1,965.55 → 1,649.30

Every row has military_service_income 6,000 each, and the subtraction goes from 5,500 to 11,000. Keep the session's values, which state their ages, and fix only the wording. The change is 316.25 = 5,500 × 5.75% either way.

Replacement for the witness sentence:

Witness, 2025, no dependents: a couple aged 40, each with wages of 40,000 that include 6,000 of Guard pay (military_service_income, which the model does not add to federal AGI).

P3. The changelog fragment leaves out that North Dakota now counts capital gain distributions

Where. changelog.d/dependent-capital-income-state-lines.fixed.md:1 says "floor North Dakota's long-term gain exclusion at zero", but not the distributions. They move tax for filers without dependents. In 2022, wages 60,000 and distributions 5,000 take state_income_tax from 319.14 to 278.34, and in 2025 from 15.11 to 0. The fragment's Vermont clause also leaves out the Form 4952 share, which moves tax on its own.

Replacement for the fragment's second sentence:

Also add the half of benefits to Iowa's reportable Social Security worksheet, net Vermont's eligible gains (less the Form 4952 election's share) before its percentage exclusion, floor North Dakota's long-term gain exclusion at zero and count capital gain distributions in it, and cap Virginia's National Guard subtraction per person.

Checked and sound

The invariant: "five" is right, and the count is complete.

  • Code. nd_ltcg_subtraction.py:33-39 adds non_sch_d_capital_gains to the long-term amount and floors at zero. vt_percentage_capital_gains_exclusion.py:38-66 caps eligible gains at min(line 15, line 16) less the 28% rate and unrecaptured §1250 gains, then takes off the Form 4952 share. On main the formula had neither.
  • ND distributions rows (head vs merge base, scripts/wit.py): nd_ltcg_subtraction 0 → 2,000, and state_income_tax 339.04 → 298.24 (2021), 319.14 → 278.34 (2022) and 15.11 → 0 (2025). All match the hand-off.
  • VT netting rows (scripts/vt.py). All six match the hand-off: 1,790.93 → 1,922.93, 1,732.12 → 1,864.12, 1,509.18 → 1,576.18, 5,603.92 → 5,831.92, 5,512.12 → 5,740.12 and 5,129.80 → 5,327.80.
  • VT hand values.
    • First profile: min(10,000, 10,000 − 8,000) = 2,000, and 40% is 800. The flat 5,000 option gives min(2,000, 5,000) = 2,000, which wins. The tax change is 2,000 × 6.6% = 132.
    • Second profile: min(20,000, 20,000) − 6,000 = 14,000. The election's share is 3,000 × 20,000 / 23,000 = 2,608.70. 40% × (14,000 − 2,608.70) = 4,556.52, which matches. The exclusion falls from 8,000 to 5,000 (the flat option), and 3,000 × 7.6% = 228.
  • No sixth change. I read every non-mask line in the 52 changed variable and parameter files. Two reach units without dependents but leave them unchanged:
    • ca_yctc now uses filer_schedule_d_lines(...).line_16. Without dependents that equals the old net_capital_gains plus distributions, on both of its branches.
    • filer_loss_limited_net_capital_gains_person (GA and DE) reduces to loss_limited_net_capital_gains_person.
    • az_long_term_capital_gains_subtraction swaps net_capital_gains for long-term plus short-term gains, which is what net_capital_gains adds. It differs only if a dataset supplies net_capital_gains directly.
    • UT, WV, ME, IL, NH and MI only mask dependents.

Witnesses (scripts/wit.py; merge base → head):

  • IA 2022: ia_reportable_social_security 0 → 5,000 and ia_income_tax 0 → 190.93. IA 2021: 0 → 5,000 and 0 → 199.15. By hand: 0.5 × 30,000 + 20,000 = 35,000; less the 25,000 base is 10,000; half of that is 5,000.
  • ND 2025 loss: nd_ltcg_subtraction −1,200 → 0, nd_subtractions 0 both ways, nd_income_tax 249.11 both ways.
  • VA: see the P3 above.

IA worksheet reader. ia_reportable_social_security.py:29 subtracts add(tax_unit, ..., ["taxable_social_security"]) over every member. taxable_social_security gives dependents 0, so that sum is the filers' amount. Its one reader is ia/tax/income/modified_income/sources.yaml.

Readers of the five no-dependent lines.

  • ia_reportable_social_security feeds Iowa modified income.
  • va_national_guard_subtraction feeds va_national_guard_subtraction_person, which feeds va/.../subtractions.yaml.
  • nd_ltcg_subtraction feeds nd/.../subtractions/sources.yaml.
  • vt_percentage_capital_gains_exclusion feeds vt_capital_gains_exclusion.

I found no reader that this PR makes worse.

The defect reproduces at the merge base and is fixed at the head. I reran the body's two TY2025 cases (scripts/defect.py):

Case Variable Base without the child's income Base with it Head with it
ND: head wages 50,000 and qualified dividends 1,000; child qualified dividends 5,000 nd_qdiv_subtraction 400 2,400 400
VT: head wages 150,000 and long-term gain 10,000; child long-term gain 100,000, all VT-eligible vt_percentage_capital_gains_exclusion 0 40,000 0
Same VT case vt_capital_gains_exclusion 5,000 40,000 5,000
Same VT case vt_income_tax 6,623.20 4,800.00 6,623.20

Federal AGI is 160,000 in every VT column. These match the body's table. In the P2 witness the head earns 60,000 with no capital income of their own, so the ND, VT and MD subtractions were 0 on both trees.

Tests at the head, one process per file:

File Result
ia_reportable_social_security.yaml 4 passed
va_national_guard_subtraction.yaml 7 passed
nd_ltcg_subtraction.yaml 6 passed
vt_percentage_capital_gains_exclusion.yaml 10 passed
mt_capital_gains_tax_joint.yaml 12 passed
mt_capital_gain_credit.yaml 4 passed
mt_dependent_exemptions_person.yaml 28 passed
mt_interest_exemption_person.yaml 4 passed
mt_agi_indiv.yaml 5 passed
mt_agi_joint.yaml 3 passed
mt_taxable_social_security_joint.yaml 2 passed
test_dependent_capital_income_state_tax_invariants.py 2 passed (25.7 s pytest, 82 s wall, 1,275 MiB peak RSS)
test_state_tax_head_spouse_swap_invariance.py 8 passed (104.7 s pytest, 197 s wall, 2,923 MiB peak RSS)
test_source_conflicts.py 103 passed

These are local runs on a 4 vCPU Linux VM, not CI. The property file's 1,275 MiB matches the body's 1.25 GiB.

Mutants. The hand-off listed none, since the round changed no code. I added five aimed at the round 2 rows. Each ran in its own process on the checked-out head, and each file was restored with git checkout --. git status --porcelain was empty after each run.

Mutant Result Killing case (mutant value vs expected)
N1 ND: drop non_sch_d_capital_gains from the long-term amount 1 of 6 fail nd_ltcg_subtraction.yaml "Capital gain distributions reported without Schedule D count" (0 vs 400)
N2 VA: cap per return, min_(tax_unit.sum(pay), p.cap) 1 of 7 fail va_national_guard_subtraction.yaml "Each spouse subtracts their own Guard pay up to the cap" (5,500 vs 11,000)
N3 IA: drop worksheet line 2, fraction * benefits 3 of 4 fail ia_reportable_social_security.yaml "IA reportable social security unit test 1" (4,000 vs 14,000), plus the dependent case and its twin (0 vs 5,000)
N4 VT: drop the Form 4952 share 1 of 10 fail vt_percentage_capital_gains_exclusion.yaml "The Form 4952 election share on gains comes off the eligible gains" (40,000 vs 20,000)
N5 VA: count a dependent's Guard pay 1 of 7 fail va_national_guard_subtraction.yaml "A dependent's Guard pay neither uses up the cap nor shares in it" (11,000 vs 5,500)

The ND floor and VT netting mutants were round 1's M4 and M6, both killed then. This round's commit changes no formula, so I didn't rerun them.

Body claims checked.

  • Counts. git diff abc67b98de 10cdebc3eb shows 43 changed YAML test files, 63 added - name: lines and 3 removed.
  • "What changes". Its intro says "the last four rows" reach units without dependents, and they are IA, VA, ND and VT.
  • EITC. The bullet is gone from "Not in this PR".
  • Swap-invariance docstring. move_dependent_amounts_to_filer is called only from ar_agi_indiv, de_agi_indiv, ia_net_income and ms_agi, and mt_agi_indiv.py:49-50 returns ~is_dependent * tax_unit_mt_agi. So the docstring is true.
  • mt_agi_indiv.py:13. It carries the new Form 8814 comment.
  • Lint. ruff format --check and ruff check (the version uv.lock pins) pass on both changed Python files.
  • Body lines. impact: the hub will measure and axiom: (hub files pe-parity before landing) are present.

Baseline movement. The diff moves baseline outputs. It moves tax units with dependents who have capital income, Social Security or Guard pay. It also moves units with no dependents in five ways:

  • IA 2021–22 Social Security recipients;
  • VA couples where both spouses have Guard pay;
  • ND filers with capital gain distributions, or a net long-term loss with other ND subtractions;
  • VT filers with VT-eligible gains above their net capital gain after netting;
  • VT filers with a Form 4952 election.

The population check should cover each. If the microdata carry no military_service_income, the VA change has no population effect.

CI. Pull request run 41861379473 at 10cdebc3eb concluded success, with all 36 jobs success. None was cancelled or skipped.

Scope.

METHODOLOGY_CHOICE yes, for the same readings round 1 named:

  • Vermont's IN-153 line 15 netting and pro rata Form 4952 share;
  • Virginia's per-person cap;
  • mt_agi_indiv dropping dependents' amounts.

What I couldn't check

  • Va. Code 58.1-322.02(8). This VM's proxy refused law.lis.virginia.gov through both curl (403 on CONNECT) and the fetch tool (name not resolved). It also refused lis.virginia.gov, tax.virginia.gov, revenue.iowa.gov, tax.nd.gov, ndlegis.gov, legislature.vermont.gov, tax.vermont.gov, govinfo.gov, uscode.house.gov, ecfr.gov, irs.gov and web.archive.org. So I couldn't check the body's Virginia bullet, the "any person" quote, the $3,000 and $5,500 caps or the 39-day limit. The code matches the bullet's description of the model: va_national_guard_subtraction.py:18-21 caps each non-dependent's pay at p.cap and sums. Whether the statute caps per person rests on the hand-off session's reading and the hub's. Two quotes, the IA 2022 Social Security worksheet and the ND-1 line 6 worksheet, also rest on the body's quotes.
  • The hand-off's full no-dependent sweep (9 profiles × 51 jurisdictions × 3 years). I reran only its ND and VT rows and the IA and VA witnesses. I didn't rerun its claim that no other state's tax moved, but I found no other non-mask change in the code (above).
  • Microsimulation impact on population data; the hub measures it.

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

The fragment summed up Vermont and North Dakota in a phrase each, leaving
out Vermont's Form 4952 netting and North Dakota's capital gain
distributions, so it now names each in its own sentence.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: be20e6f81009a8ba456f8fb76b7df1c7da79684f (one commit on 10cdebc3eb, which the brief named; nobody else pushed in between).

What the brief asked and how it's resolved

  1. The fragment. In changelog.d/dependent-capital-income-state-lines.fixed.md, the second sentence, from "Also add the half of benefits" to "per person.", is now the brief's three sentences. The first sentence is unchanged, and the fragment is still one top-level *.fixed.md file. uv run towncrier build --draft renders it as one bullet that ends "…and count capital gain distributions in it."
  2. Runs. No code changed, so the fragment check was the only run.

The body's invariants bullet and Virginia witness, which the hub rewrote, are untouched. The body names neither the fragment's wording nor a CI run at a specific head, so this commit makes none of its sentences false, and I didn't PATCH it.

CI

Pull request run 41949737927 at be20e6f810 concluded success: 36 jobs, all success, none cancelled or skipped. The body names no run or head in a CI sentence, so I had nothing to replace and added none.

Judgment calls

  • The commit message gives the reason: the fragment now names Vermont's Form 4952 netting and North Dakota's capital gain distributions. I didn't check those two code paths in this session. The wording is the brief's.

Proposed follow-up

None.


Generated by Claude Code

PavelMakarchuk added a commit that referenced this pull request Nov 6, 2026
Mississippi AGI read loss_limited_net_capital_gains_person, which nets and
limits capital gains and losses over every tax unit member. With the
dependent's column zeroed, a dependent's gains changed the parent's
Mississippi AGI and could lift the parent's loss above the 26 USC 1211(b)
limit. Mississippi now reads filer_loss_limited_net_capital_gains_person
(copied byte for byte from #9894), which limits only the head's and
spouse's own net loss and gives a dependent nothing.

- Add ms_agi cases for a child's gain smaller and larger than the
  parent's loss, a child's loss against the parent's gain, and spouses
  sharing a limited loss.
- Add a Worksheet D case with an age-eligible dependent who has income.
- Merge same-document references (AR, IA, NJ, MS) into one href each.
- Point the Worksheet D reference at PDF page 23 of the 2025 booklet.

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

DTrim99 commented Nov 7, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Leave dependents' capital income out of state lines on the filers' return

Summary

The core change is sound. A tax unit dependent's interest, dividends and capital gains are already left out of federal AGI (irs_gross_income, capital_losses_allowed_against_gains, filer_schedule_d_lines). This PR stops about 20 state lines from pooling them back in. The filer-only pieces are the head-and-spouse sums from tax_unit_non_dep_add, the per-person masks in DC, DE and OH, and the new filer_loss_limited_net_capital_gains_person for GA and DE.

I checked the new person variable that the #10667 review recommended:

  • It applies the 26 U.S.C. 1211(b) limit to the filers' combined net loss only: $3,000, or $1,500 for a separate return, through filer_loss_limited_net_capital_gains.
  • The per-person shares sum to that limited total, and a dependent gets 0.
  • It does not repeat Leave tax unit dependents' income out of Arkansas, Mississippi, New Jersey and Iowa income #10667's bug. A head-only probe at this head shows the difference. With a parent's −5,000 loss and a child's +20,000 gain, the pooled variable gives the parent −5,000, past the $3,000 limit. The new variable gives −3,000.

Every state line that treats long-term gain separately nets the filers' own short- and long-term amounts in the order its form uses: AZ, MT, ND, SC, VT and WI. No changed state brings a dependent's income onto the parents' return except through the federal Form 8814 election, which the model doesn't implement. The four corrections that don't need a dependent are each right against the source:

  • Iowa's worksheet line 2;
  • Virginia's National Guard cap, now per person;
  • North Dakota's floor at zero and capital gain distributions;
  • Vermont's IN-153 netting and Form 4952 share.

CI is 36/36 green. I found no critical issue. Four should-fix items remain:

  1. Several changed masks are not pinned by any test.
  2. A dependent's Social Security still reaches Maine's fairness-credit income, in a formula this PR rewrote.
  3. Two reference entries break the CLAUDE.md reference rules.
  4. The body's axiom: line isn't in the required form, and California integration test #1424 doesn't cover the four no-dependent corrections.

Critical (must fix)

None.

Should address

S1. Several new dependent masks are unpinned (mutation rule). Reverting each of these lines to add(...) fails no test. The PR's cases give the child only gains, or only the one item that another line already covers. Nothing in the PR's YAML or in the existing files for these variables puts a dependent's short-term loss, dividends against a filer loss, collectibles gain, bank interest, student loan interest or ALDs into these states. The property test covers ND and VT only, and it never draws investment_income_elected_form_4952.

File Unpinned line(s) A case that would kill the revert
sc_net_capital_gain_deduction.py stcg Head LT +10,000, child ST −8,000: 44% × 10,000 = 4,400. Pooled gives 44% × 2,000 = 880.
wi_capital_gain_loss_subtraction.py stcg_net Same household: 30% × 10,000 = 3,000. Pooled gives 600.
mt_capital_gains_tax_joint.py, mt_capital_gains_tax_applicable_threshold_joint.py, mt_regular_income_tax_joint.py (2024+) stcg Child ST loss against the head's LT gain (TY2025)
ma_part_c_gross_income.py, ma_part_c_agi.py, ma_part_a_agi.py (ST and collectibles lines), ma_short_term_loss_against_dividends.py and ma_long_term_loss_against_dividends.py (both lines each) as listed Child ST loss with head LT gain; child dividends with a head ST or LT loss; child collectibles gain
ma_part_b_taxable_income_deductions.py bank interest TY2021–2023 only, while exemptions.interest.in_effect is true: child taxable interest with head interest under the cap
mn_social_security_subtraction.py student_loan_interest Child with student loan interest
mt_taxable_social_security_joint.py mt_applicable_ald_deductions, student_loan_interest_ald, mt_subtractions Child with an ALD or a Montana subtraction
wa_millionaires_tax_base_income.py both lines TY2028, when millionaires_tax.in_effect turns on: child LT gain or tax-exempt interest. The file has no dependent case at all.
vt_percentage_capital_gains_exclusion.py the filer masks on the Form 4952 election and on qualified dividends in the line 7 share Head election with child qualified dividends
ca_yctc.py the limited_capital_gains line alone Parent ST −40,000 and child LT +50,000. Pooled limited = +10,000, while the filers' limited amount is −3,000 and line 16 is −40,000. That fails line 23b, so the credit is 0.

Fix: add one case per row, in the variable-named YAML files. The first two are one-household additions to the existing SC and WI files, and the probe values above are hand-checked.

S2. A dependent's Social Security still reaches Maine's fairness-credit income. me_sales_and_property_tax_fairness_credit_income.py now reads tax_unit_non_dep_add(tax_unit, period, p.income_sources). Its comment says the income is the head's and spouse's. But the list item tax_exempt_social_security is a TaxUnit variable, tax_unit_social_security − tax_unit_taxable_social_security, and tax_unit_social_security adds social_security over every member. So it's added as is, and a child's survivor benefits still count.

  • 36 M.R.S. 5213-A(1)(B) adds Social Security in the same way it adds interest. It adds "(2) Interest received to the extent not included in federal adjusted gross income" and "(3) Payments received under the federal Social Security Act and railroad retirement benefits to the extent not included in federal adjusted gross income". 5219-KK(1)(D) uses the same definition.
  • Under the PR's own reading, that the additions are the filer's because federal AGI is, a dependent's benefits are out too. The body says that formulas which also summed a dependent's other amounts "get the same rule", and IA, MN and MT got it for Social Security.
  • Example, TY2025, the ME twin household plus a child with social_security_survivors: 10,000: income at this head is 8,000 + 28,000 + 500 = 36,500. The filers' own amount is 26,500.
  • This is not a regression, since main had the same.

Fix: compute the Social Security term from the filers' own benefits, for example tax_unit_non_dep_add(["social_security"]) − add(["taxable_social_security"]), in place of the list's TaxUnit item. Add the child-survivor case to me_sales_and_property_tax_fairness_credit_income.yaml.

S3. Two references break the CLAUDE.md reference rules.

  • nd_ltcg_subtraction.py adds two entries for one document, the 2025 ND-1 booklet, that differ only by #page=14 (line 6) and #page=15 (worksheet). CLAUDE.md: "URLs that differ only by #page= are one source". The pages themselves are right: I confirmed the line 6 text on file page 14, and the worksheet and the line 13 text on page 15. Fix: one entry ...booklet.pdf#page=14 with a # PDF pages 14-15 comment above it.
  • vt_percentage_capital_gains_exclusion.py cites only IN-153-2024.pdf#page=2 (Part II). The new netting and Form 4952 share come from Part I lines 1–7 on page 1, which is where line 10 ("Enter the amount from Part I, Line 4") and line 16 ("Part I, Line 7") point. Fix: #page=1 with a # PDF pages 1-2 comment.

S4. The axiom line is invalid and doesn't match the head.

  • The body's line is axiom: (hub files pe-parity before landing). That is none of the CLAUDE.md forms (encoded-correct, encoded, queued, n/a: <reason>).
  • The "Axiom note" names Encode IRC §1(g)(7) parent's election (the rule that decides whether a child's items reach a parent's return) TheAxiomFoundation/rulespec-us#1424. It is OPEN, labelled pe-parity, and dispatch-ready for 26 U.S.C. 1(g)(7). Its 2026-10-06 extension for this PR has verbatim law and companion cases 1–4 (ND, VT and WA) from the form lines and statutes, which matches the dependent-pooling part.
  • It does not cover the four corrections added since then, which change tax for units with no dependents:
    • Iowa's worksheet line 2;
    • the Virginia per-person cap (Va. Code 58.1-322.02(8));
    • North Dakota's floor and capital gain distributions (57-38-30.3(2)(d)(1); ND-1 line 6 worksheet);
    • Vermont's IN-153 netting and Form 4952 share.

Fix: write the line in the required form, e.g. axiom: TheAxiomFoundation/rulespec-us#1424 queued. Extend #1424, or file a pe-parity issue, with dispatch-ready cases for the four corrections. For ND, first check whether us-nd:...resident_core_liability_pipeline's net long-term gain input already floors at zero and includes distributions, which would make that part encoded-correct.

Suggestions

  1. Changelog and body scope. The changelog lists PA, and the body's table has PA and MT mt_agi_joint rows. But at this head pa_eligibility_income.py changes only its reference: main had already left dependents out (Wire military retirement pay and survivors into federal and state tax (#10181) #10182), as it had for MT joint AGI (Fix AR, MT and NM income tax gaps found in TAXSIM comparison sweep #9585). Consider leaving PA out of the fragment's state list.
  2. Stale "Not in this PR" and Invariants. Leave tax unit dependents' income out of Arkansas, Mississippi, New Jersey and Iowa income #10667 (merged 2026-11-07) now leaves dependents out of AR, MS, NJ and IA income, and Leave tax unit dependents' income out of Alabama AGI #9943 fixed AL. The PR's own AL test expects al_agi 100,000 and passes. Refresh the body's exception list after merging main.
  3. Mixed-sign allocation in filer_loss_limited_net_capital_gains_person. With a head +5,000 and a spouse −10,000, the shares are +3,000 and −6,000. They sum to the −3,000 limit, but the eligible head's GA or DE countable gain is 3,000, not their own 5,000. It is the same convention as the pooled variable and is defensible. Consider a sentence in the documentation and one YAML case that pins it.
  4. Vermont flat exclusion. IN-153 Part I line 8 also subtracts the line 7 Form 4952 share before the $5,000 cap. The PR applies the share only to the percentage path. This is pre-existing and worth a follow-up.
  5. Python test cost note. The body gives local timings for the new property test. CLAUDE.md also asks to check the affected CI group's existing timing and memory reports; here that is core. Cite the core group's CI timing artifact.

Verified correct

The federal person variable. filer_loss_limited_net_capital_gains_person:

  • own_gains is ~dependent × (capital_gains + max(0, non_sch_d_capital_gains)). Its sum equals filer_schedule_d_lines(...).line_16 for component inputs.
  • filer_loss_limited_net_capital_gains is max(−loss_limit[filing_status], line_16), with SEPARATE = 1,500.
  • In the loss branch, Σ(limited × own/Σown) = limited.
  • Carryovers are already inside long_term_capital_gains: long_term_capital_loss_carryover is a memo item that Schedule D line 14 includes.
  • The file is byte-identical to the copy Leave tax unit dependents' income out of Arkansas, Mississippi, New Jersey and Iowa income #10667 put on main, so the add/add merges cleanly. There is no reinvented variable, and readers are GA and the DE list, plus MS on main.

Probe at this head (TY2025, head of household, head aged 63, child aged 10). Each case also ran a twin with the child's amounts set to zero.

  • P, parent loss plus child gain. Head: wages 60,000, LT −5,000. Child: LT +20,000, all VT-eligible. Federal AGI is 57,000 = 60,000 − 3,000.
    • AZ, SC, ND and VT subtractions: 0, because the filers' net is a loss.
    • WI: wi_capital_loss 3,000 = min(3,000, 5,000).
    • GA ga_retirement_exclusion_countable_capital_gains and DE de_pension_exclusion_income: [−3,000, 0]. The pooled variable gives [−5,000, 20,000].
    • MT capital gains tax: 0.
  • Q, child loss plus parent gain. Head: wages 60,000, LT +10,000, all VT-eligible. Child: ST −8,000. Federal AGI is 70,000.
    • AZ 2,500 (25% × 10,000), SC 4,400, ND 4,000, WI subtraction 3,000.
    • GA and DE: [10,000, 0].
    • VT: percentage 4,000 and exclusion 5,000 (the flat amount is larger).
    • MT capital gains tax 410 (4.1% × 10,000).
  • state_income_tax equals the twin's to the cent in all 8 states for P and Q: AZ, SC, ND, WI, GA, VT, DE and MT.

Hand-checked expectations from the PR's YAML (arithmetic shown):

  1. filer_loss_limited_net_capital_gains_person [−2,400, −600, 0]. The filers' net is −10,000, limited to −3,000 (joint), and shared 0.8/0.2; the child has 0. This is a parent loss with a child gain.
  2. The same variable gives [5,000, 0] for head +5,000 and child −10,000. GA gives [5,000, 0]. DE de_pension_exclusion_income gives [15,000, 0] = pension 10,000 + 5,000. These are child losses with parent gains in GA and DE.
  3. AZ child-loss case: the filers' net is 10,000 > 0, so 25% × 10,000 = 2,500. ND child-loss case: 40% × min(2,000, 2,000) = 800. WI wi_capital_loss: the filers' net is +1,000, so 0.
  4. ND worksheet cases: line 15 = −2,000 gives 0 ("If zero or less, stop here"). Distributions of 1,000 give 400.
  5. VT netting: 40% × min(10,000, 10,000 − 4,000) = 2,400. Form 4952 share: 100,000 − 100,000 × 100,000 / 200,000 = 50,000, and 40% is 20,000.
  6. IA unit test 1: 20,000 + 38,000 + 2,000 = 60,000; less 32,000 is 28,000; half is 14,000, below 20,000. IA dependent case: 15,000 + 20,000 − 25,000 = 10,000, half 5,000.
  7. MA Part C household: Part B is 60,000 − 2,000 (payroll tax deduction) − 6,800 (HoH exemption) − 1,000 (dependent) = 50,200, × 5% = 2,510. Part C is 2,000 × 5% = 100. Less the $440 child and family credit, that is 2,170.
  8. UT retirement credit: 450 − (30,000 + 500 − 25,000) × 0.025 = 312.5, and 900 − (40,000 + 1,000 − 32,000) × 0.025 = 675.
  9. Others:
    • SC 4,400; WI 3,000; MD 2% × 10,000 = 200 (AGI 410,000 > 350,000).
    • NH 3,000 − 2,400 = 600.
    • ME 8,000 + 18,000 + 500 = 26,500; WV 8,000 + 500 = 8,500.
    • VA Guard 5,500 × 2 = 11,000, and the dependent's 5,500 neither counts nor uses up the cap.
    • MT mt_capital_gain_credit 2% × 2,000 = 40; mt_dependent_exemptions_person 1,894.57 + 2% × 1,000 = 1,914.57.

Law, per state. I read these this session:

  • VT. Schedule IN-153 (2024). Line 1 is the "smaller of Line 15 or 16 from federal Form 1040, Schedule D", and line 4 subtracts lines 18 and 19. Lines 5–7 are the Form 4952 share, 4g × 4e / (4b + 4e). Line 10 is "the amount from Part I, Line 4", line 15 "Entry cannot be less than zero", and line 17 = line 15 − line 16. The code matches.
  • ND. 2025 booklet worksheet: "Enter amount from 2025 Schedule D (Form 1040), line 15. If zero or less, stop here; no exclusion is allowed". Distributions without Schedule D go "on line 3 of this worksheet". Line 13 multiplies "the qualified dividends from Form 1040 or 1040-SR, line 3a, by 40 percent".
  • IA. 2022 IA 1040 instructions, Step 4 worksheet:
    • line 1 is box 5 of the SSA-1099, "If you filed a joint federal return, enter the totals for both spouses";
    • line 2 is "Enter one-half of line 1 amount";
    • line 13 is "the smaller of line 2 or line 12".
  • VA. Va. Code 58.1-322.02(8): "The wages or salaries received by any person ... not to exceed ... $5,500", which is a per-person cap.
  • NH. Rev 906.02(c): a joint return only for spouses filing a joint federal return. A dependent is a separate individual.
  • ME. 36 M.R.S. 5213-A(1)(B) and 5219-KK(1)(D) start from the individual's federal AGI.

The rest start from the filer's own federal AGI, taxable income or Schedule D:

  • AZ 43-1022(22): "included in federal adjusted gross income";
  • SC 12-6-1150; WI Schedule WD; MD's surtax on FAGI over $350,000;
  • WA RCW 82.87.040: "an individual's" gains, with spouses combined;
  • MT Form 2; MA c.62 §2; IL 203(a)(2)(A); MN via IRC 86; UT 59-10-1019, 1042 and 1047; WV 11-21-22a(d);
  • PA forgiveness, which counts the claimant and spouse; CA FTB 3514 line 23b; GA IT-511, "allocated to the person who owns the item"; DE 30 Del. C. 1106; MI MCL 206.30(1)(p).

None of these states taxes the family unit or has its own analogue of Form 8814.

Code checks.

  • Vectorization and entities. The code uses where, max_, min_, np.divide with where= masks, and & on booleans. The if "us_govt_interest_person" in p... checks test a parameter list (scalar), not data.
  • tax_unit_non_dep_add adds TaxUnit items such as ut_taxable_income, adjusted_gross_income and wv_additions as is. Person-level items are masked.
  • Periods and keys. Every variable is YEAR. There is no new state-gated int or enum key, so the NH Add New Hampshire state supplementation for 2026 #10156 pattern doesn't arise.
  • The VT netting inputs (capital_gains_28_percent_rate_gain, the unrecaptured section 1250 gain) are already filer-only.
  • Readers of us_govt_interest are all state tax subtraction lists. Per-person readers of us_govt_interest_person in DC, DE and OH are masked. VA's list is filer-only via va_subtractions and va_subtractions_person.

Tests and guards.

Axiom line assessment

Invalid; see S4. The body's line axiom: (hub files pe-parity before landing) is not in a CLAUDE.md form. rulespec-us#1424 is OPEN, labelled pe-parity and dispatch-ready, and its #9894 extension covers the dependent-pooling half of the change. It does not cover the four corrections that need no dependent: IA worksheet line 2, the VA per-person Guard cap, ND's floor and distributions, and VT's IN-153 netting and Form 4952 share.

CI status

gh pr checks 9894: 36 of 36 pass at be20e6f, run 41949737927. None is failing or pending. The merged tree has not run in CI.

Branch status

  • Position. 14 ahead and 63 behind main, with merge base abc67b9.
  • Conflict. git merge-tree --write-tree PolicyEngine/main be20e6f810 reports one conflict, in the docstring of policyengine_us/tests/core/test_state_tax_head_spouse_swap_invariance.py. Main's Leave tax unit dependents' income out of Arkansas, Mississippi, New Jersey and Iowa income #10667 says Delaware and Montana move dependents' amounts, and that AR, IA and MS leave them off. This PR says Montana leaves them off. Resolve it as: Delaware moves; AR, IA, MS and MT leave them off. The test logic already checks only de_agi_indiv against the moving allocation (MOVES_DEPENDENT_AMOUNTS).
  • Other files changed on both sides merge cleanly:
  • Re-run CI after resolving the conflict, since main's 63 commits haven't run against this PR's tax-total expectations (MA 2,170, MT 1,847.725 and 1,914.57, VT 6,623.20). I found no main change to those states' 2022–2025 paths.

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 per the review above: 0 critical, 4 should (unpinned dependent masks in several states; Maine's fairness-credit income still counts a dependent's Social Security; two references split or incomplete; axiom line not in a valid form and #1424 doesn't cover the four no-dependent corrections), 5 suggestions. The core filer-only loss limitation is sound.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants