Skip to content

Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction - #10093

Merged
MaxGhenis merged 22 commits into
mainfrom
or-federal-tax-subtraction-worksheet
Nov 8, 2026
Merged

MaxGhenis merged 22 commits into
mainfrom
or-federal-tax-subtraction-worksheet

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

What changes

Oregon lets residents subtract their federal income tax liability, capped by a schedule that falls with federal AGI (ORS 316.680(1)(b), 316.685, 316.695). Main computes the liability as max(0, income_tax + eitc). That already nets out every federal refundable credit PolicyEngine models except the EITC, but it never nets out the premium tax credit, because PolicyEngine keeps that credit out of income_tax. It also counts the employee Social Security and Medicare tax on unreported tips and wages.

This PR computes the liability as follows:

  • Start from income_tax_before_refundable_credits: federal tax after nonrefundable credits plus the other taxes PolicyEngine adds there, including the net investment income tax.
  • Take out unreported_payroll_tax. ORS 316.685(3)(a) says federal income tax does not include employee Social Security and Medicare taxes, and the OR-17 worksheet leaves them out too.
  • Subtract the federal refundable credit total (income_tax_refundable_credits, which at baseline adds the gov.irs.credits.refundable list), less eitc when that list carries it, plus the premium tax credit (assigned_aca_ptc, the allowed credit on Form 8962, line 24).
  • Floor at zero, then apply the existing AGI cap.

PolicyEngine follows the statute's text here, read with the publication's own prose.

Refundable credits

The subtraction now nets every federal refundable credit except the EITC, plus the premium tax credit. ORS 316.685(1)(b) says the taxpayer "shall not take into account any amount of the earned income credit provided under section 32 of the Internal Revenue Code". It leaves out no other credit. Publication OR-17 (2025, PDF page 71) says the same: "Federal income tax credits, except for the EITC, reduce your federal tax subtraction."

26 U.S.C. 36B(a) allows the premium tax credit "as a credit against the tax imposed by this subtitle", so it reduces the subtraction like any other credit. The OR-17 worksheet's Part A lists fewer credits each year than the statute and the prose allow: outside 2021 it omits the additional child tax credit, and it never lists the refundable payroll tax credit. This PR follows the statute, so both reduce the subtraction in every year. A refundable credit that PolicyEngine later adds to the federal list, or that a reform adds through income_tax_refundable_credits, reduces the subtraction with no Oregon change.

The net investment income tax

The PR keeps the net investment income tax in the subtraction, as main does, because the statute does. 26 U.S.C. 1411(a)(1) imposes the tax "in addition to any other tax imposed by this subtitle", in Subtitle A (Income Taxes), chapter 2A. ORS 316.685(3) gives a closed list of what federal income tax does not include. It names employee payroll taxes and Self-Employment Contributions Act taxes (Subtitle A, chapter 2), not chapter 2A. The OR-17 worksheet's other-taxes line leaves the net investment income tax out.

The tax can matter only where the cap stays positive above the tax's threshold: head-of-household filers with modified AGI from $200,000 to $290,000, joint and surviving-spouse filers from $250,000 to $290,000, and separate filers from $125,000 to $145,000. Even there, it changes the subtraction only when nonrefundable credits leave less federal tax than the cap. For example, a 2025 head-of-household filer with $230,000 of long-term gains, a child aged 10, and $200,000 of solar spending has their regular tax wiped out by the clean energy credit. They subtract the $1,140 net investment income tax, inside the $8,500 cap (case 20).

Sources

Where it matters

The premium tax credit drives the main change: for marketplace enrollees who owe federal tax, main overstated the subtraction by the credit. Household calculations also move. PolicyEngine assigns a premium tax credit by default to a filer with no coverage inputs. For example, a single 35-year-old earning $50,000 subtracts $751.72 in 2024 (federal tax of $4,016 less a $3,264.28 credit) instead of $4,016.

The unreported payroll tax lowers the subtraction for filers who owe it.

Microsimulation impact

We estimate that this branch raises Oregon income tax by $9.7m in 2025 on the default dataset. We compared main at 097b3c7f8a with this branch's merge commit, cc9d7f989d. The current head, 7a0bf46dd8, changes only comments, one test's parameter override and a register note, so the estimate holds for it.

The subtraction falls by $123.0m for 19 tax units (38,494 weighted), by at most $5,920 each, and Oregon income tax rises by at most $467 per unit. Household net income falls by the same $9.7m, and no household gains. Federal income tax, the premium tax credit, federal refundable credits and household_benefits don't change. The default dataset carries no unreported payroll tax, so the whole change comes from netting out the premium tax credit: main's liability, income_tax + eitc, already nets every other refundable credit except the EITC.

Output Main total Change Records changed (weighted)
or_federal_tax_liability_subtraction $5.64bn -$123.0m 19 (38,494)
or_income_tax $12.75bn +$9.7m 19 (38,494)
state_income_tax $548.2bn +$9.7m 19 (38,494)
household_net_income $16,380.3bn -$9.7m 18 (38,494)
income_tax $2,422.0bn $0 0 (0)
assigned_aca_ptc $60.62bn $0 0 (0)
unreported_payroll_tax $0 $0 0 (0)

In 2026, we estimate that this branch raises Oregon income tax by $7.3m. The subtraction falls by $83.5m for 16 tax units (29,192 weighted), by at most $5,581 each, and Oregon income tax rises by at most $488 per unit. Household net income falls by the same $7.3m, and no household gains. Federal income tax, the premium tax credit, federal refundable credits and household_benefits don't change. We compared the same two commits, and the default dataset again carries no unreported payroll tax.

Output Main total Change Records changed (weighted)
or_federal_tax_liability_subtraction $5.73bn -$83.5m 16 (29,192)
or_income_tax $15.14bn +$7.3m 16 (29,192)
state_income_tax $577.3bn +$7.3m 16 (29,192)
household_net_income $17,198.3bn -$7.3m 16 (29,192)
income_tax $2,595.3bn $0 0 (0)
assigned_aca_ptc $47.56bn $0 0 (0)
unreported_payroll_tax $0 $0 0 (0)

Overlap with #10038

#10038 landed first. It added claim_of_right_credit to gov.irs.credits.refundable, so this PR's formula subtracts the section 1341 credit through income_tax_refundable_credits with no Oregon change. Merge commit cc9d7f989d brings main into this branch and resolves the three files the two PRs share:

A follow-up commit, 7a0bf46dd8, tightens three notes after an independent review of the merge: the comment above names both section 1341 methods, Case 21's override list keeps claim_of_right_credit, and the register note dates the 2026-10-20 statute ruling. No formula or expected value changed. The same review ran a differential check at the merge over 960 Oregon tax units with section 1341 repayments under both methods, in 2020, 2025 and 2026. With no premium tax credit or unreported payroll tax, the merged subtraction equals main's to the cent.

Tests

  • or_federal_tax_liability_subtraction.yaml: 22 cases, each with its arithmetic in a comment; all 22 pass at 7a0bf46dd8. The 10 existing cases set income_tax_before_refundable_credits instead of income_tax.
    • Cases 11, 13 and 15 cover the premium tax credit, the floor, and the 2021 credits.
    • Case 12: a $600 additional child tax credit reduces a $1,000 liability to $400, and the $500 EITC doesn't reduce it further. Counting the EITC would give 0; leaving out the additional child tax credit would give 1,000.
    • Case 14: the net investment income tax stays in, and the unreported payroll tax comes out.
    • Case 16: the 2020 additional child tax credit reduces the subtraction.
    • Case 17: the 2026 additional child tax credit reduces the subtraction.
    • Case 18: the refundable payroll tax credit reduces the subtraction.
    • Case 19: a 2025 family's additional child tax credit offsets a 10% early-distribution tax.
    • Case 20: the head-of-household witness above.
    • Case 21: with no EITC on the federal refundable credit list, the code backs out none.
    • Case 22: Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038's case. A $2,200 section 1341 credit lowers the subtraction to $471.50; adding the credit back would give $2,671.50.
  • policyengine_us/tests/policy/contrib/harris/lift/middle_class_tax_credit.yaml: a new case under the Harris LIFT middle-class credit reform, which adds its credit through income_tax_refundable_credits rather than the federal list. A 2025 Oregon single filer aged 35 with $50,000 of wages and aca_ptc: 0 gets a $1,718.13 credit, income_tax of $2,153.37, a subtraction of $2,153.37 and or_income_tax of $3,014.64, as on main. The file passes 6 of 6.
  • policyengine_us/tests/core/test_or_federal_tax_liability_subtraction_invariants.py: Hypothesis properties for 2020, 2021 and 2025. The test draws federal tax before refundable credits from 3,000 to 20,000 and each credit from 0 to 1,000. So the dollar-for-dollar check in property 3 mostly runs on units with a positive subtraction below the cap, not on units floored at zero. In CI at 2bffdc0942 it runs in the Rest Python core group (shard 2, chunk core-1), where its three cases take about 0.43 s each and the chunk peaks at 9.3 GiB of memory over 25:57.
  • gov/states/or/tax/income/integration.yaml: the four $50,000 single-filer cases set aca_ptc: 0, as the file's elderly case does. That keeps them testing the kicker arithmetic their comments compute.

Pull request run 42514224872 passed all 36 jobs at the current head, 7a0bf46dd8. Locally, the Oregon YAML file (22 of 22) and the source-conflict guard (103 of 103) pass at 7a0bf46dd8. At the merge, the invariants test (3 of 3), Oregon's integration.yaml, #10038's section 1341 state property test and three other files that compute the subtraction (47 of 47) also pass.

Invariants

With T = federal tax before refundable credits, U = the unreported payroll tax and cap = the AGI-based limit:

  1. 0 ≤ subtraction ≤ min(max(0, T − U), cap(AGI)).
  2. The EITC never changes the subtraction. Neither does the net investment income tax beyond its share of T. Outside 2021, neither does the child and dependent care credit.
  3. While the cap doesn't bind, raising the premium tax credit, the additional child tax credit, the refundable payroll tax credit or U by d lowers the subtraction by min(d, subtraction). In 2021 the child and dependent care credit does the same. Where the cap binds, the subtraction never rises.
  4. Raising T by d raises the subtraction by at most d, and never lowers it.

Source conflicts

The OR-17 worksheet conflicts with the statute on two points: it leaves out the net investment income tax, and it lists fewer refundable credits than the statute allows. PolicyEngine follows the statute on both. source_conflicts/candidates.yaml records them as us-or:federal-tax-subtraction-niit and us-or:federal-tax-subtraction-unlisted-refundable-credits, alongside us-or:claim-of-right-federal-tax-subtraction.

impact: Oregon income tax +$9.7m in 2025 (19 tax units, 38,494 weighted) and +$7.3m in 2026 (16 tax units, 29,192 weighted), from netting out the premium tax credit
axiom: TheAxiomFoundation/rulespec-us#2027 queued. Oregon's federal tax subtraction composition and cap integration wait on historical source ingestion and a signed corpus re-pin.

🤖 Generated with Claude Code

MaxGhenis and others added 4 commits October 11, 2026 09:44
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ndable credits only

ORS 316.680(1)(b) and 316.685; Publication OR-17 federal tax worksheet,
Part A (2017-2025). The worksheet starts from Form 1040 line 22 plus the
other income taxes on Schedule 2, lines 8, 16 and 17, and subtracts only
the American Opportunity credit, the premium tax credit, and in 2020-2021
the recovery rebate credit and (2021) the refundable child tax and child
care credits. Main used income_tax + eitc, so it subtracted the additional
child tax credit in every year, never subtracted the premium tax credit,
and kept the net investment income tax.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bounds, independence from the EITC (and the ACTC outside 2021), and
1-Lipschitz responses to listed credits and federal tax, checked with
Hypothesis on batches of twin tax units in one simulation per example.

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

Bring the branch up to date with main before review; the merge was clean.

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

Copy link
Copy Markdown
Contributor Author

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


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 7807e297fdf308b4950664dea3645ffdb28ae469

This continues an earlier session that pushed nothing to this PR. The four commits from 2026-10-11 are kept unchanged; this session added only a merge of origin/main (clean, no conflicts).

Blocker: primary sources not re-read

This session's network policy refused www.oregon.gov and web.archive.org (I did not try further hosts once two were refused). So I could not fetch ORS 316.680/316.685 or any year's Publication OR-17, and I have not verified the PR's quotes, line numbers or page numbers. Everything below checks the code against the worksheet as the PR describes it. Before landing, someone with access should confirm, for each year 2017–2025: the credits on Part A's subtraction lines, that line 1 is Form 1040 line 22, and that the other-taxes line is Schedule 2, lines 8, 16 and 17 only. I removed the verbatim OR-17 and ORS quotes from the PR body for that reason.

Items

  1. Still needed? Yes. Main still computes max_(0, income_tax + eitc) (or_federal_tax_liability_subtraction.py on main, lines 17–19). No other open PR does this change.
  2. Overlap: Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 (open) edits the same file: the reference tuple (lines 10–17 here) and the comment above or_federal_income_tax (lines 36–41 here). Its new YAML case uses income_tax: 2_500 as input, which yields 0 under this PR's formula; whichever lands second moves that input to income_tax_before_refundable_credits. I did not touch Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038.
  3. Right? Read on this branch:
    • income_tax_before_refundable_credits = income_tax_before_credits + NIIT + investment-credit recapture + unreported_payroll_tax + qualified_retirement_penalty − capped nonrefundable credits. Subtracting NIIT and unreported_payroll_tax (or_federal_tax_liability_subtraction.py:31-35) leaves tax after nonrefundable credits plus recapture and the retirement-plan penalty, which matches the PR's reading of worksheet line 5.
    • assigned_aca_ptc is the allowed credit times take-up; PolicyEngine models no advance payments or excess-APTC repayment, so it stands for Form 8962, line 24 as the PR intends.
    • The 2021 list mirrors the federal 2021 refundable list plus the PTC; 2020 adds recovery_rebate_credit.
  4. Reviews: none on the PR (no reviews, inline comments or issue comments besides pick-up notes).
  5. Defect reproduced: with main's formula restored over the PR's YAML file, 6 of the 7 new cases fail (11–14, 16, 17). Case 15 (2021) passes on main too, because main's 2021 federal refundable list already nets out the same credits and the case has no PTC.

Hand computations (all under the cap)

Case Year Computation Expected
11 2025 4,000 − 1,500 PTC 2,500
12 2025 1,000 other tax; ACTC 3,000 and EITC 500 ignored 1,000
13 2025 max(0, 2,000 − 1,000 AOC − 1,500 PTC) 0
14 2025 5,000 − 600 NIIT − 400 unreported payroll tax 4,000
15 2021 9,000 − 3,000 CTC − 2,800 RRC − 1,000 CDCC 2,200
16 2020 3,000 − 2,400 RRC; ACTC ignored 600
17 2026 6,000 − 1,000 AOC − 2,000 PTC; ACTC ignored 3,000

Tests run (merged head)

  • or_federal_tax_liability_subtraction.yaml: 17 passed.
  • test_or_federal_tax_liability_subtraction_invariants.py: 3 passed (2020, 2021, 2025; 4 derandomized examples of 6 units × 5 variants each).
  • test_source_conflicts.py: 103 passed.
  • ruff format --check and ruff check on the changed Python files: clean.

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

  • Drop assigned_aca_ptc from the 2022 list: 3 YAML failures (killed).
  • Stop netting out the NIIT: 1 YAML failure, case 14 (killed).

CI

Draft, so Pull request CI skips it. CI has not run at this head.

Judgment calls

  • Kept the PR's reading that ORS 316.685 governs and the OR-17 worksheet fills in which credits count. I could not check the statute text, so I can't say whether the worksheet's exclusion of the NIIT is a gap-filler or conflicts with ORS 316.685; if it conflicts, the source-conflicts register applies.
  • The cap brackets start in 2021 while the credit list starts in 2017; I left that as main had it.

Proposed follow-up

  • Oregon federal tax subtraction cap before 2021. The cap schedules under federal_tax_liability/cap/ start in 2021, though the subtraction applies earlier. Add the 2017–2020 amounts from those years' OR-40 instructions.

Generated by Claude Code

@MaxGhenis
MaxGhenis marked this pull request as ready for review November 3, 2026 20:49
@codecov

codecov Bot commented Nov 3, 2026 •

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (aae7f98) to head (7a0bf46).
⚠️ Report is 67 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10093   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         1           
  Lines           37        21   -16     
=========================================
- Hits            37        21   -16     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Reviewing head 7807e297fdf308b4950664dea3645ffdb28ae469. Findings so far, to be replaced by the full review:

  • P1: CI is red at this head. policyengine_us/tests/policy/baseline/gov/states/or/tax/income/integration.yaml fails 3 kicker cases (Quick Feedback and Baseline states-shard-3). A default single filer aged 35 earning $50,000 gets assigned_aca_ptc of $3,264.28 (2024) or $3,735.68 (2025), and the subtraction now nets it out. Main passes 9/9. Adding takes_up_aca_if_eligible: false to the three kicker cases' tax units passes 9/9 at this head.
  • P2: the body's NIIT sentence is false for head-of-household, joint and separate filers. A 2025 head-of-household witness at $230,000 AGI gives 0 here, against $1,140 if the NIIT counted.
  • P2: the body's plan for Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 fails. In a trial merge, Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038's case with its input moved to income_tax_before_refundable_credits computes 4,000, not 2,500, because claim_of_right_credit is not in the Oregon list.
  • P2: the NIIT and additional-child-tax-credit exclusions follow the worksheet over a statute that names only the EITC and payroll taxes, and no source-conflict candidate records that choice.

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: REQUEST_CHANGES
7807e29
METHODOLOGY_CHOICE: yes

The head hasn't moved past 7807e297fd. Main (6145ad4513) is this head's merge base, so the diff below is the whole PR.

The formula follows the federal tax worksheet, Part A, as the hub read it for 2017 to 2025, and every hand value I computed matches. Three things block it: CI is red, two statements in the body are false, and the worksheet-over-statute choices aren't recorded as source conflicts.

P1

1. CI fails: three Oregon kicker integration cases break because the subtraction now nets out the imputed premium tax credit

policyengine_us/tests/policy/baseline/gov/states/or/tax/income/integration.yaml:199, :228 and :253 (the three kicker cases).

The Pull request run at this head concluded failure. Two jobs failed, Quick Feedback (Selective Tests + Coverage) and Baseline tests / Full Suite - Baseline (states-shard-3), both on this file. Every other job passed, including Household API Partners and Microsimulation. Contrib tests were skipped.

The hand-off's line "Draft, so Pull request CI skips it" no longer holds: the PR isn't a draft, and CI has run.

Failure scenario: a single filer aged 35 with $50,000 of wages and no other inputs. takes_up_aca_if_eligible defaults to true, so the filer gets a premium tax credit, and this PR now subtracts it from the federal tax.

$ uv run python -m policyengine_core.scripts.policyengine_command test policyengine_us/tests/policy/baseline/gov/states/or/tax/income/integration.yaml -c policyengine_us
  Test '2025 single filer includes the kicker on the 2025 return':
    or_income_tax_before_credits@2025: [3805.053] differs from 3478.179931640625 ...
  Test '2025 kicker uses the provided 2024 liability, not the 2025 liability':
    or_income_tax_before_credits@2025: [3805.053] differs from 3478.179931640625 ...
  Test '2024 single filer receives no kicker in an even year':
    or_taxable_income@2024: [46503.28] differs from 43239.0 ...
3 failed, 6 passed          (main 6145ad4513: 9 passed)

The same household through Simulation at this head:

Year Tax before refundable credits assigned_aca_ptc Subtraction here Subtraction on main
2024 4,016.00 3,264.28 751.72 4,016
2025 3,871.50 3,735.68 135.82 3,871.50

The behavior is what the PR intends, but the PR left the integration expectations stale. These cases test the kicker, so the least invasive fix keeps them on their purpose. Add one line under tax_unit: in each of the three kicker cases:

    tax_units:
      tax_unit:
        members: [person1]
        takes_up_aca_if_eligible: false
        or_tax_before_credits_in_prior_year: ...   # unchanged

With that change the file passes 9/9 at this head. I ran it on a scratch copy and pushed nothing. Alternatively, keep take-up on and recompute the expected values with the credit netted out. Either way the hub should expect household-API results for Oregon filers without employer coverage to move by up to the full credit.

P2

2. The body's net investment income tax sentence is false for every filing status except single

The body says: "The net investment income tax rarely matters, because the cap reaches zero ($145,000 single, $290,000 joint) at or below the tax's $200,000 and $250,000 thresholds."

The cap stays positive above the NIIT threshold for head-of-household filers ($200,000 to $290,000), joint and surviving-spouse filers ($250,000 to $290,000) and separate filers ($125,000 to $145,000). I read the 2025 cap parameters, and they match the hub's reading of Table 9.

It binds whenever nonrefundable credits wipe out the regular tax, because those credits are capped at income_tax_before_credits and never reach the NIIT. Witnesses at this head, 2025, built from ordinary inputs with no tax variables set:

Household AGI Regular tax Nonrefundable credits NIIT Subtraction here With the NIIT counted (main's treatment)
Head of household, $230,000 long-term gain, one child aged 10, $200,000 solar expenditure 230,000 21,243.75 21,243.75 1,140 0 1,140 (cap 8,500)
Joint, $270,000 long-term gain, $200,000 solar expenditure 270,000 21,270 21,270 760 0 760 (cap 3,400)

Without such credits, federal tax at these incomes exceeds the cap. A head-of-household filer with $210,000 of gains and no solar gets the full $8,500 cap either way. So the exclusion changes the result only for filers whose credits leave less tax than the cap.

Replacement text for the body:

The net investment income tax matters only where the cap stays positive above the tax's threshold: head-of-household filers with modified AGI from $200,000 to $290,000, joint and surviving-spouse filers from $250,000 to $290,000, and separate filers from $125,000 to $145,000. Even there it changes the subtraction only when nonrefundable credits (for example the residential clean energy credit) leave less federal tax than the cap. A 2025 head-of-household filer at $230,000 whose regular tax the clean energy credit wipes out subtracts $0 here, against $1,140 with the NIIT counted.

3. The body's plan for #10038 doesn't work: the two PRs disagree on the section 1341 credit

Body, "Overlap": "that test moves its input to income_tax_before_refundable_credits; otherwise it computes 0 under this formula."

#10038 adds claim_of_right_credit to gov.irs.credits.refundable. Its new case asserts that the section 1341 credit reduces the Oregon subtraction: income_tax: 2_500 plus claim_of_right_credit: 1_500 gives 2,500. This PR's list doesn't include claim_of_right_credit, so the credit no longer reduces anything.

I trial-merged pr-10038 into this head on a scratch copy, kept this PR's side of every conflict, and added #10038's case with its input moved as the body proposes (income_tax_before_refundable_credits: 4_000, claim_of_right_credit: 1_500, expect 2,500):

  Test '10038 case with input moved per 10093 body':
    or_federal_tax_liability_subtraction@2025: [4000.] differs from 2500.0 with an absolute margin [1500.] > 0.001
1 failed, 17 passed

Then I aborted the merge and confirmed git status was clean.

The lines the two PRs share in or_federal_tax_liability_subtraction.py:

Both PRs also append to or_federal_tax_liability_subtraction.yaml.

The register's candidate us-or:claim-of-right-federal-tax-subtraction (in source_conflicts/candidates.yaml, which lists #10038) is recorded as "Ruled in October 2026 to follow the worksheet". It says the worksheet doesn't subtract the section 1341 credit. This PR matches that ruling. #10038's comment and test contradict it.

Replacement text for the body's Overlap paragraph:

#10038 edits the same reference tuple and formula lines, and asserts that the section 1341 credit reduces this subtraction. This PR's list leaves that credit out, matching the ruling recorded for us-or:claim-of-right-federal-tax-subtraction (follow the worksheet, which doesn't subtract it). Whichever PR merges second keeps this formula. It moves #10038's case input to income_tax_before_refundable_credits: 4_000 and changes the expected value to 4,000, unless the hub rules that the section 1341 credit belongs in refundable_credits.

4. The worksheet-over-statute choices aren't recorded as source conflicts

The hub read the statute this way:

  • ORS 316.685(1)(b) leaves out only the earned income credit.
  • ORS 316.685(3) leaves out only the employee payroll taxes and self-employment tax.
  • The statute names no other tax, and in particular not the NIIT.

This PR follows the worksheet instead in two places:

  • The NIIT. The PR removes it (or_federal_tax_liability_subtraction.py:27-35). The statute doesn't say whether the section 1411 tax is among "federal income taxes accrued", so this is a reading the text leaves open, not a flat conflict. CONTRIBUTING.md's tie-break then follows the administering agency, which is what the PR does.
  • The additional child tax credit and other unlisted refundable credits. Outside 2021 the PR doesn't subtract them, nor the refundable payroll tax credit (refundable_credits.yaml). The existing candidate quotes the publication's prose as saying credits other than the EITC reduce the subtraction, and the worksheet doesn't list the additional child tax credit. That is the same prose-against-worksheet question as the section 1341 credit.

CLAUDE.md says to record every such choice. The PR adds no entry to source_conflicts/candidates.yaml, and test_source_conflicts.py can't catch that (it passes: 103 passed).

Fix: step 1 of CONTRIBUTING.md#source-conflicts. Extend us-or:claim-of-right-federal-tax-subtraction into a worksheet-credits item, or add us-or:federal-tax-subtraction-niit and us-or:federal-tax-subtraction-unlisted-refundable-credits. Each needs years: 2017–2025, the statute and worksheet sources, both readings, pe_us.follows: worksheet, code: [the variable, refundable_credits.yaml] and prs: [#10093]. Add no marker yet.

P3

5. The formula comment overstates the worksheet's other-taxes line

or_federal_tax_liability_subtraction.py:27-31 says "the other taxes on Schedule 2, lines 8, 16 and 17". The hub's reading:

  • 2017 to 2019: retirement-plan tax, homebuyer recapture and recapture taxes.
  • 2020: lines 6, 7b and 8.
  • 2021 to 2024: add line 10 for some first-time homebuyer credit repayments.
  • 2025: excludes line-17 penalties and interest, and adds Form 1040-NR line 23a.

Replacement:

        # Federal tax worksheet, Part A: federal tax after nonrefundable
        # credits plus the other income taxes the worksheet lists for the year
        # (from 2021, Schedule 2, lines 8, 16 and 17, and some line 10
        # homebuyer repayments). Every year leaves out the net investment
        # income tax and the Social Security and Medicare tax on unreported
        # tips and wages (ORS 316.685(3)).

6. Reference titles break the repo's page convention

refundable_credits.yaml:

  • The 2017, 2018, 2019, 2022, 2023 and 2024 titles cite a single page but end with #page N. CLAUDE.md puts a single page in the href only. Drop #page 63 and the others from those titles.
  • The 2020 title says #page 65-66, but its href opens #page=66. The href must open the first page, so change it to publication-or-17_101-431_2020.pdf#page=65.

I couldn't open any OR-17 PDF, so I haven't checked the page numbers themselves.

7. The Hypothesis file kills none of the four mutants

test_or_federal_tax_liability_subtraction_invariants.py:

  • Property 1 bounds by income_tax_before_refundable_credits and the zero-AGI cap. Both are weaker than the stated min(T − NIIT − unreported payroll tax, cap(AGI)).
  • Property 3 allows a listed credit to change nothing.

So dropping the premium tax credit, the NIIT netting, refundable_ctc or cdcc all pass it (table below). Tighten property 3 so that raising assigned_aca_ptc by d lowers the subtraction by exactly min(d, slack), where slack is how far the uncapped amount sits above zero. Bound property 1 by the AGI-specific cap and by T − NIIT − unreported payroll tax.

The file takes 47 s of wall time here, including model build, for 3 tests. The hand-off reports no time or memory as CLAUDE.md asks; it runs in the Python "Rest" shards.

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

  • 2021 refundable_ctc counts the $500 credit for other dependents once the ARPA increase phases out.
    • Witness: 2021, joint, $300,000 wages, a child aged 4 and a dependent aged 20. ctc is 2,500 and refundable_ctc is 2,500 (non_refundable_ctc 0). At $60,000 or $100,000 the same family gets refundable_ctc 3,600 and non_refundable_ctc 500.
    • Cause: ctc_refundable_maximum isn't reduced by the ARPA phase-out.
    • Federal total tax doesn't change, but every state that reads refundable_ctc sees the wrong split.
    • For this subtraction it cancels if Schedule 8812 line 14e includes the nonrefundable credit for other dependents (see "What I couldn't check"). Oregon's 2021 cap is 0 at $290,000 and above anyway.
  • The cap before 2021. The cap brackets start in 2021, so 2020 and 2019 back-fill the 2021 amounts: a single filer at $80,000 gets a 7,050 cap in both years. The subtraction is in or_income_subtractions only from 2020 (subtractions.yaml), so the 2017–2019 list entries are unreachable at baseline. The hand-off already proposes this follow-up.

Checked and sound

  • Sources. From this VM every host refused the connection: oregonlegislature.gov, oregon.gov, web.archive.org, govinfo.gov, uscode.house.gov, ecfr.gov, irs.gov, law.cornell.edu and ssa.gov. I relied on the hub's 2026-11-03 reading of ORS 316.680, 316.685 and OR-17 Part A for 2017 to 2025, and quote no law beyond it.
  • The defect reproduces at main. Running the PR's YAML against main's code fails 6 of 17 cases (11, 12, 13, 14, 16 and 17). Case 15 passes on main because main's 2021 federal list already nets out the same three credits. At this head: 17 passed.
  • The credit lists match the hub's reading in every year.
    • 2017–2019 and 2022 on: the American Opportunity credit and the premium tax credit.
    • 2020: adds the recovery rebate credit.
    • 2021: the child tax credit, American Opportunity, recovery rebate, premium tax and child care credits.
    • 2025: the adoption credit has no variable in the model (only qualified_adoption_assistance_expense), as the comment says.
  • The components of income_tax_before_refundable_credits, read in this session:
    • income_tax_before_credits (income_tax_main_rates, capital_gains_tax, alternative_minimum_tax) less income_tax_capped_non_refundable_credits, which is capped at income_tax_before_credits. This is worksheet line 1 in every year. The AMT sits inside it, before nonrefundable credits, as on Form 1040. Kept.
    • qualified_retirement_penalty: the retirement-plan tax, on the other-taxes line every year. Kept.
    • recapture_of_investment_credit: recapture taxes in 2017–2019, Schedule 2 line 17 from 2021, and income tax in 2025. Kept.
    • net_investment_income_tax: left out by the worksheet every year. Removed.
    • unreported_payroll_tax: left out by the worksheet and by ORS 316.685(3). Removed.
    • Not modeled, so nothing to remove: self-employment tax and the additional Medicare tax (neither is in the variable).
    • Limits. No model input carries:
      • the first-time homebuyer credit repayment (no matching variable);
      • the excess advance premium tax credit repayment (the model has no advance credit, so there is nothing to subtract);
      • the Form 1040-NR line 23a tax.
  • Stimulus payments. recovery_rebate_credit adds rrc_cares, rrc_caa and rrc_arpa, each the full entitlement from the year's AGI with no advance-payment input. So it stands for "payments received plus the recovery rebate credit", except where an advance computed from an earlier year's AGI exceeded the entitlement. 2020 single at $80,000: 1,300.
  • 2021 identity. The model puts the credit-for-other-dependents part of the credit in non_refundable_ctc (netted inside income_tax_before_refundable_credits) and the rest in refundable_ctc. Line 1 plus the line-2 add-back of Form 1040 line 19, less line 8, then equals income_tax_before_refundable_credits − refundable_ctc, provided Schedule 8812 line 14e is the whole credit (the refundable part plus line 19). Witness at $60,000 joint: income_tax_before_credits 3,790, nonrefundable 500, refundable 3,600. cdcc in 2021 returns cdcc_potential uncapped because cdcc is in that year's federal refundable list, which is the refundable Schedule 3 line 13g credit the worksheet means.
  • The 2025 caps match Table 9.
    • Single and separate filers reach zero at $145,000.
    • Joint, head-of-household and surviving-spouse filers: 8,500, 6,800, 5,100, 3,400, 1,700, then 0 at $250,000, $260,000, $270,000, $280,000 and $290,000.
  • Hand values, computed from the worksheet arithmetic before I read the code, all match the YAML:
    • case 11: 4,000 − 1,500 = 2,500;
    • case 12: 1,000;
    • case 13: max(0, 2,000 − 2,500) = 0;
    • case 14: 5,000 − 600 − 400 = 4,000;
    • case 15: 9,000 − 3,000 − 2,800 − 1,000 = 2,200;
    • case 16: 3,000 − 2,400 = 600;
    • case 17: 6,000 − 3,000 = 3,000.
  • Mutants, each run in its own process on a scratch copy, which I restored (git status clean):
Mutant YAML (killing cases) Hypothesis file
Drop assigned_aca_ptc from the 2022 list killed: 11, 13, 17 passes (3/3)
Stop netting out the NIIT killed: 14 passes
Drop refundable_ctc from the 2021 list (mine) killed: 15 passes
Drop cdcc from the 2021 list (mine) killed: 15 passes

What I couldn't check

  • No primary source was reachable from this VM (list above). In particular, I couldn't check:
    • the OR-17 page numbers in the references;
    • the parameter's per-year worksheet line numbers ("Line 6", "Line 11" and the rest);
    • Schedule 2 line numbers 5, 6 and 12 in the comments;
    • the content of 2021 Schedule 8812 line 14e (the 2021 identity rests on it).
  • For the hub to confirm in the 2020 worksheet: its other-taxes line includes Schedule 2 line 8. If 2020's line 8 carried Form 8960 (the NIIT), the 2020 worksheet would count the NIIT, and this PR's year-invariant exclusion would be wrong for 2020. I couldn't open the 2020 Schedule 2 to tell.
  • Population impact: no microsimulation data here. CI's Microsimulation job passed.

Generated by Claude Code

These cases test the kicker and assume Oregon subtracts the whole federal
liability. The model gives this household (no coverage inputs) a 3,264
(2024) or 3,736 (2025) premium tax credit, which the OR-17 worksheet now nets
out of the subtraction, so the kicker cases broke. Setting aca_ptc to 0, as
the file's elderly case already does, keeps them testing what their comments
compute.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6Fmi4DmuA9GyXnRKdV
- Record the worksheet's NIIT exclusion and its unlisted refundable credits as
  source-conflict candidates, and list this PR on the claim-of-right item.
- Reword the formula comment: the other-taxes line differs by year.
- Drop single-page numbers from reference titles and open the 2020 href on
  the first cited page.
- Tighten the Hypothesis properties: bound by T less the NIIT and unreported
  payroll tax and by the cap at the unit's AGI, and require a listed credit to
  lower an uncapped subtraction dollar for dollar. Dropping the premium tax
  credit, the NIIT netting, or the 2021 child tax or child care credit now
  fails it.

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

Copy link
Copy Markdown
Contributor Author

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


Generated by Claude Code

Bring the branch up to date before the round 1 fixes.

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

ORS 316.685(1)(b) leaves out only the earned income credit, so every other
federal refundable credit reduces the subtraction, and so does the premium
tax credit. Read the federal refundable list without the EITC, plus
assigned_aca_ptc, instead of the worksheet's closed per-year list. This
restores the additional child tax credit outside 2021 and the refundable
payroll tax credit, and a credit added to the federal list later reduces
the subtraction with no Oregon change.

Keep the net investment income tax in the base: 26 U.S.C. 1411(a)(1)
imposes it under Subtitle A, and ORS 316.685(3) excludes only payroll and
self-employment taxes. Keep the employee payroll tax on unreported tips
and wages out, per 316.685(3)(a).

Move cases 12, 14, 16 and 17 to the new rules, and add a refundable payroll
tax credit case, an additional child tax credit family with an
early-distribution tax, and a head-of-household witness that owes the net
investment income tax after clean energy credits wipe out its regular tax.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The net investment income tax and refundable credits the OR-17 worksheet
does not list are ruled for ORS 316.685, and the code now follows it. The
claim-of-right item was ruled for the statute on 2026-10-20; its note said
otherwise. Drop the deleted refundable_credits.yaml from the code paths.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Bound the subtraction by the AGI cap and by federal tax before refundable
credits less the unreported payroll tax, check that the EITC and the net
investment income tax (with T fixed) never move it, and check that the
premium tax credit, the additional child tax credit, the refundable payroll
tax credit, the unreported payroll tax and, in 2021, the child care credit
lower it by exactly min(d, slack) below the cap.

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

Ready for hub review
088177c

Round 1 fixes, built on 0f5c79db79 and 073c5c7b5c. No commit I didn't write landed while I worked: the remote head was still 073c5c7b5c just before my push.

Commits

  • 34e0940530: merges origin/main (adea7035ea) with a merge commit. No files conflicted.
  • dfe261c991: makes the formula follow ORS 316.685, deletes refundable_credits.yaml, moves the affected cases and adds cases 18 to 20.
  • 5255c3bae6: updates the three register items to the rulings.
  • 86cece3312: re-derives the Hypothesis properties.
  • 088177c9fc: updates the changelog fragment.

Items

  1. Pick-up and merge. I posted the pick-up comment, then merged origin/main with a merge commit; no files conflicted. The files I changed afterwards are rerun below. The last push before mine (22:16Z) was inside the 6-hour window. I went ahead because those were the two commits the item names and tells me to build on.

  2. P1, the kicker cases. I changed nothing. integration.yaml: 9 passed at the head.

  3. The rulings. or_federal_tax_liability_subtraction.py:46-57.

    • The base is income_tax_before_refundable_credits − unreported_payroll_tax, so the net investment income tax stays in (:46-48).
    • The reducing credits are gov.irs.credits.refundable without eitc, plus assigned_aca_ptc (:52-56).
    • refundable_credits.yaml is deleted. Nothing else referenced it: I grepped for both the parameter path and the file path.
    • Its worksheet citations moved into the variable's reference tuple, one href per OR-17 year from 2017 to 2025 (:10-31). Multi-page hrefs carry a # PDF pages comment, and the tuple adds 26 U.S.C. 1411(a)(1).
    • The p binding is kept for the cap.
  4. Tests for the rulings. or_federal_tax_liability_subtraction.yaml: 20 passed (17 before). I computed each value by hand before running it.

    Cases that moved:

    Case Before Now Why
    12 (:137), 2025 1,000 0 The 3,000 additional child tax credit now offsets the 1,000 tax: max(0, 1,000 − 3,000). The 500 EITC still doesn't count.
    14 (:167), 2025 4,000 4,600 The 600 net investment income tax stays in: 5,000 − 400.
    16 (:199), 2020 600 600 The input tax rose from 3,000 to 4,000: 4,000 − 2,400 − 1,000.
    17 (:214), 2026 3,000 1,300 The 1,700 additional child tax credit now counts: 6,000 − 1,000 − 2,000 − 1,700.

    On case 16: under the ruling the old input gives max(0, 3,000 − 2,400 − 1,000) = 0, which would hide whether the credit counts dollar for dollar. I raised the tax so the result stays positive and stated the arithmetic.

    Cases 11, 13 and 15 don't move: their credits are on both the closed list and the federal list. Cases 1 to 10 don't move either.

    New cases:

    • Case 18 (:229), 2023: the refundable payroll tax credit. 3,000 − 500 = 2,500.
    • Case 19 (:243), 2025 joint: two children aged 6 and 9, $20,000 of wages, a $30,000 taxable pension distribution, and qualified_retirement_penalty: 3_000. I pinned aca_ptc: 0. Hand values:
      • AGI 50,000;
      • tax (50,000 − 31,500) × 10% = 1,850;
      • the nonrefundable child tax credit takes 1,850;
      • ACTC = min(4,400 − 1,850, 2 × 1,700, 15% × (20,000 − 2,500) = 2,625) = 2,550;
      • tax before refundable credits 3,000;
      • subtraction 3,000 − 2,550 = 450, against 3,000 under the closed list. The case also asserts the intermediate values.
    • Case 20 (:284), the review's head-of-household witness. 2025, $230,000 of long-term gains, one child aged 10, $200,000 of solar spending, no pinned taxes. Hand values:
      • tax (230,000 − 23,625 − 64,750) × 15% = 21,243.75;
      • nonrefundable credits capped at 21,243.75;
      • NIIT 3.8% × (230,000 − 200,000) = 1,140;
      • cap 8,500 (the first step is at 250,000);
      • subtraction 1,140, as the review computed. It passed on the first run.
  5. P2-2 and P2-3, the body. Rewritten and read back; only the server's footer differs. It covers:

    The impact: and axiom: lines are unchanged.

    Trial merge with Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 at its head e1e57f8dcf. I merged into a scratch worktree, kept this PR's side of every conflict, then removed the worktree. Most conflicts come from Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 being behind main (the CA supplement files, uv.lock, pyproject.toml).

  6. P2-4, the register (source_conflicts/candidates.yaml). All three items now read follows: statute and state: ruled:

    • :7598 claim of right: readings.statute says 316.685(1)(b) leaves out only the EITC. The note says the item was ruled for the statute on 2026-10-20, the formula subtracts the credit once it's in the federal list, and the worksheet's list conflicts with the statute.
    • :7632 NIIT: the statute reading says section 1411 is a Subtitle A tax and 316.685(3)'s closed list excludes chapter 2, not 2A.
    • :7661 unlisted credits: refundable_credits.yaml is dropped from code.

    readings.other is kept as the worksheet's reading. No item says "Code not yet on main", and none names a decision number. test_source_conflicts.py: 103 passed. The diff from 073c5c7b5c is below.

  7. P3-5, the formula comment. :40-45 and :49-51 describe what the code does. The base is federal tax after nonrefundable credits plus other taxes, keeping the NIIT (1411(a)(1); 316.685(3) doesn't exclude it). The unreported payroll tax comes out (316.685(3)(a)). Then every federal refundable credit except the EITC (316.685(1)(b)) and the premium tax credit come off. It names no Schedule 2 line numbers.

  8. P3-6, titles. refundable_credits.yaml is gone. Its worksheet citations are in the variable's tuple, one href per document. The 2020 href opens page 65, and the 2020, 2021 and 2025 page lists are in # PDF pages comments, since variable tuples carry no titles. The hub's item says the code cites "the premium tax credit line". The comment gives the line numbers from the old parameter's comments (7, from 2025 line 8; 2021 line 11), which I couldn't check against the PDFs.

  9. P3-7, the property test (test_or_federal_tax_liability_subtraction_invariants.py). 3 passed: 6.6 s in pytest and 49 s of wall time with model build. Peak RSS was 980 MB, measured locally on this VM. It runs in CI's Python "Rest" shards. The properties:

    • 0 ≤ S ≤ min(max(0, T − U), cap(AGI)), using each unit's own cap;
    • raising the EITC, or the NIIT with T fixed, changes nothing, and outside 2021 neither does cdcc;
    • below the cap, raising assigned_aca_ptc, refundable_ctc, refundable_payroll_tax_credit or unreported_payroll_tax by d (and cdcc in 2021) lowers S by exactly min(d, S). Where the cap binds, S never rises;
    • raising T by d raises S by between 0 and d.
  10. Mutants. Each ran in its own process and the formula was restored after each (git status clean).

Mutant YAML: killing cases Hypothesis
(a) drop the premium tax credit 11, 13, 17 (3 failed) killed: 2021, 2025
(b) drop refundable_ctc 12, 15, 16, 17, 19 (5 failed) killed: 2020, 2021, 2025
(c) subtract the NIIT again 14, 20 (2 failed) killed: 2021, 2025
(d) count the EITC 2, 19 (2 failed) killed: 2021, 2025
(e) restore the closed worksheet list 12, 16, 17, 18, 19 (5 failed) killed: 2020, 2021, 2025

None survived. The 2020 Hypothesis run passes a, c and d because its four derandomized examples happen to sit where the cap binds. The run is live: it kills b and e, and a direct 2020 probe gives the expected 7,050 cap. The YAML cases cover those mutants in every year.

Tests at the head

File Result
or_federal_tax_liability_subtraction.yaml 20 passed
gov/states/or/tax/income/integration.yaml 9 passed
test_or_federal_tax_liability_subtraction_invariants.py 3 passed
test_source_conflicts.py 103 passed

ruff format --check and ruff check are clean on both Python files. I ran no full suites or microsimulations.

CI

Pull request run at 088177c9fc (https://github.com/PolicyEngine/policyengine-us/actions/runs/41741384278) was still in_progress 60 minutes after the push (00:44Z).

  • 35 of 36 jobs had concluded success. They include Quick Feedback, PR validation, Household API Partners, Microsimulation, every Baseline shard (states-shard-3 among them, the shard that failed at 7807e297fd), every Python Rest shard, and 8 of 9 Contrib shards.
  • Contrib tests / Full Suite - Contrib (other-shard-1) was still running.
  • No job had failed or been cancelled.

The hub should read the final conclusion at that URL. I didn't push again.

Judgment calls

  • Sources. I couldn't fetch any source from this VM: law.cornell.edu, oregonlegislature.gov and oregon.gov were refused by the proxy, and so were web.archive.org, uscode.house.gov and govinfo.gov. The body's quotes of 316.685(1)(b), OR-17 page 71, 1411(a)(1) and 36B(a) are the hub's 2026-11-03 text, each 25 words or fewer. I didn't verify them, the OR-17 page numbers, or the worksheet line numbers in the comment.
  • Hard-coded EITC exclusion. I deleted refundable_credits.yaml and wrote the EITC exclusion and the premium tax credit addition in the formula, instead of a one-element parameter. The statute names only the EITC, and a parameter would only restate that.
  • The federal list as the credit set. The formula reads gov.irs.credits.refundable, so a reform that edits that list also changes Oregon's subtraction. Main's income_tax + eitc behaved the same way.
  • Case 16. I raised the input instead of letting the expected value fall to 0 (item 4 above).
  • The 2026 cases. Case 17 is 2026, outside the register's 2017–2025 years. The formula is year-invariant, so the statute reading applies there too.
  • Comments. The YAML header comment quotes OR-17's prose, and none of the new comments matches the register's conflict-phrase patterns: test_source_conflicts.py passes.

Proposed follow-up

  • Oregon federal tax subtraction: test with ordinary inputs across years. Most cases pin income_tax_before_refundable_credits. A small set of ordinary-input households for 2020, 2021 and 2024 would catch upstream changes to the credit split, such as the 2021 refundable_ctc phase-out issue the review noted.

candidates.yaml diff from 073c5c7b5c

diff --git a/source_conflicts/candidates.yaml b/source_conflicts/candidates.yaml
index ec5010d761..6320893080 100644
--- a/source_conflicts/candidates.yaml
+++ b/source_conflicts/candidates.yaml
@@ -7614,19 +7614,21 @@ candidates:
     citation: 2025 OR-17 federal tax subtraction worksheet, PDF pp.71-72 (also 2017, 2018 and 2021)
     url: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=71
   readings:
-    statute: The statute refers to federal income taxes accrued; how it treats the section 1341 credit is unresolved.
+    statute: ORS 316.685(1)(b) leaves out only the earned income credit, so the section 1341 credit reduces the
+      subtraction.
     other: The prose says credits except EITC reduce the subtraction, but the worksheet does not subtract the section
       1341 credit.
   pe_us:
-    follows: unknown
+    follows: statute
     code:
     - policyengine_us/variables/gov/states/or/tax/income/subtractions/or_federal_tax_liability_subtraction.py
     prs:
     - https://github.com/PolicyEngine/policyengine-us/pull/10038
     - https://github.com/PolicyEngine/policyengine-us/pull/10093
   state: ruled
-  note: 'Ruled in October 2026 to follow the worksheet; establish the statutory treatment before determining whether
-    a model change is required. Code not yet on main: or_federal_tax_liability_subtraction.'
+  note: Ruled for the statute on 2026-10-20. The formula subtracts every federal refundable credit except the EITC,
+    so the section 1341 credit reduces the subtraction once it is in gov.irs.credits.refundable. The worksheet's
+    list conflicts with the statute.
 - id: us-or:federal-tax-subtraction-niit
   program: income-tax
   years:
@@ -7643,18 +7645,19 @@ candidates:
     citation: 2025 OR-17 federal tax worksheet, Part A, PDF pp.71-72 (each year 2017-2025)
     url: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=71
   readings:
-    statute: The statute allows federal income taxes accrued and names only the EITC and payroll and self-employment
-      taxes as left out, so it does not settle whether the section 1411 tax counts.
+    statute: 26 U.S.C. 1411(a)(1) imposes the net investment income tax under Subtitle A, and the closed list in ORS
+      316.685(3) excludes only payroll taxes and the self-employment tax (chapter 2), not chapter 2A, so the net
+      investment income tax counts.
     other: The worksheet's other-taxes line does not include the net investment income tax.
   pe_us:
-    follows: gap-fill
+    follows: statute
     code:
     - policyengine_us/variables/gov/states/or/tax/income/subtractions/or_federal_tax_liability_subtraction.py
     prs:
     - https://github.com/PolicyEngine/policyengine-us/pull/10093
-  state: unreviewed
-  note: 'Code not yet on main: the subtraction nets the net investment income tax out of the federal tax, following
-    the worksheet as the administering agency''s reading where the text is open.'
+  state: ruled
+  note: The code follows the statute and keeps the net investment income tax in the federal tax it subtracts; the
+    worksheet's other-taxes line leaves it out.
 - id: us-or:federal-tax-subtraction-unlisted-refundable-credits
   program: income-tax
   years:
@@ -7674,20 +7677,19 @@ candidates:
     citation: 2025 OR-17 federal tax worksheet, Part A, PDF pp.71-72 (each year 2017-2025)
     url: https://www.oregon.gov/dor/forms/FormsPubs/publication-or-17_101-431_2025.pdf#page=71
   readings:
-    statute: The statute allows federal income taxes accrued and leaves out only the EITC; it does not say how other
-      refundable credits count.
+    statute: ORS 316.685(1)(b) leaves out only the earned income credit, so every other refundable credit reduces the
+      subtraction.
     other: The publication's prose says credits other than the EITC reduce the subtraction, but the worksheet subtracts
       only the credits it lists for each year.
   pe_us:
-    follows: gap-fill
+    follows: statute
     code:
     - policyengine_us/variables/gov/states/or/tax/income/subtractions/or_federal_tax_liability_subtraction.py
-    - policyengine_us/parameters/gov/states/or/tax/income/subtractions/federal_tax_liability/refundable_credits.yaml
     prs:
     - https://github.com/PolicyEngine/policyengine-us/pull/10093
-  state: unreviewed
-  note: 'Code not yet on main: refundable_credits.yaml lists only the worksheet''s credits, the same
-    prose-against-worksheet question as us-or:claim-of-right-federal-tax-subtraction.'
+  state: ruled
+  note: The code follows the statute and subtracts every federal refundable credit except the EITC, plus the premium
+    tax credit, including credits the worksheet does not list, such as the additional child tax credit outside 2021.
 - id: us-or:liheap-final-payment-band-bound
   program: liheap
   years:

Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Reviewing head 088177c9fc0d8c12a9b6d6ccc98964d30737aab2 (unchanged since the round 2 hand-off). Findings so far, to be replaced by the full review:

  • CI: the Pull request run at this head (run 41741384278) concluded success, 36 of 36 jobs.
  • P1 (reform path): the formula reads the gov.irs.credits.refundable list, but six structural reforms add a refundable credit by overriding income_tax_refundable_credits instead of the list (Harris LIFT middle-class credit, rent relief credit, Tlaib BOOST and End Child Poverty Act, Romney Family Security Act 2024, refundable credit conversion). Main nets those credits through income_tax; this head doesn't. Witness, 2025 Oregon single filer, $50,000 wages, aca_ptc: 0, under the LIFT reform: credit 1,718.13; subtraction 2,153.37 on main, 3,871.50 here; Oregon tax 3,014.64 on main, 2,879.13 here. Baseline is the same on both.
  • Sources: every host for the statute, the Code and OR-17 refused the connection from this VM, so the review relies on the hub's reading.

Generated by Claude Code

MaxGhenis pushed a commit that referenced this pull request Nov 4, 2026
Once #10093 nets the premium tax credit in Oregon's subtraction, this
household's modeled credit would zero the subtraction. Pinning it keeps the
case isolating the section 1341 credit at 471.50.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: REQUEST_CHANGES
088177c
METHODOLOGY_CHOICE: yes

The head hasn't moved past 088177c9fc. This is a delta review of 7807e297fd..088177c9fc against the round 1 review and the round 2 fix items. Main has moved three commits past this branch's merge base (adea7035ea) to 25c4253483, and the branch still merges with it cleanly (git merge-tree).

The round 1 fixes work at baseline. CI is green, every hand value I computed matches, and the readings PolicyEngine took are coded as stated. One problem blocks the PR. Main reaches refundable credits through income_tax, but this formula reads only the gov.irs.credits.refundable list. So credits that six structural reforms add by overriding income_tax_refundable_credits no longer reduce the subtraction. Main gets those reforms right, and this branch makes them wrong.

P1

1. Refundable credits that reforms add through income_tax_refundable_credits no longer reduce the subtraction

policyengine_us/variables/gov/states/or/tax/income/subtractions/or_federal_tax_liability_subtraction.py:52-56

The formula builds its credit set from the parameter list:

federal_refundable_credits = parameters(period).gov.irs.credits.refundable
reducing_credits = [credit for credit in federal_refundable_credits if credit != "eitc"] + ["assigned_aca_ptc"]

Six structural reforms add a refundable credit by replacing the income_tax_refundable_credits variable (add(..., p.refundable) plus their own credit) and leave the list alone:

  • reforms/harris/lift/middle_class_tax_credit.py:32
  • reforms/harris/rent_relief_act/rent_relief_tax_credit.py:46
  • reforms/congress/tlaib/boost/boost_middle_class_tax_credit.py:28
  • reforms/congress/tlaib/end_child_poverty_act.py:181
  • reforms/congress/romney/family_security_act_2024/ctc/family_security_act_2024_ctc.py:109
  • reforms/refundable_credit_conversion/refundable_credit_conversion.py:102

Main computes max_(0, income_tax + eitc), and income_tax subtracts income_tax_refundable_credits, so main nets these credits. This head doesn't. The PR's own reading (ORS 316.685(1)(b): every federal refundable credit except the EITC reduces the subtraction) says it should. Baseline output is the same on both. Reform scoring of Oregon moves.

Witness: 2025, an Oregon single filer aged 35 with $50,000 of wages and aca_ptc: 0, under the Harris LIFT middle-class credit reform (Simulation(situation=..., reform=middle_class_tax_credit)):

middle_class_tax_credit income_tax Subtraction or_income_tax
main 25c4253483 1,718.13 2,153.37 2,153.37 3,014.64
this head 1,718.13 2,153.37 3,871.50 2,879.13
this head with the fix below 1,718.13 2,153.37 2,153.37 3,014.64

At baseline, all three give a subtraction of 3,871.50 and Oregon tax of 2,879.13.

No test catches this, because every YAML case and every Hypothesis property runs at baseline.

Replace lines 52-56 with the following, which reads the federal total and backs out the EITC:

        # Read income_tax_refundable_credits, not the list, so credits that
        # structural reforms add to that variable count too.
        refundable = parameters(period).gov.irs.credits.refundable
        eitc = tax_unit("eitc", period) if "eitc" in refundable else 0
        refundable_credits = (
            tax_unit("income_tax_refundable_credits", period)
            - eitc
            + tax_unit("assigned_aca_ptc", period)
        )

I applied this on a scratch copy and ran it, then restored the code (git status clean):

  • or_federal_tax_liability_subtraction.yaml: 20 passed;
  • the witness: 2,153.37, matching main.

The "eitc" in refundable guard keeps a reform that drops the EITC from the list from subtracting it twice. A reform that neutralizes eitc (the End Child Poverty Act does) gives 0 either way.

Add one YAML case under a reform so this can't regress. A contrib test with the LIFT credit's in_effect set to true and the household above would do it: subtraction 2,153.37.

The body's sentence "A credit later added to the federal list reduces the subtraction with no Oregon change" holds only for the list. With the fix, it can read: "A refundable credit later added to the federal list, or by a reform to income_tax_refundable_credits, reduces the subtraction with no Oregon change."

P2

2. The PR title states the reading this round reversed

Title: "Reduce Oregon's federal tax subtraction by the OR-17 worksheet's refundable credits only". The code now subtracts every federal refundable credit except the EITC, plus the premium tax credit, and the body and the changelog fragment say so. A squash merge would land this title on main as the commit subject. Replacement:

Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction

P3

3. The Hypothesis properties seldom reach a positive, uncapped subtraction, so the dollar-for-dollar check is mostly 0 = 0

policyengine_us/tests/core/test_or_federal_tax_liability_subtraction_invariants.py:67-70

Property 3's exact check, r[name] == base - min(delta, base), only tests something when 0 < base < cap. I printed uncapped and base for every unit the derandomized run draws (a scratch copy with one print added):

Year Uncapped, base > 0 Uncapped, base = 0 Capped
2020 4 18 2
2021 0 21 3
2025 4 19 1

Hypothesis draws small values first, and with seven credits of up to 3,000 against T from 0, most units floor at zero. That's why mutants (a), (c) and my (f) below pass the 2020 run. The hand-off says the 2020 examples "happen to sit where the cap binds". They don't: they sit at the zero floor.

Replacement (lines 67 and 70):

        "income_tax_before_refundable_credits": draw(st.integers(3_000, 20_000)),
        ...
        **{credit: draw(st.integers(0, 1_000)) for credit in CREDITS},

On a scratch copy with these two lines:

  • the file passes 3/3;
  • uncapped units with a positive base rise to 16 (2020), 17 (2021) and 17 (2025) of 24;
  • mutant (a) now fails all three years, where the file at head misses it in 2020.

4. Case 12's name claims the EITC doesn't reduce the subtraction, but the case can't tell

or_federal_tax_liability_subtraction.yaml:137-151. The expected value is max(0, 1,000 − 3,000) = 0. If the EITC were counted, the result would still be 0, so the case can't show the EITC is left out (mutant (d) is killed by cases 2 and 19, not 12). Set refundable_ctc: 600 and expect 400:

  • here: max(0, 1,000 − 600) = 400;
  • with the EITC counted: max(0, 1,000 − 600 − 500) = 0;
  • under the old closed list: 1,000.

That is a hand computation; I didn't run this edit.

5. The net investment income tax register item doesn't list the source its reading rests on

source_conflicts/candidates.yaml, us-or:federal-tax-subtraction-niit. readings.statute rests on 26 U.S.C. 1411(a)(1), but sources lists only ORS 316.680/316.685 and the OR-17 worksheet. Add:

  - kind: statute
    citation: 26 U.S.C. 1411(a)(1)
    url: https://www.law.cornell.edu/uscode/text/26/1411#a_1

6. Merging with #10038 conflicts in three files, and keeping this side everywhere drops #10038's case

The body says "whichever lands second keeps this formula". That holds for the .py. But git merge-tree 088177c9fc pr-10038 reports content conflicts in all three shared files:

A trial merge with -X ours kept no #10038 case in the YAML. Whoever lands second has to keep both sides of the YAML and merge the register item's two rewrites by hand. Worth one sentence in the body's overlap section.

Mutants

I ran each mutant in its own process on the checkout, restoring the formula after each one (git status clean after each).

Mutant YAML: failing cases Hypothesis file: failing years
(a) drop assigned_aca_ptc (hand-off) 11, 13, 17 (3 failed, 17 passed) 2021, 2025 (2020 passes)
(c) subtract the net investment income tax again (hand-off) 14, 20 (2 failed, 18 passed) 2021, 2025 (2020 passes)
(f) mine: count unreported_payroll_tax again (main's treatment) 14 (1 failed, 19 passed) 2021, 2025 (2020 passes)
(g) mine: also leave out refundable_payroll_tax_credit 18 (1 failed, 19 passed) 2020, 2021, 2025

The results for (a) and (c) match the hand-off's table. None survives the YAML file. A seventh mutant, the reform path in finding 1, survives every test.

Checked and sound

  • CI. The Pull request run at this head (41741384278) concluded success: 36 of 36 jobs success, none skipped or cancelled. This includes Contrib other-shard-1, which the hand-off left running.
  • The defect reproduces at main. I copied this head's YAML into a main worktree (25c4253483): 4 failed, 16 passed.
    • Failing cases: 11 (4,000 vs 2,500), 13 (1,000 vs 0), 14 (5,000 vs 4,600) and 17 (3,300 vs 1,300).
    • These are exactly the premium tax credit and unreported payroll tax cases. Cases 12, 16, 18, 19 and 20 pass on main, so this round's credit and net investment income tax changes restore main's behavior, as the body says.
  • The formula against the readings (:40-57):
    • The base is income_tax_before_refundable_credits − unreported_payroll_tax. income_tax_before_refundable_credits adds income_tax_before_credits, net_investment_income_tax, recapture_of_investment_credit, unreported_payroll_tax and qualified_retirement_penalty, less the capped nonrefundable credits. So the net investment income tax stays in and the unreported payroll tax comes out.
    • The credits are the list without eitc, plus assigned_aca_ptc (= aca_ptc × takes_up_aca_if_eligible).
    • The result is floored at 0, then the AGI cap is applied for all five filing statuses.
  • Excess advance premium tax credit repayment. income_tax_before_refundable_credits carries none. Neither gov/aca nor gov/irs has a variable for advance payments or their repayment (grep for "repayment", "excess…advance" and "advance…premium"). So the model has nothing for worksheet line 2 to remove.
  • gov.irs.credits.refundable by year.
  • The deleted refundable_credits.yaml. No file at the head names federal_tax_liability.refundable_credits or the file path; the only grep hits are the variable, its tests, subtractions.yaml and two tests that set the subtraction as an input. Its ten references, the ORS page and nine OR-17 years, are in the variable's tuple. The 2020 href now opens page 65. Page-range comments sit above the 2020, 2021 and 2025 hrefs, and the comment gives the premium tax credit line as 7, 8 from 2025 and 11 in 2021, as the old parameter did.
  • Hand values, computed before reading each case's arithmetic comment:
    • case 12: max(0, 1,000 − 3,000) = 0;
    • case 14: 5,000 − 400 = 4,600;
    • case 16: 4,000 − 2,400 − 1,000 = 600;
    • case 17: 6,000 − 1,000 − 2,000 − 1,700 = 1,300;
    • case 18: 3,000 − 500 = 2,500;
    • case 19, 2025 joint: AGI 50,000. Tax (50,000 − 31,500) × 10% = 1,850, which the nonrefundable child tax credit uses up. The refundable credit is min(4,400 − 1,850, 2 × 1,700, 15% × 17,500 = 2,625) = 2,550. Tax before refundable credits 1,850 − 1,850 + 3,000 = 3,000; subtraction 3,000 − 2,550 = 450. The hand-off reports that mutant (d) fails this case, which needs a nonzero EITC. I didn't rerun (d);
    • case 20, 2025 head of household: (230,000 − 23,625 − 64,750) × 15% = 21,243.75. The 60,000 clean energy credit caps nonrefundable credits at that amount. NIIT 3.8% × 30,000 = 1,140; the cap at 230,000 is 8,500; subtraction 1,140.
  • Case 16's raised input. At 3,000 the ruling gives max(0, 3,000 − 2,400 − 1,000) = 0, a floor that hides both credits. At 4,000 the result is positive, so the case still tests the recovery rebate credit dollar for dollar, as it did before, and now the additional child tax credit too.
  • The properties hold for the formula as written.
    1. Each unit's own cap and max(0, T − U) bound min(max(0, T − U − C), cap).
    2. The formula never reads net_investment_income_tax beyond T, and outside 2021 it never reads eitc or cdcc.
    3. Below the cap, max(0, x − d) = x − min(d, x). At the cap, the value can only fall.
    4. Raising T by d raises x by d, so the capped, floored value rises by between 0 and d.
  • Test runs at this head, one file per process:
    • or_federal_tax_liability_subtraction.yaml: 20 passed;
    • or/tax/income/integration.yaml: 9 passed (four cases pin aca_ptc: 0, as the body says);
    • test_source_conflicts.py: 103 passed;
    • the Hypothesis file: 3 passed, 17.4 s inside pytest and 76 s of wall time with model build, run locally on this VM. It needs the dev extra for hypothesis.
    • ruff format --check and ruff check are clean on both Python files.
  • The register.
    • All three items read follows: statute and state: ruled.
    • Each note says what the code does, and none names a decision number.
    • The claim-of-right note's "ruled for the statute on 2026-10-20" is a date, not an id.
    • No marker was added, as CONTRIBUTING step 1 requires.
  • Body.
    • The quotes of ORS 316.685(1)(b), 26 U.S.C. 1411(a)(1) and 36B(a), and OR-17's PDF page 71 prose, match the hub's text word for word.
    • "Main computes max(0, income_tax + eitc)" and "already nets out every federal refundable credit … except the EITC" are true of main at baseline.
    • The $751.72 example reproduces at this head: 2024 tax 4,016.00, assigned_aca_ptc 3,264.28, subtraction 751.72; main gives 4,016. In 2025 the same household gives 135.82 here and 3,871.50 on main.
    • The net investment income tax ranges match the thresholds and the cap's zero points.
    • The impact: and axiom: lines are present.
    • The only sentence that misstates the code is the one in finding 1.
  • Overlap with Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038, at its head e1e57f8dcf.
  • Scope. The six changed files include nothing under .github/ or tests/policy/baseline/partners/.
  • Baseline impact. The diff moves baseline outputs for Oregon tax units with assigned_aca_ptc > 0 whose federal tax sits under the cap, and for units with unreported_payroll_tax. Population checks for the hub:
    • Oregon units with a premium tax credit and positive federal tax, which carry the main effect: a lower subtraction and higher Oregon tax;
    • units with unreported payroll tax.
      The credit and net investment income tax rulings restore main's treatment, so they shouldn't move baseline.
  • Against the author's brief. Items 1 to 11 are done as asked. The brief didn't mention the reform path in finding 1 or the title in finding 2.

What I couldn't check

  • Sources. From this VM the proxy refused every host I tried:

    • oregonlegislature.gov and oregon.gov;
    • law.cornell.edu, uscode.house.gov and govinfo.gov;
    • ecfr.gov, irs.gov and web.archive.org.

    So I confirmed no source myself. I relied on the hub's 2026-11-03 and 2026-11-04 reading and quote no law beyond it. In particular I couldn't check the OR-17 page numbers, the worksheet's premium tax credit line for 2017–2020 and 2022–2023, or whether any year's OR-17 has a line the federal list misses.

  • Population impact. I have no microdata here.

  • Rulespec parity. That is the hub's axiom: line.


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

Bring the branch up to main before the round 3 fixes; no conflicts.

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

Six structural reforms add a refundable credit by overriding
income_tax_refundable_credits and leave gov.irs.credits.refundable alone.
Reading only the list dropped those credits from the subtraction, where main
nets them through income_tax. Read income_tax_refundable_credits, back out
eitc when the list carries it, and add assigned_aca_ptc. Baseline values
don't move; a LIFT middle-class credit case pins the reform path.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JQeNTyqxk53oyZpmf
With a 3,000 additional child tax credit against 1,000 of tax, the case
floored at 0 whether or not the EITC counted. A 600 credit leaves 400, which
counting the 500 EITC would drive to 0.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JQeNTyqxk53oyZpmf
With tax from 0 and seven credits up to 3,000, most drawn units floored at
zero, so the dollar-for-dollar property mostly checked 0 = 0. Draw tax from
3,000 to 20,000 and each credit up to 1,000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JQeNTyqxk53oyZpmf
The item's statute reading rests on section 1411(a)(1), which its sources
did not list.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6JQeNTyqxk53oyZpmf
@MaxGhenis MaxGhenis changed the title Reduce Oregon's federal tax subtraction by the OR-17 worksheet's refundable credits only Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction Nov 4, 2026
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
2bffdc0

This is the fix round from the round 2 review (comment 6632521904), built on 088177c9fc. No one pushed to the branch between that head and this round's push.

Commits

  • c33ba33f42 merges main (25c4253483) into the branch with a merge commit. It had no conflicts. I reran every changed file after the merge (below).
  • 63b518fab8: Read the federal refundable credit total in Oregon's federal tax subtraction (P1), and add the LIFT contrib case.
  • bd03571854: Make Oregon subtraction case 12 show the EITC is left out (P3-4).
  • 1386584119: Draw Oregon subtraction properties away from the zero floor (P3-3).
  • 2bffdc0942: Cite 26 U.S.C. 1411(a)(1) in the Oregon NIIT source-conflict item (P3-5).

Items

  1. Pick-up and merge. I posted the pick-up comment, then merged origin/main with a merge commit. No files conflicted.
  2. P1. or_federal_tax_liability_subtraction.py:49-61 now has the review's code. It reads income_tax_refundable_credits, subtracts eitc only when gov.irs.credits.refundable carries it, and adds assigned_aca_ptc. One comment (lines 49-54) now covers what the code reads and why. It merges the old ORS 316.685(1)(b) / Form 8962 comment with the review's two lines.
    • I read income_tax_refundable_credits in this session. It is adds = "gov.irs.credits.refundable", so baseline values don't move.
    • The contrib case is at policyengine_us/tests/policy/contrib/harris/lift/middle_class_tax_credit.yaml:111-138, in the existing file. It uses the file's reform and the in_effect: true input, so it adds no new reform combo. It asserts all four values with absolute_error_margin: 0.01.
    • I left the Economic Dignity for All Agenda child poverty path alone, as the item says.
  3. P2. The title is now exactly "Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction". I read it back from the PATCH response.
  4. P3-3. test_or_federal_tax_liability_subtraction_invariants.py:67 draws from st.integers(3_000, 20_000), and :70 draws each credit from st.integers(0, 1_000).
  5. P3-4. or_federal_tax_liability_subtraction.yaml:137-153: refundable_ctc: 600, expected 400, and the comment now gives the arithmetic. I checked the review's hand values first:
    • 1,000 − 600 = 400;
    • with the EITC counted, max(0, 1,000 − 600 − 500) = 0;
    • under the old closed list, which left out the additional child tax credit, 1,000.
  6. P3-5. source_conflicts/candidates.yaml:7677-7679 adds the 26 U.S.C. 1411(a)(1) statute source under us-or:federal-tax-subtraction-niit.
  7. Body. I made the three replacements word for word and updated "Tests". I also changed one sentence the change made false: the third "What changes" bullet said the code subtracts "every credit in the federal refundable list". It now names income_tax_refundable_credits, which at baseline adds that list, less eitc when the list carries it. The impact: and axiom: lines are unchanged. The readback matches the file I sent, apart from a trailing newline.

Witness at this head

This is the 2025 Oregon single filer aged 35, with $50,000 of wages and aca_ptc: 0, under harris.lift.middle_class_tax_credit (Simulation(situation=..., reform=...)):

middle_class_tax_credit income_tax Subtraction or_income_tax
this head 1,718.13 2,153.37 2,153.37 3,014.64
main 25c4253483 (worktree, run here) 1,718.13 2,153.37 2,153.37 3,014.64

At baseline, both give income_tax 3,871.50, a subtraction of 3,871.50 and or_income_tax 2,879.13.

Tests at this head

I ran each file in its own process, after the merge and the edits:

File Result
or_federal_tax_liability_subtraction.yaml 20 passed
contrib/harris/lift/middle_class_tax_credit.yaml 6 passed (5 existing and the new case)
test_reform_combo_budget.py 1 passed
test_source_conflicts.py 103 passed
test_or_federal_tax_liability_subtraction_invariants.py 3 passed: 12.3 s inside pytest (2020 2.55 s, 2021 0.95 s, 2025 0.74 s calls), 65.7 s of wall time with model build. Run locally on this VM; I had no /usr/bin/time, so I have no peak memory. It needs the dev extra for hypothesis.

ruff format and ruff check are clean on both changed Python files. I didn't rerun integration.yaml, which no change this round touched.

Mutants

I ran each mutant in its own processes and restored the file with git checkout after each. git status was clean after each one.

Mutant Result
P1: restore the head's list-based lines 52-56 Killed by the new contrib case ("Oregon federal tax subtraction nets the reform's refundable credit": subtraction 3,871.50 vs 2,153.37). The contrib file failed 1 and passed 5. The Oregon YAML passed 20 of 20, so only the contrib case kills it.
(a) drop assigned_aca_ptc Oregon YAML: cases 11, 13 and 17 fail (3 failed, 17 passed). Hypothesis file: fails 2020, 2021 and 2025; at 088177c9 it missed 2020.
(d) count the EITC (drop - eitc) Oregon YAML: cases 2, 12 and 19 fail (3 failed, 17 passed). Case 12 now kills it: 0 vs 400.

CI

The Pull request run at this head, 41895282059, was still in_progress at 20:17Z, 62 minutes after the push, so I'm handing off as the brief says:

  • 34 of 36 jobs concluded success, including PR validation, Household API Partners, every Baseline shard, the Python and Microsimulation suites, and Package and compatibility. None failed, and none was skipped or cancelled.
  • Two jobs were still in_progress: Contrib other-shard-1 (job) and Contrib other-shard-3 (job). The new LIFT case runs in one of the contrib shards; it passed locally (6 of 6).

The run's conclusion is still open; the hub should read it before landing.

Judgment calls

  • The contrib case went into the existing LIFT file, not a new one. It shares that file's single reform combo (the same reform and the same in_effect override), so the file's reform budget is unchanged; test_reform_combo_budget.py passes.
  • The case's comment says the subtraction equals income_tax. That holds for this household because it has no EITC, no premium tax credit, no unreported payroll tax and no cap binding. I didn't make it a general claim.
  • I reworded the merge commit's default message to name the PR branch, before pushing.
  • I didn't change the changelog fragment. It already says "every federal refundable credit except the EITC, plus the premium tax credit", which is still true.
  • To support "as on main" in the body, I ran the witness on a main worktree myself rather than relying on the review's figures.

Proposed follow-up

  • Back out only the EITC that income_tax_refundable_credits carries in Oregon's federal tax subtraction. The EDAA child poverty reform's override (edaa_end_child_poverty_act.py:165-185, read in this session) leaves eitc and refundable_ctc out of the total but keeps them in the list and doesn't neutralize eitc. So - eitc adds back an EITC that never reduced federal tax, as main's income_tax + eitc does. The hub tracks this outside the PR.

Generated by Claude Code

@DTrim99

DTrim99 commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction

Summary

The PR changes how or_federal_tax_liability_subtraction builds federal tax. Main uses max(0, income_tax + eitc). The head uses:

income_tax_before_refundable_credits − unreported_payroll_tax − (income_tax_refundable_credits − eitc + assigned_aca_ptc)

floored at zero, then capped as before. At baseline, two things change:

  • the premium tax credit now reduces the subtraction;
  • the employee Social Security and Medicare tax on unreported tips and wages (Forms 4137 and 8919) no longer counts.

The EITC, the additional child tax credit, the other listed refundable credits and the net investment income tax are treated exactly as on main. The new contrib case confirms that credits a reform adds through income_tax_refundable_credits still reduce the subtraction.

I read the law before the code:

  • Statute. ORS 316.680(1)(b) allows "the amount of any federal income taxes accrued by the taxpayer during the taxable year as described in ORS 316.685". ORS 316.685(1)(b) says the taxpayer "shall not take into account any amount of the earned income credit provided under section 32 of the Internal Revenue Code that reduced the amount of the taxpayer's federal income tax liability for the tax year". ORS 316.685(3) excludes only "(a) Taxes, contributions or other payments paid by employees in pursuance of federal laws relating to Social Security, railroad retirement, unemployment compensation or old age benefits" and "(b) Taxes paid pursuant to the Self-Employment Contribution Act, subtitle A, chapter 2". The cap is ORS 316.695(3)(b)-(f), unchanged here.
  • Publication OR-17, every year 2017-2025. I read each year's Part A worksheet. The prose says "Federal income tax credits, except for the EITC, reduce your federal tax subtraction". It exempts only the credits for federal tax on special fuels, special oils and a regulated investment company. The worksheet's credit lines are:
Year Credits the worksheet subtracts PDF page
2017-2019 American Opportunity credit, premium tax credit (Form 8962, line 24) 63 / 68 / 63
2020 the same, plus "2020 federal economic stimulus payments you received in 2020 or early 2021 plus your recovery rebate credit" (line 8) 66
2021 the refundable CTC (Schedule 8812, line 14e, with the nonrefundable CTC added back on line 2), American Opportunity credit, 2021 stimulus payments plus the recovery rebate credit, premium tax credit, CDCC (lines 8-12) 72-73
2022-2024 American Opportunity credit, premium tax credit 73 / 73 / 71
2025 American Opportunity credit, refundable adoption credit, premium tax credit (lines 6-8) 71-72

In every year the other-taxes line covers the IRA/retirement-plan tax, recapture taxes and Schedule 2 line 17 income-tax items. It leaves out the net investment income tax (Schedule 2, line 12) and the Social Security and Medicare tax on unreported tips and wages (lines 5-7). Line 2 takes out the excess advance premium tax credit repayment.

How the head compares:

  • Matches the worksheet: the premium tax credit; the American Opportunity credit; the 2020-2021 recovery rebate credit (rrc_cares/rrc_caa/rrc_arpa model the full entitlement, which is what the worksheet's "payments received plus credit" adds up to); the 2021 CDCC and refundable CTC; and dropping unreported payroll tax.
  • Departs from the worksheet, following the statute: the additional child tax credit outside 2021, the refundable payroll tax credit, and keeping the net investment income tax. These are main's existing treatment. The statutory reading is well supported:
    • the EITC-only carve-out in 316.685(1)(b) presupposes that other refundable credits reduce "federal income tax liability", and OR-17's own prose agrees;
    • 316.685(3)(b) had to name a Subtitle A chapter (SECA) to exclude it, while 26 U.S.C. 1411(a)(1) imposes the NIIT in Subtitle A, chapter 2A, which (3) does not list.
  • Recorded: the PR records both departures in source_conflicts/candidates.yaml as follows: statute, which is CONTRIBUTING.md's step 1.

There are no critical findings. Two items need changes: the axiom: line is not a valid form, and the new "eitc" in refundable guard is unpinned. CI is 38/38 green at the head, and the branch is level with main.

Critical

None.

Should address

1. The axiom: line is not a valid form, and no rulespec-us issue covers this change.

  • What it says. The PR body reads axiom: (hub files pe-parity before landing). That is none of the CONTRIBUTING.md#axiom-parity forms (encoded-correct, <PR> encoded, <issue> queued, n/a: <reason>). Kansas Limit Kansas itemized deductions to the four K.S.A. 79-32,120(a) categories at each year's shares #10095, Vermont Phase out Vermont's child tax credit per child #10644 and New York Choose New York's itemizing election by State plus City tax and require federal itemizing only before 2018 #10100 drew the same finding for the same line.
  • Search. I searched TheAxiomFoundation/rulespec-us issues and PRs for "Oregon federal tax", "316.680", "316.685", "federal tax liability subtraction", "federal income tax subtraction", "premium tax credit Oregon", "OR-17", "or_federal_tax_liability_subtraction" and "10093". Nothing covers it.
  • Why encoded-correct doesn't fit. The repo's only Oregon income-tax module, us-or/policies/income_tax/resident_core_liability_pipeline.yaml (2026 only), takes or_pit_2026_accrued_federal_income_tax_excluding_eitc_and_self_employment_tax as an input. It doesn't compose that amount from federal tax, credits and other taxes, so the rule this PR changes isn't encoded. n/a doesn't fit a policy change.
  • Fix. File a dispatch-ready, pe-parity-labelled rulespec-us issue and change the line to axiom: TheAxiomFoundation/rulespec-us#<n> queued. The issue needs:
    • the module path (for example us-or/statutes/316/685.yaml);
    • the corpus citations (ORS 316.680(1)(b), 316.685(1)(b) and (3), 26 U.S.C. 1411(a)(1) and 36B(a));
    • the verbatim law;
    • the required outputs: accrued federal income tax net of non-EITC credits and the premium tax credit, excluding employee payroll tax and SECA tax;
    • companion tests from the same external source as the YAML, for example the OR-17 worksheet line arithmetic.

2. The new "eitc" in refundable guard is unpinned (or_federal_tax_liability_subtraction.py:61).

  • The guard. eitc = tax_unit("eitc", period) if "eitc" in refundable else 0 backs out the EITC only when gov.irs.credits.refundable carries it.
  • Why no test reaches the else branch. Every baseline value of that list (2013, 2021 and 2022 entries) contains eitc. Only two reforms replace the list, the FISC Act and AWRA, and both keep eitc. No YAML or Python test overrides the list, so every test runs the if branch.
  • The mutant. Deleting the guard, so the line always reads tax_unit("eitc", period), fails no test. That is the case the guard exists for: a list without eitc would then have the EITC subtracted from a total that never held it, raising the subtraction by the EITC.
  • Fix. Add one case to or_federal_tax_liability_subtraction.yaml. List overrides in YAML input already work, for example gov.states.de.tax.income.deductions.itemized.sources in the Delaware tests:
- name: Case 21, 2025 a refundable credit list without the EITC backs out no EITC.
  period: 2025
  input:
    gov.irs.credits.refundable:
      [refundable_american_opportunity_credit, refundable_ctc, recovery_rebate_credit, refundable_payroll_tax_credit]
    state_code: OR
    filing_status: SINGLE
    adjusted_gross_income: 40_000
    income_tax_before_refundable_credits: 3_000
    eitc: 1_000
    assigned_aca_ptc: 0
  output:
    # The EITC is not in income_tax_refundable_credits, so nothing is backed
    # out: 3,000 - 0 = 3,000. Without the guard: 3,000 - (0 - 1,000) = 4,000.
    or_federal_tax_liability_subtraction: 3_000

This adds one distinct parameter override to the Oregon tree, so the cost is one more cached system copy in that shard.

Suggestions

  1. CI cost note for the new Python test. CLAUDE.md asks for the affected CI group, elapsed time and peak memory for new simulation-building Python tests. The hand-off comment gives local times and says peak memory wasn't available, but it doesn't name the CI group. The head's CI run (41895282059) fills that in:

    • the file ran in the Rest Python core group (shard 2, chunk core-1);
    • its three cases took 0.436 s, 0.428 s and 0.437 s;
    • the chunk peaked at 9.3 GiB RSS (9,755,968 KB) over 25:57 of wall time.

    One sentence with these figures in the PR body would close the item. The cost is clearly small.

  2. Changelog wording. The fragment says the change will "Reduce Oregon's federal tax liability subtraction by every federal refundable credit except the EITC, plus the premium tax credit". Main already nets every listed refundable credit except the EITC, so a reader would think more changed than did. Suggested text, matching the title: "Net the premium tax credit out of Oregon's federal tax liability subtraction, and stop counting the employee payroll tax on unreported tips and wages."

  3. Pre-existing, for a follow-up: the 2017-2020 caps. The cap schedules start on 2021-01-01, so 2017-2020 use the 2021 amounts ($7,050; $3,525 separate). OR-17 gives $6,550 for 2017, $6,650 for 2018, $6,800 for 2019 and $6,950 for 2020, with half for separate filers. The PR cites the 2017-2020 worksheets and its Hypothesis test runs 2020, but it doesn't touch the caps, so this isn't a finding against it. Separately, the five cap files' statute href is ors316.html #316.800, with a stray space and the wrong section. The cap is ORS 316.695(3).

  4. Register years. The three us-or: candidate items stop at 2025, but the formula applies the same readings in 2026. Consider extending years.to once the 2026 OR-17 is published.

  5. Follow-up already noted by the author. The EDAA child-poverty override keeps eitc in the list but drops it from income_tax_refundable_credits, so - eitc adds back an EITC that never reduced federal tax. Main has the same overstatement through income_tax + eitc, so this PR doesn't regress it. Tracking it in an issue would keep it from getting lost.

Verified correct

  • Formula against the law.
    • income_tax_before_refundable_credits = income_tax_before_credits (regular tax, capital gains tax, AMT) + NIIT + recapture_of_investment_credit + unreported_payroll_tax + qualified_retirement_penalty − capped nonrefundable credits. Nonrefundable credits are capped at income_tax_before_credits, so they can't absorb the NIIT, as on Form 1040 line 22.
    • Subtracting unreported_payroll_tax follows ORS 316.685(3)(a).
    • Neither SECA tax nor the Additional Medicare Tax enters the base.
    • assigned_aca_ptc (aca_ptc × takes_up_aca_if_eligible) is the allowed credit the worksheet takes from Form 8962, line 24. PolicyEngine models no advance payments, so there is no repayment for worksheet line 2 to remove.
    • PolicyEngine has no federal adoption credit, so the 2025 worksheet's line 7 has no counterpart.
  • Vectorization, entity and period. All reads are TaxUnit/YEAR. The in test is on a scalar list parameter. The select over filing status is unchanged. No new state-gated int or enum key, so the NH Add New Hampshire state supplementation for 2026 #10156 pattern doesn't apply. The new reads are all upstream of income_tax, which main already read, so there's no new cycle.
  • Every new term is pinned except the guard above.
    • The PTC term: cases 11, 13 and 17.
    • The unreported payroll tax: case 14.
    • The EITC back-out: cases 2, 12 and 19.
    • Reading the total rather than the list: the new LIFT contrib case.
  • References. Each OR-17 year is its own document with one href. I opened every anchor and each shows the premium tax credit line: 2017 p.63, 2018 p.68, 2019 p.63, 2020 p.65-66, 2021 p.72-73, 2022 p.73, 2023 p.73, 2024 p.71, 2025 p.71-72. The multi-page hrefs carry # PDF pages comments, as CLAUDE.md requires. The YAML comment's quote of 2025 OR-17 p.71 is verbatim.
  • Source-conflict register. The claim-of-right, NIIT and unlisted-credit items each have at least two sources, readings, pe_us.follows: statute, code, PRs and state: ruled, with YAML cases for each reading (NIIT: 14 and 20; unlisted credits: 12, 16, 17, 18 and 19). There's no premature marker. test_source_conflicts.py passed in CI on a tree level with main.
  • Changelog. changelog.d/or-federal-tax-subtraction-worksheet.fixed.md is top-level, with type fixed.
  • Tests. Cases go in the variable-named YAML and the existing LIFT reform file. The LIFT case uses the reforms: key and the file's existing in_effect override, so it adds no reform combo. Hypothesis invariant files in tests/core are established practice; this is one of about 150 there.
  • Partner tests. No file under tests/policy/baseline/partners/ changed, and Household API Partners passed.

Hand checks (head formula; caps from the schedules):

  1. Case 1, 2021 single, AGI $80,000: min(10,000 − 0, 7,050) = 7,050.
  2. Case 2: refundable credits = 1,000 (EITC) − 1,000 = 0, so min(7,000, 7,050) = 7,000. Same as main's 6,000 + 1,000.
  3. Case 4, 2021 separate: 50% × 7,050 = 3,525.
  4. Case 9, 2026 joint, AGI $255,000: the $250,000-$260,000 step is 7,000, so min(30,000, 7,000) = 7,000.
  5. Case 12, 2025 joint: income_tax_refundable_credits = 500 + 600 = 1,100; − 500 EITC + 0 PTC = 600; 1,000 − 600 = 400. Counting the EITC gives 0.
  6. Case 13: max(0, 2,000 − 1,000 − 1,500) = 0.
  7. Case 14: 5,000 − 400 = 4,600. The $600 NIIT inside the $5,000 is not removed.
  8. Case 15, 2021 joint: 9,000 − 3,000 − 2,800 − 1,000 (the CDCC is on the 2021 list) = 2,200.
  9. Case 16, 2020 joint: 4,000 − 2,400 − 1,000 = 600.
  10. Case 17, 2026 head of household: 6,000 − 1,000 − 2,000 − 1,700 = 1,300, under 8,750.
  11. Case 18, 2023 single: 3,000 − 500 = 2,500, under 7,800.
  12. Case 19, 2025 joint:
    • AGI 20,000 + 30,000 = 50,000;
    • tax (50,000 − 31,500) × 10% = 1,850;
    • CTC 2 × 2,200 = 4,400; the nonrefundable part is 1,850;
    • ACTC = min(2,550, 2 × 1,700, 15% × (20,000 − 2,500) = 2,625) = 2,550;
    • tax before refundable credits 1,850 − 1,850 + 3,000 = 3,000;
    • subtraction 3,000 − 2,550 = 450, under 8,500.
  13. Case 20, 2025 head of household:
    • tax (230,000 − 23,625 − 64,750) × 15% = 21,243.75;
    • AMT: AMTI 230,000 − 88,100 = 141,900, tentative tax 11,572.50, so none;
    • nonrefundable credits: CTC 2,200 − 1,500 (5% × 30,000 over $200,000) = 700, plus the §25D credit 30% × 200,000 = 60,000; the total is capped at 21,243.75;
    • NIIT 3.8% × 30,000 = 1,140;
    • no ACTC (no earned income);
    • the cap at $230,000 uses the joint thresholds (316.695(3)(e)), giving 8,500;
    • subtraction 1,140.
  14. LIFT contrib case, 2025 single, $50,000 wages:
    • tax (50,000 − 15,750) gives 1,192.50 + 12% × 22,325 = 3,871.50;
    • income_tax 3,871.50 − 1,718.13 = 2,153.37;
    • subtraction 3,871.50 − (1,718.13 − 0 + 0) = 2,153.37, which matches main's income_tax + eitc.

A filer with refundable credits, against each year's worksheet:

  • 2021 (case 15). Worksheet lines 1 + 2 = 9,000. Lines 8, 10 and 12 total 3,000 + 2,800 + 1,000. Line 14 = 2,200, exactly the head's value.
  • 2024 (the PR body's example). Single, $50,000: federal tax (50,000 − 14,600) gives 1,160 + 12% × 23,800 = 4,016. The worksheet's line 7 takes off the $3,264.28 premium tax credit, giving 751.72, the head's value. Main gave 4,016. I hand-checked the tax; the credit amount is PolicyEngine's default assignment, which the hand-off reproduced at this head.
  • 2020 (case 16) and 2025 (case 19). These show the recorded departure. The worksheet doesn't list the ACTC, so it gives 4,000 − 2,400 = 1,600 for 2020 and 3,000 for 2025. The head gives 600 and 450, following ORS 316.685(1)(b) and OR-17's own prose. Main gives the same 600 and 450.

Axiom line assessment

Invalid. axiom: (hub files pe-parity before landing) is not a CONTRIBUTING.md form. No rulespec-us issue or PR covers ORS 316.685's composition of accrued federal tax, and the 2026 Oregon module takes that amount as an input. See Should address 1.

CI status

All 38 checks pass at the head (gh pr checks 10093, re-checked just before writing). Pull request run 41895282059 concluded success, including the two Contrib shards that were still running at the hand-off. The new LIFT case runs in one of them.

Branch status

Verdict: REQUEST_CHANGES

DTrim99
DTrim99 previously requested changes Nov 4, 2026

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes: 0 critical, 2 should (the axiom line isn't a valid form; the new "eitc" in refundable guard is unpinned). The statutory reading of ORS 316.685 and the recorded departures from OR-17 are well supported. Full report in the comment above.

@MaxGhenis

MaxGhenis commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Complete. The final review is #10093 (comment) (VERDICT: APPROVE at 2bffdc09421a380c1a7c444a7ec619eabf8c27e6).


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
2bffdc0
METHODOLOGY_CHOICE: yes

This is a delta review of 088177c9fc..2bffdc0942 against the round 2 review and the round 3 fix items. The head hasn't moved past 2bffdc0942. The branch carries:

  • a clean merge of main 25c4253483 (c33ba33f42; git show --remerge-diff c33ba33f42 is empty);
  • four fix commits: 63b518fab8, bd03571854, 1386584119 and 2bffdc0942.

The round 2 P1 is fixed:

  • The formula reads the federal refundable credit total.
  • The LIFT witness matches main.
  • A new contrib case is the only test that kills the round 2 code.

CI is green: the Pull request run concluded success, with 36 of 36 jobs passing. I found no P1 or P2. Two P3s remain, both about the body's #10038 overlap section, plus one follow-up. The follow-up is identical on main, so it doesn't block this PR.

METHODOLOGY_CHOICE stays yes. The PR follows ORS 316.685 over the OR-17 worksheet on the net investment income tax and on the refundable credits the worksheet doesn't list. Those choices are recorded as us-or:federal-tax-subtraction-niit and us-or:federal-tax-subtraction-unlisted-refundable-credits.

P3

1. "Overlap with #10038" describes #10038's old head; its current head already pins aca_ptc: 0

PR body, "Overlap with #10038", the paragraph "A trial merge of #10038's head (e1e57f8dcf) …" and its three bullets.

#10038's head is now 6906894550. Its commit b695dcd5e1 ("Pin aca_ptc to 0 in Oregon's section 1341 witness") added aca_ptc: 0 under tax_unit: in its Oregon case, with a comment saying why. So the first bullet ("computes 0 instead of 471.50") no longer describes #10038.

The conditional sentence that follows ("adds aca_ptc: 0 … if it lacks the pin") stays true, but a reader would expect a fix-up that is no longer needed.

What I ran:

Replace the trial-merge paragraph, its three bullets and the final sentence of the section with:

A trial merge of #10038's head (6906894550) into this branch, keeping this PR's formula and both PRs' YAML cases, passes all 21 cases. #10038's case already pins aca_ptc: 0 (its commit b695dcd5e1), so its expected 471.50 holds under this formula with no edit: 2,671.50 − 2,200 = 471.50.

2. The .py conflict also covers #10038's reference tuple, which "keeps this PR's formula" doesn't settle

Same section, the sentence "Whoever lands second keeps this PR's formula in or_federal_tax_liability_subtraction.py …".

#10038's side of the conflict also changes the reference tuple:

  • it relabels the ORS 316 page comment;
  • it adds https://www.oregonlegislature.gov/bills_laws/Archive/2023ors316.pdf#page=52 with the comment # ORS 316.680(1)(b) and 316.685(1), PDF pages 52, 54;
  • it adds a two-line comment above max_(0, federal_itax + federal_eitc).

That last comment goes with main's formula and should go. Whether the archived PDF stays is the merger's call, but the body should say there is one to make. Suggested addition after that sentence:

In the same file, #10038 also adds the archived 2023 ORS chapter 316 PDF to the reference tuple; whoever lands second decides whether to keep it beside this PR's ORS link.

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

Back out only the EITC that income_tax_refundable_credits carries in Oregon's federal tax subtraction. The Economic Dignity for All Agenda child poverty reform (edaa_end_child_poverty_act.py:165-185) builds income_tax_refundable_credits from the list minus eitc and refundable_ctc. It doesn't neutralize eitc, and the list still carries it. So the guard at or_federal_tax_liability_subtraction.py:56 subtracts an EITC that never entered the total. Main's max_(0, income_tax + eitc) adds it back the same way.

Witness: Simulation(situation=..., reform=(end_child_poverty_act, in_effect=true from 2026)), 2026, Oregon, aca_ptc: 0. Head and main give identical values in every row:

Household eitc income_tax Subtraction or_income_tax
Joint, two children (5, 8), $40,000 wages 5,454.30 −620.00 4,834.30 121.13
Joint, two children, $50,000 3,348.30 880.00 4,228.30 1,343.99
Joint, two children, $60,000 1,242.30 2,440.00 3,682.30 2,561.61

In each row, the subtraction equals income_tax + eitc, under the cap. One fix keys the back-out on the total rather than the list. The other six overrides use add(tax_unit, period, p.refundable), plus their own credit, plus flat_refundable_credit for the conversion reform. Each of those carries eitc exactly when the list does, so they are unaffected.

Mutants

I ran each mutant on its own scratch worktree at the head, with one process per test file. Afterwards I removed the worktrees; git status is clean in every checkout.

Mutant or_federal_tax_liability_subtraction.yaml LIFT contrib file Hypothesis file
P1: restore 088177c9fc's list-based lines (code identical to that head, comment aside) 20 passed 1 failed, 5 passed: "Oregon federal tax subtraction nets the reform's refundable credit", 3,871.50 vs 2,153.37 3 passed
(a) drop assigned_aca_ptc cases 11 (4,000 vs 2,500), 13 (1,000 vs 0), 17 (3,300 vs 1,300): 3 failed, 17 passed 6 passed 2020, 2021, 2025 fail
(d) drop - eitc (count the EITC) cases 2 (6,000 vs 7,000), 12 (0 vs 400), 19 (0 vs 450): 3 failed, 17 passed 6 passed 2020, 2021, 2025 fail
(m) mine: drop the zero floor (or_federal_income_tax = federal_tax - refundable_credits) case 13 (−500 vs 0): 1 failed, 19 passed 6 passed 2020, 2021, 2025 fail

These match the hand-off's results for P1, (a) and (d):

  • Only the new contrib case kills P1.
  • Case 12 now kills (d).
  • The new Hypothesis ranges kill (a) in all three years.

No YAML case or property runs under a reform except the LIFT case. So the P1 mutant survives everything else, as expected.

Checked and sound

  • CI. The Pull request run at this head (41895282059) concluded success. All 36 jobs are completed/success, none skipped or cancelled, including:

    • Contrib other-shard-1 and other-shard-3, which the hand-off left running;
    • Household API Partners;
    • every Baseline and Python shard;
    • PR validation.

    contrib/harris runs as one batch (Makefile:194), so the new LIFT case ran in CI.

  • The formula (or_federal_tax_liability_subtraction.py:49-61) matches the round 2 replacement exactly.

    • income_tax_refundable_credits is adds = "gov.irs.credits.refundable" at baseline. The list carries eitc in every period (2013, 2021 and 2022 values).
    • So for every path where the list carries eitc, the floored amount is main's minus unreported_payroll_tax and assigned_aca_ptc. Algebraically, main's income_tax + eitc = income_tax_before_refundable_credits − income_tax_refundable_credits + eitc. That covers baseline and six of the seven reforms that override the total.
    • No reform overrides income_tax itself (grep of policyengine_us/reforms).
    • The abolish-federal-income-tax flag zeroes income_tax_before_refundable_credits as well as income_tax. So the head's subtraction is 0 there, where main's is the EITC.
    • The one comment above the code (lines 49-54) says what the code reads: the federal total, less the EITC when the list carries it, plus the premium tax credit. It says why: credits that structural reforms add to the total count too. Both statements are true of the code.
  • The LIFT witness, run with Simulation(situation=..., reform=(middle_class_tax_credit, in_effect=true)):

    middle_class_tax_credit income_tax Subtraction or_income_tax
    head 2bffdc0942 1,718.13 2,153.37 2,153.37 3,014.64
    main 25c4253483 1,718.13 2,153.37 2,153.37 3,014.64

    At baseline both give 3,871.50 / 3,871.50 / 2,879.13. I computed the first three values by hand before reading the case:

    • 2025 parameters read from the model: LIFT cap 3,700, single phase-out start 36,850, width 24,550.
    • Credit: 3,700 − 3,700 × (50,000 − 36,850) / 24,550 = 3,700 − 1,981.87 = 1,718.13.
    • Federal tax: (50,000 − 15,750) = 34,250 of taxable income; 1,192.50 + 12% × 22,325 = 3,871.50; less the credit, 2,153.37.
    • The Oregon single cap at 50,000 of AGI is 8,500 (read from the parameter), so it doesn't bind.

    or_income_tax comes from the runs only. The contrib case at middle_class_tax_credit.yaml:111-138 asserts all four values, with absolute_error_margin: 0.01, the file's reform and in_effect: true.

  • Case 12 (or_federal_tax_liability_subtraction.yaml:137-153), computed by hand:

    • 1,000 − 600 = 400;
    • with the EITC counted, max(0, 1,000 − 600 − 500) = 0;
    • with the additional child tax credit left out, 1,000.

    The case asserts 400, and its comment gives the same arithmetic.

  • The Hypothesis ranges. T is drawn from st.integers(3_000, 20_000) (:67) and each of seven credits from st.integers(0, 1_000) (:70).

    • Up to 7,000 of credits plus 1,000 of unreported payroll tax can still reach the zero floor.
    • AGI from 0 to 200,000 still reaches the cap.
    • So both boundaries stay reachable while most units sit between them. The kills of (a) in 2020 and of (m) in every year show the dollar-for-dollar and bound checks now bite.
    • Surviving-spouse filers aren't drawn, as in round 2; the YAML cases cover that status's cap through the shared select.
  • The NIIT source. source_conflicts/candidates.yaml:7677-7679 lists kind: statute, 26 U.S.C. 1411(a)(1), https://www.law.cornell.edu/uscode/text/26/1411#a_1 under us-or:federal-tax-subtraction-niit, as the review gave it.

  • Test runs at this head, one file per process:

    File Result
    or_federal_tax_liability_subtraction.yaml 20 passed
    contrib/harris/lift/middle_class_tax_credit.yaml 6 passed
    or/tax/income/integration.yaml 9 passed
    test_reform_combo_budget.py 1 passed
    test_source_conflicts.py 103 passed
    test_or_federal_tax_liability_subtraction_invariants.py 3 passed: 9.7 s in pytest, 46 s of wall time with model build, on a 4 vCPU VM; peak memory not measured
  • Readers. or_federal_tax_liability_subtraction is read only through gov/states/or/tax/income/subtractions/subtractions.yaml (into Oregon taxable income and or_income_tax). The PR changes no parameter on net, and income_tax_refundable_credits is read, not changed.

  • Title. It is exactly "Net the premium tax credit and leave out unreported payroll tax in Oregon's federal tax subtraction".

  • Body.

    • The three replacements the brief gave are present word for word.
    • The third "What changes" bullet now names income_tax_refundable_credits, "which at baseline adds the gov.irs.credits.refundable list", less eitc when the list carries it, plus assigned_aca_ptc. That is what the code does.
    • "Tests" lists 20 cases, the new case 12 arithmetic, the LIFT case with its four values and "6 of 6", and the new Hypothesis ranges. Each matches my runs.
    • The impact: and axiom: lines are present and unchanged.
    • The only stale text is the Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 trial-merge passage (P3 1).
  • Baseline impact. Baseline moves only through the premium tax credit and the unreported payroll tax, as in round 2. The credit total equals the list at baseline, so this round's change is neutral there. Population checks for the hub are unchanged from round 2: Oregon units with a premium tax credit and positive federal tax under the cap, and units with unreported payroll tax.

  • Scope and overlap.

  • Against the author's brief. Items 1 to 8 are done as asked, and the EDAA path was left alone as item 2 said. The hand-off reports CI as still in progress; it has since concluded success.

What I couldn't check

  • Sources. From this VM, the proxy refused every source host (HTTP 403 on CONNECT): oregonlegislature.gov, oregon.gov, law.cornell.edu, uscode.house.gov, govinfo.gov and web.archive.org. So I confirmed no statute or OR-17 text myself, and I quote none. In particular, I couldn't check whether OR-17's premium tax credit line takes Form 8962 line 24 or the net credit on line 26 when advance payments exist. The model has no advance-payment input on main; Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 adds net_premium_tax_credit as an input.
  • Population impact. I have no microdata here.
  • Rulespec parity. That is the hub's axiom: line.

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 "eitc" in refundable guard had no test on its else branch: every
baseline refundable credit list carries the EITC, so deleting the guard
failed nothing. Case 21 overrides the list without the EITC; with the
guard removed it returns 4,000 instead of 3,000.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LwQq6GcUUKCP2fg997bbmc
Main already nets every listed refundable credit except the EITC, so the
fragment now names just the two changes: the premium tax credit and the
unreported payroll tax.

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

MaxGhenis commented Nov 5, 2026 •

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: ebc373c6e01e6968533b8727e7e8b6c6b15469e1 (branch or-federal-tax-subtraction-worksheet, built on 2bffdc09421a380c1a7c444a7ec619eabf8c27e6; no other commits landed in between).

Commits

  • 11d9ac7d54 Pin the EITC guard in Oregon's federal tax subtraction (Case 21).
  • ebc373c6e0 Say only what changed in the Oregon subtraction changelog.

No merge of main: the branch was 2 commits behind, git merge-tree against origin/main was clean, and no check needed it.

Items

  1. Pick-up. Posted. Branch name and head 2bffdc0942 confirmed before starting. The last push was at 18:49Z, under 6 hours before pick-up (00:30Z). That push was the hub's previous round, which handed off at 20:17Z, and it is the head this item names, so I went ahead.

  2. The "eitc" in refundable guard (review item 2). or_federal_tax_liability_subtraction.yaml:317-331 adds Case 21 with the reviewer's inputs and output, under the agreed name and comment.

    • At the head, the file passes 21 of 21.
    • Mutant. I replaced or_federal_tax_liability_subtraction.py:56 with eitc = tax_unit("eitc", period). Case 21 fails with [4000.] differs from 3000.0. The other 20 cases pass, so the result is 1 failed and 20 passed. Code restored, with an empty git diff on the .py.
    • The list override takes. If the override were ignored, income_tax_refundable_credits would include the $1,000 EITC. The mutant would then return 3,000 − (1,000 − 1,000) = 3,000 and pass. It returns 4,000, so the override works.
  3. Changelog (review suggestion 2). changelog.d/or-federal-tax-subtraction-worksheet.fixed.md:1 now reads "Net the premium tax credit out of Oregon's federal tax liability subtraction, and stop counting the employee payroll tax on unreported tips and wages." towncrier build --draft --version 0.0.0 renders it under "### Fixed".

  4. Body. I PATCHed the body and read it back; it matches what I sent byte for byte.

    • "Tests": the counts now read 21 cases, all 21 pass, and the Case 21 line follows Case 20.
    • The invariants bullet has the CI sentence (review suggestion 1). The job logs at the final head show per-test times but no peak memory or wall time. Those are in the rest-python-*-resources.txt artifact, and the proxy blocks this session's download of it. So the sentence keeps the figures from run 41895282059 and says "at 2bffdc0942".
    • Placement changed at the final head. In run 41944400959 the shard planner put the file in shard 4, chunk core-2 (32 files, 263 passed in 20:40), not shard 2 core-1. Its cases took 0.46, 0.45 and 0.45 s. The body sentence is dated to 2bffdc0942, so it stays true.
    • Sentence made false. The Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 overlap paragraph said "all 21 cases pass" for the merge of Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038's head, which predates Case 21. It now reads "merged into this branch at 2bffdc0942, before Case 21 was added". I did not redo the cross-PR merge. With Case 21 the merged file would have 22 cases.
    • The impact: and axiom: lines are untouched.
  5. Runs, each in its own process, at the head:

    • or_federal_tax_liability_subtraction.yaml: 21 passed.
    • contrib/harris/lift/middle_class_tax_credit.yaml: 6 passed.
    • test_or_federal_tax_liability_subtraction_invariants.py: 3 passed.
    • gov/states/or/tax/income/integration.yaml: 9 passed.
    • test_source_conflicts.py: 103 passed.
    • ruff format --check on the changed .py and the invariants test: already formatted.
  6. CI. Pull request run 41944400959 at ebc373c6e0 concluded success, and all 36 jobs concluded success. Those are:

    • PR validation;
    • Quick Feedback;
    • Household API Partners;
    • Package and compatibility;
    • Plan Rest Python shards, Rest Python shards 1-9, and Check Rest Python shards;
    • Microsimulation;
    • Baseline: states-shard-1 to 4, irs, reform, ssa-usda, contrib-hhs, household, rest-a and rest-b;
    • Contrib: states-shard-1 to 4, other-shard-1, other-shard-2a, other-shard-2b, other-shard-3 and congress.

    The sentence is now at the end of "Tests".

  7. The review. I did not reply to it or dismiss it.

Hand computation (Case 21)

The list override has no eitc, so income_tax_refundable_credits = 0. No children or education inputs are given, so I'd expect the other listed credits to be 0. That is not checked separately; the 3,000 result agrees with it.

  • The code backs out no EITC: 3,000 − (0 − 0 + 0) = 3,000, under the 2025 single cap at $40,000 AGI.
  • Without the guard: 3,000 − (0 − 1,000) = 4,000, which is what the mutant returned.

Judgment calls

Follow-ups

None beyond the reviewer's suggestions 3 to 5, which the hub is routing.


Generated by Claude Code

@MaxGhenis
MaxGhenis dismissed DTrim99’s stale review November 5, 2026 15:47

DTrim99's report (issue comment 6643503950) gets these answers. Should 1: rulespec-us#2027 queues Oregon's federal tax subtraction, and the axiom line names it. Should 2: Case 21 pins the eitc guard, and the mutant that drops it returns 4,000 against the expected 3,000. Suggestions 1 and 2: ebc373c6e0 adds the CI cost sentence and the fragment wording. Suggestions 3 and 4 and the EDAA follow-up go to later PRs: the 2017-2020 caps and EDAA apply to main as well, and the register's years wait for the 2026 OR-17. The independent review's third round approved 2bffdc0942, and the commit since then adds that case and text.

MaxGhenis added a commit that referenced this pull request Nov 7, 2026
…41) and North Carolina's rules that depend on it (#10038)

* Add North Carolina's claim of right repayment itemized deduction

G.S. 105-153.5(a)(2)d, added by S.L. 2016-5 for 2014 and later, lets North
Carolina itemizers deduct repayments of income included in AGI in an earlier
year under a claim of right (Form D-400 Schedule A line 8). Repayments above
$3,000 are deductible in full; smaller ones are reduced by 2% of federal AGI
less other miscellaneous itemized deductions, which the 2016 and 2025 NCDOR
worksheets both reproduce. PE had no input for these repayments, so it adds
claim_of_right_repayment (person level; only the head's and spouse's count)
and moves North Carolina's uncapped itemized items into a parameter list.

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

* From 2026 limit North Carolina's claim of right deduction to income NC taxed

Review r1 (subfleet 20261009-090558-nc-itemized-review):
- S.L. 2026-31 section 1.8 rewrites G.S. 105-153.5(a)(2)d for taxable years
  beginning on or after January 1, 2026: the repaid amount must have been
  included in AGI "as modified in G.S. 105-153.5 and G.S. 105-153.6" in the
  earlier year. New person input
  nc_claim_of_right_repayment_excluded_from_income (the part NC deducted when
  received, such as repaid Social Security) and a dated parameter apply it.
- Count only the head's and spouse's other miscellaneous expenses against
  the pre-2018 floor; a dependent's are on the dependent's return.
- The property grid now varies property taxes (so the $20,000 cap binds) and
  adds a 2026 grid for the excluded part.

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

* Model the section 1341 claim of right deduction and credit; wire North Carolina's 1341(a)(5) bar and G.S. 105-266.2 payment (WIP)

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

* Keep the federal claim of right deduction and credit out of state rules that exclude them (AZ, KS, WI, MO, OR, VA); property tests

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

* Document the unmodeled pre-2018 small repayment deduction; changelog fragment

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

* Fix the federal method in the North Carolina worksheet property test to keep it at two seconds

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

* Remove a stray empty log file

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

* Address review: isolate the method branches, compare to the cent, return-level prior-year inputs, California and Montana flows

- The branches that fix the claim of right method start from inputs only, and
  the itemization branches settle the method in the parent first, so a tax
  liability calculated before the method is known cannot leak into them.
- Ties are compared to the cent, so a one-cent difference picks the credit.
- The prior-year federal decrease and NC increase are return-level inputs
  defined on chapter 1 and Article 4 tax.
- California leaves out the federal deduction (Schedule CA Part II line 16);
  Montana deducts it through 2023 (Form 2 itemized line 18).
- Section 67(g) is now 67(h); document the Arizona and NC coverage limits.

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

* Keep the simulation's claim of right method in the itemizing branches

Review r2 found that with itemization branching off, tax_liability_if_not_itemizing
used the parent's method when calculated first but chose its own after
income_tax. The itemizing branches now take the method as an input. A branch
keeps its parent's cache when an override equals the value the parent already
holds, so ordinary simulations still share it. Adds the review's Texas
household as an order test under both itemization settings.

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

* Merge main into North Carolina claim of right deduction

* Merge PR 10028's branch (with main) into the section 1341 branch

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

* Restore conservative branch invalidation; fix the method in itemizing branches only when someone qualifies

Review r3 showed that keeping a parent's cache when an override equals the
parent's current value can reuse tax calculated before another input
changed. Branches again drop inherited values whenever the parent knows an
overridden variable. The itemizing branches add the claim of right method as
an override only when some tax unit repays more than $3,000, so simulations
without a qualifying repayment keep sharing the parent's cache. Adds the
review's stale-parent sequence as a test.

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

* Merge main into the section 1341 branch

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

* Test both NC contrib credits stacked on the claim of right payment with use tax

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

* Merge main into the section 1341 branch

Resolves the add/add conflicts with #10028's squash (557b539) by
keeping this branch's 26 U.S.C. 1341(a)(5) bar on the North Carolina
deduction, its added NC cases and the method fixture in the NC property
grid, and keeps both sides' Arizona itemized deduction cases.

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

* Carry the claim of right deduction into Montana itemized deductions from 2024

From 2024 Montana Form 2 line 2 takes the federal itemized deductions
(2024 instructions, Worksheet A line 1: Form 1040 line 12), which include
Schedule A line 16. The miscellaneous list was empty from 2024, so a
Montana itemizer lost the federal claim of right deduction. Review r5b
(P3-1) found it.

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

* Merge main into the section 1341 branch

Resolves the period_branch.py conflict with #9913 (5c51e0d) by taking
main's _input_keys docstring, which no longer says whether Core's clone
copies or shares the input-key set.

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

* Give every itemizing branch the simulation's claim of right method

From 2026 tax_unit_itemizes opens the itemizing branch for the section 68
limitation (#10136) with only tax_unit_itemizes overridden. From
policyengine-core 3.33 (#560), set_input on a branch drops the values stored
after the overridden variable was first stored anywhere in the simulation,
so that branch lost the inherited section 1341 method and chose its own as a
filer forced to itemize. Its itemized deductions then decided itemization on
a liability tie, or always with itemization branching off: CI failed two 2026
grid points, and a 2026 household with branching off came out at $4,988
instead of $4,100.

get_itemization_branch builds the itemizing and non-itemizing branches for
all three callers, adding the method as an input whenever someone qualifies,
so they share one branch and none chooses a method. A 2026 test covers that
household in every calculation order under both itemization settings; it
fails before this change.

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

* Drop #10028's merged changelog fragment; date period_branch's set_input note

main compiled the North Carolina claim of right fragment into CHANGELOG.md
when #10028 merged, so the copy left on this branch would print it again.
The period_branch module docstring said set_input on a branch clears
nothing, which policyengine-core 3.33 changed (review r8).

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

* Clarify federal claim of right input documentation

* Keep claim of right threshold references specific to its value

* Explain Wisconsin claim of right worksheet interpretation

* Carry the federal claim of right deduction into California itemized deductions

* Require section 1341 eligibility for North Carolina repayment relief

* Merge remote-tracking branch 'gh/main' into federal-claim-of-right-1341

* Align NC refundable claim-of-right accounting with state tax invariants

* Apply statute-first claim-of-right treatment in AL MO OR WI and VA

* Complete Montana repayment deductions and federal taxable income base

* Preserve Alabama federal deduction credit treatment before 2021

* Document Alabama credit-list verification boundary

* Correct entities in Montana repayment regression fixtures

* Remove duplicate household inputs from Montana fixtures

* Merge remote-tracking branch 'gh/main' into federal-claim-of-right-1341

* Fix Montana state-tax adjustment before taxable-income floor

* Correct tax-unit inputs in Montana property test

* Merge main into federal-claim-of-right-1341

Merge upstream main at 86d3ae6.

Keep California's federal section 1341 deduction carryover and $11,000
regression alongside main's mortgage insurance premium exclusion cases.

Keep Wisconsin's repayment in the itemized deduction credit base under
Wis. Stat. 71.07(5)(a)6-7, while excluding federally deductible mortgage
insurance premiums as introduced by upstream commit 7545b2c (#10003).
Preserve both sets of references and regression cases without changing
either parent's expected values.

* Merge main into federal-claim-of-right-1341

Preserve the 2020 above-the-line charitable deduction and 2021
non-itemizer treatment while retaining section 1341 method-aware
itemization branches. Keep the single Schedule A parameter block
with the claim-of-right deduction.

Add six hand-calculated 2020/2021 regressions combining charitable
deductions and both section 1341 methods, including comparisons
with itemization branching disabled.

* Merge current main into federal-claim-of-right-1341

* Merge main into federal-claim-of-right-1341

Conflicts resolved:
- Wisconsin itemized deduction credit: keep main's explicit Schedule 1
  component list (#10311) and add claim_of_right_deduction to it, so the
  section 1341 repayment stays in the credit base as this PR intends
  under Wis. Stat. 71.07(5)(a)6-7.
- Alabama and Wisconsin YAML tests: keep both sides' cases.

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

* Montana: add the state-income-tax addback after the line 3 floor from 2025

The 2024 Form 2 Worksheet A reduces the federal deduction by the capped
state-income-tax addback before line 3's zero floor. The 2025 form stops
adjusting line 2 and reports the addback on Schedule I line 4, a Montana
addition after the floor, as MCA 15-30-2120(1) treats the (2)(j) item
alongside the other additions. A new boolean parameter is true in 2024 and
false from 2025 (2026 follows the 2025 form by convention), and both
mt_taxable_income_joint and mt_federal_deductions read it.

Adds 2025 and 2026 YAML witnesses (the review's AGI 10,000 / itemized
20,000 household gives 4,250 in 2025), keeps the 2024 pre-floor witnesses,
limits the property test to 2024, and points the misc_deductions comment at
mt_taxable_income_joint, which is what reads the federal base.

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

* Alabama: apply the regulation's payment-credit list from 2015

Ala. Admin. Code r. 810-3-15-.20(1)(d)2 subtracts all credits shown as
payments on the federal return, and its current text dates from a 2010
amendment, so it governs every year the parameter covers, not only 2021 on.
The legacy pre-2021 list (total AOC, no section 1341 credit or net PTC) is
dropped, and the 2020 case that pinned it now expects
5,000 - 1,000 - 1,500 - 1,000 - 750 = 750.

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

* Update the four source-conflict candidates this PR resolves

main's backlog still described the code before this PR: AL, MO and WI as
following the worksheets and OR as unknown. The head follows the statute or
regulation in all four, so set pe_us.follows to statute and state to ruled,
replace the notes that said a model change was still needed, start AL's
years in 2015 (the corrected credit list now applies from the parameter's
first year), and cite ORS 316.685(1)(b), the EITC-only disregard the OR
code follows.

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

* Replace two dead Montana references

The 2017 Montana Form 2 with Legal References now 404s at its original
address; cite the Wayback Machine copy of 2024-01-07, whose PDF p. 6
Schedule III lines 25, 26 and 28 the reviewer confirmed. Drop the "2023 HB
109" reference: its URL mixes HB0199 and HB0109, returns 404, and what it
was meant to support could not be confirmed; MCA 15-30-2120 is already
cited for the deduction.

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

* Rebuild the Missouri and Oregon section 1341 witnesses from a repayment

Both cases set claim_of_right_credit as an input that neither formula reads,
so they only checked that income_tax passes through. They now use the
federal integration case 4 household (2025, $40,000 wages, $10,000
repayment, $2,200 prior-year decrease), where the credit method wins and
flows through income_tax: 2,671.50 - 2,200 = 471.50. Missouri deducts
25% x 471.50 = 117.875 and Oregon subtracts 471.50. Adding the credit back
in either formula fails its case.

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

* Run the section 1341 property grid in 2020 too

main's #10186 made the 2020 itemization choice compare two branches with
different AGI (the 2020 above-the-line charitable deduction), and no
invariant in this file ran on that path. Add 2020 to YEARS (last, so the
tests that read YEARS[0] and YEARS[1] keep 2025 and 2026) and run the
calculation-order test in 2020 as well. Every existing assertion is kept;
all 58 tests pass. Locally the file went from 182 s to 220 s and its peak
RSS from 3.2 GB to 3.5 GB.

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

* Changelog: state the Montana and Alabama scope for every filer

The claim-of-right fragment described the Montana work as repayment
treatment, but from 2024 every Montana filer's taxable income now starts
from the federal base (all six mt_taxable_income_joint cases changed), and
every Alabama AOC claimant now subtracts only the refundable part.

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

* Merge remote-tracking branch 'origin/main' into federal-claim-of-right-1341

* Pin Montana's QBI add-back before the zero floor with a reachable 2026 case

The old 2025 case supplied a positive QBI deduction at a binding floor, which
the model can't produce before 2026. From 2026 the 199A(i) $400 minimum can
exceed taxable income, so a derived household now pins the order: MCA
15-30-2101(22) and 15-30-2120(2)(i) add the deduction back to federal
taxable income before Form 2's line 7 floor, giving 0, not 400.

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

* Pin aca_ptc to 0 in Oregon's section 1341 witness

Once #10093 nets the premium tax credit in Oregon's subtraction, this
household's modeled credit would zero the subtraction. Pinning it keeps the
case isolating the section 1341 credit at 471.50.

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

* Drop the unreachable 2025 branches from the Montana property test

YEARS is (2024,), so the Schedule 1-A draw and the year >= 2025 branches
never ran; YAML cases cover 2025 and 2026.

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

* Document mt_federal_deductions as display-only

mt_taxable_income_joint computes its own deduction term from
taxable_income_deductions, which also holds the QBI deduction and the
Schedule 1-A deductions, so the two differ (e.g. 15,750 against 21,750 in
the 2025 tip case) and the formula can't read this variable.

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

* Start the Alabama credit-list item at 2018, the first worksheet compared

The code applies the regulation's list from 2015, but worksheets before 2018
went unchecked, so the item can't claim a conflict for 2015-2017.

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

* Explain Montana's QBI add-back order from Form 2, not section 63

The comment argued from federal taxable income having no floor, which
would also remove the line 3 floor the code keeps for Montana's other
additions. The order follows the form: line 2 leaves the QBI deduction
out, and line 3 floors line 1 minus line 2 at zero.

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

* Merge main

Main removed gov.household.household_refundable_credits and
gov.household.household_tax_before_refundable_credits, which no formula
read after it dropped the abolish-tax structural reforms. This branch had
added North Carolina to both. The merge keeps the removal; this branch's
North Carolina entries stay in gov.household.household_refundable_state_credits
and the gov.states.household lists.

Main's new North Carolina use tax case under the payroll tax repeal enters
nc_income_tax, which this branch's state list no longer reads: the list now
counts North Carolina tax before refundable credits. The case now enters
nc_income_tax_before_refundable_credits, and its totals stay at 1,020.

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

* Merge main into federal-claim-of-right-1341

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

* Merge main into federal-claim-of-right-1341

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

* Merge main into federal-claim-of-right-1341

#10094 also adds an entry to
source_conflicts/candidates.yaml. Git merges the two cleanly, and the
merged register keeps unique ids.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits November 8, 2026 13:45
Resolve the overlap with the section 1341 change (#10038):
- or_federal_tax_liability_subtraction.py keeps this branch's formula. The
  federal refundable credit list includes claim_of_right_credit, so the
  formula's income_tax_refundable_credits already subtracts section 1341
  relief. Add main's 2023 ORS reference and a comment saying so.
- The YAML test keeps this branch's cases and adds main's section 1341 case
  (income tax 471.50, subtraction 471.50), which passes unchanged.
- candidates.yaml takes main's statute reading for
  us-or:claim-of-right-federal-tax-subtraction, rewrites its note to describe
  the merged code, and keeps this branch's two new Oregon entries.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- or_federal_tax_liability_subtraction.py: the comment now says the federal
  refundable list carries the section 1341(a)(5) credit, while relief under
  1341(a)(4), an itemized deduction, already lowers
  income_tax_before_refundable_credits.
- Case 21's override list keeps claim_of_right_credit, so the list stays
  the 2025 list without the EITC.
- candidates.yaml: the us-or:claim-of-right-federal-tax-subtraction note
  dates the statute ruling and names the credit.

No formula or expected value changes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis merged commit 1140c07 into main Nov 8, 2026
38 checks passed
@MaxGhenis
MaxGhenis deleted the or-federal-tax-subtraction-worksheet branch November 8, 2026 21:29
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Merging at 7a0bf46dd8 as an admin squash with --match-head-commit, past the required-review gate.

  • Checks: gh pr checks passes all 38 checks at this head, including pull request run 42514224872 (36 of 36 jobs). GitHub reports the PR MERGEABLE, and no change request stands: we dismissed DTrim99's review 5776647473 on 2026-11-05 with a mapping of each point to its fix.
  • Review r3 approved the formula and tests at 2bffdc0942 (comment 6644163301). The two commits after it change a test and the changelog: 11d9ac7d54 adds Case 21, which pins the EITC guard, and ebc373c6e0 rewords the changelog.
  • An independent review of the main merge approved cc9d7f989d. Its differential check covered 960 Oregon tax units with section 1341 repayments under both methods, in 2020, 2025 and 2026. With no premium tax credit or unreported payroll tax, the merged subtraction equals main's to the cent.
  • An independent delta review approved 7a0bf46dd8. No formula or expected value moved from cc9d7f989d, and Case 21 fails with the guard removed. These pass on a merge with current main, 92caead63e:
    • the Oregon subtraction file (22 of 22) and Oregon's integration.yaml (9 of 9);
    • the Harris LIFT case file (6 of 6) and refundable_ctc.yaml (26 of 26);
    • the invariants test (3 of 3) and the 2021 child tax credit split test (7 of 7);
    • the source-conflict guard (103 of 103).

The body gives the 2025 impact; the 2026 estimate follows.

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