Skip to content

Add state tax comparison outputs and caller documentation - #10310

Open
MaxGhenis wants to merge 7 commits into
mainfrom
outside-modeler-comparison-outputs-docs
Open

MaxGhenis wants to merge 7 commits into
mainfrom
outside-modeler-comparison-outputs-docs

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

WIP: Add state-tax comparison outputs and clarify caller input conventions identified in the Yale/PSL triage.

Plan:

  • Add state_income_tax_rebate_adjustment and state_income_tax_before_one_time_rebates with an additive reconciliation contract. The registry covers 17 states, follows existing dated credit lists and amount gates, respects Virginia/South Carolina liability caps, and sums person-level Massachusetts/Montana amounts. Ordinary credits, filing elections, and other credit bases remain fixed.
  • Document tax-unit membership, adult spouse inference, and explicit dependent/role inputs in all three requested variables; add U1's five regression cases.
  • Add the optional person-level ca_out_of_state_municipal_bond_interest input (default 0), document the California-source assumption, and exclude dependents' own interest from the parent's return. Federal AGI is unchanged.
  • Document automatic Alaska PFD/energy-relief calculation, person-level overrides, and the separate 2022 taxable and energy components.

Validation: 67 cases passed across 7 files (57 new and 10 existing regression cases). Affected YAML files run individually in foreground processes, one at a time, with all requested thread caps set to four. make format, lint, and the GitHub documentation build pass. CI test jobs were skipped for this draft PR. Microsimulation impact validation is left to the hub.

Impact: none expected for existing inputs, since existing output behavior is preserved and the new California input defaults to 0. Supplying the new input explicitly adds the reported nondependent portion to California AGI. The comparison output preserves current rebate attribution and historical eligibility behavior; it does not rerun taxes under a counterfactual policy.

axiom: TheAxiomFoundation/rulespec-us#1860 queued. California R&TC 17143 (other states' bond interest added to California AGI): the statutes are in the pinned corpus release, the Schedule CA form text waits for the W6 re-pin, and the module waits for the hub's signed-encoder dispatch. n/a: the state income tax rebate adjustments, the pre-rebate comparison output, and the Alaska PFD and tax-unit role documentation change no calculated tax or benefit.

@MaxGhenis
MaxGhenis marked this pull request as ready for review October 21, 2026 00:16
@MaxGhenis MaxGhenis changed the title WIP: Add state tax comparison outputs and caller documentation Add state tax comparison outputs and caller documentation Oct 21, 2026
@DTrim99

DTrim99 commented Oct 22, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Add state tax comparison outputs and caller documentation

PR #10310 (author: MaxGhenis), head bda3538997. This is a first review. The PR has no comments or reviews yet, and it touches no partner test file.

What the PR adds:

  • Two comparison outputs (TaxUnit).
    • state_income_tax_rebate_adjustment sums a new 17-state registry, gov.states.household.state_income_tax_rebate_adjustments.
    • state_income_tax_before_one_time_rebates = state_income_tax + that adjustment.
    • Refundable rebates (AZ, CO, CT, DE, GA, HI, ID, IL, IN, MA, ME, NM, NY, RI): each state's variable adds only the registered rebates that are in that year's refundable-credit list.
    • Montana adds mt_income_tax_rebate.
    • Virginia and South Carolina: the variable restores the rebate's effect on final tax, max(T − C − S, 0) − max(T − C − R − S, 0).
  • One optional California input. ca_out_of_state_municipal_bond_interest is a Person input that defaults to 0. It enters ca_additions through tax_unit_non_dep_add, so dependents' amounts are left out.
  • Documentation only.
    • Docstrings on is_tax_unit_dependent, tax_unit_dependents, tax_unit_count_dependents, tax_exempt_interest_income and both Alaska PFD variables.
    • Two new pages, docs/validation/state-rebates.md and docs/validation/alaska-pfd.md, and a new section in docs/validation/taxsim.md.
  • Tests and changelog. 57 new YAML cases in four variable-named files, and four top-level changelog fragments.

Who consumes it. The PR body says the outputs come from the Yale/PSL outside-modeler triage. The docs link them to the TAXSIM dashboard's "Within 1%, net of rebates" column. That column's PolicyEngine srebate comes from policyengine-taxsim (state_output_resolver.py, ONE_TIME_REBATE_VARIABLES), which uses a twin simulation and a different list (S2).

Sources read for this review (all fetched 2026-10-21):

Summary

What is right:

  • The outputs are pure additions.
    • state_income_tax, the credit totals and household_net_income are unchanged.
    • The three existing California additions are TaxUnit variables, which tax_unit_non_dep_add passes to add unchanged. So ca_additions changes only when the new input is supplied.
  • The arithmetic follows the canonical credit paths.
    • Every registered refundable state computes xx_income_tax = before − xx_refundable_credits, with adds of its dated list.
    • va_rebate and sc_2022_rebate are last in their ordered nonrefundable lists.
    • Montana's person-level rebate is summed the same way mt_refundable_credits enters state_refundable_credits.
  • There is no double counting. The adjustment restores exactly what state_income_tax subtracted: in MA, the 62F base stays fixed; in MT, dependents' rebates are zero by formula.
  • The documentation is accurate on the points I checked. These include the Alaska split, the IRS footnote, Schedule 1 line 8g, Notice 2023-56 § 3.04, FTB Pub. 1001 and the spouse-inference behavior.
  • Tests:
    • CI ran all four new files green. Each state is covered through the aggregate, including out-of-year and mixed-state cases.
    • I hand-checked 18 expectations.
    • There are no new Python tests and no parameter overrides, so the CI-cost rule is met.

There are no critical findings and three should-address items:

  • S1. Invalid Axiom line. axiom: pending: hub files parity is not an allowed form, as on the batch siblings. The comparison outputs and documentation are n/a. The California input, however, encodes R&TC § 17143 and needs a queued pe-parity issue: rulespec-us has no module for it.
  • S2. The registry's scope is inconsistent.
    • It includes Colorado's recurring residual TABOR sales refund but leaves out Oregon's recurring kicker.
    • It differs from the policyengine-taxsim rebate list behind the dashboard column that the new taxsim.md sentence points to. That list includes or_kicker and leaves out co_sales_tax_refund, MA premium pay and the ME affordability payment.
  • S3. A new 2022 Colorado case pins household_net_income = 73,535.80.

There are also four suggestions: Virginia wording after #10277, an optional bound on the California input, a Colorado year inconsistency in the docs, and the PR body.

Status:

Critical (must fix)

None.

Should address

S1. The Axiom line axiom: pending: hub files parity is not a valid form, and the California input needs a queued issue

  • Where: the last line of the PR body.
  • The rule (CLAUDE.md, "Axiom Parity"): the line must be encoded-correct, <rulespec PR> encoded, <rulespec issue> queued or n/a: <reason>. We flagged the same pending form on the batch siblings (Fix the 2021 CTC split and surviving-spouse phaseout threshold #10306, Fix pre-2018 AMT child exemption and income base #10308, Fix Arkansas tax indexing, Kentucky 2021 CDCC, and WV 529 coordination #10324, Follow the statute for the NYC school tax rate reduction, IN supplemental homestead, VA 529 and WA senior/disabled property tax exemption #10330 and Fix Montana disability exclusion repeal, elderly credit receipt timing and occupancy tests #10331).
  • What is n/a:
    • The two comparison outputs and the 17 state adjustment variables change no calculated tax or benefit.
    • The Alaska and tax-unit role edits are documentation.
  • What is policy: the California input. It adds a statutory adjustment to California AGI.
    • The statute. R&TC § 17143: "Sections 103 and 141 to 150, inclusive, of the Internal Revenue Code, relating to interest on governmental obligations, shall not apply."
    • The FTB guidance. FTB Pub. 1001 (2025), file page 8: "California taxes the interest from non-California state and local bonds. Enter the interest from non-California state or local bonds on Schedule CA (540), Part I ... Section A, line 2, column C." The 2023 edition (file page 7) is the same.
    • rulespec-us has no module. main at cea8379e0b has no us-ca/statutes/rtc/17133 or 17143 module and no Schedule CA addition. The two us-ca/policies/income_tax pipelines are source holds.
  • Severity: should. An invalid Axiom line is a merge blocker, not a model error.
  • Fix: change the line to axiom: TheAxiomFoundation/rulespec-us#<ca> queued; n/a: the rebate comparison outputs and the Alaska and tax-unit role documentation change no calculated tax or benefit. <ca> must be an open, pe-parity-labelled, dispatch-ready issue modelled on Add AMT parameters for 2020-2023 #1467:
    • Target: for example us-ca/statutes/rtc/17143.yaml, with the § 17133 exemption for California obligations.
    • Corpus: § 17143 verbatim (quoted above) and FTB Pub. 1001's Schedule CA line 2, column C instruction.
    • Required outputs: the line 2, column C addition and its entry into California AGI.
    • Companion cases from the statute and Pub. 1001:
      • federal AGI 100,000 with 12,500 of other-state bond interest → CA AGI 112,500;
      • 50,000 of tax-exempt interest, all California-source → no addition;
      • a joint return with 5,000 and 7,500 of other-state bond interest → an addition of 12,500.
    • Blocker: if the encoder is blocked, name the blocker in the issue.

S2. The registry's inclusion rule is inconsistent across states and with the TAXSIM convention the docs point to

  • Where:
    • policyengine_us/parameters/gov/states/household/state_income_tax_rebate_adjustments.yaml:11-28;
    • docs/validation/state-rebates.md:48-51 ("Despite the comparison output's name, its scope includes Colorado's residual TABOR sales tax refund, which is a recurring statutory mechanism ...");
    • docs/validation/taxsim.md:204: "See state rebate attribution and the pre-rebate comparison output for the model's reconciliation contract."
  • Inconsistency across states.
    • Colorado is in. co_sales_tax_refund is a recurring refundable credit claimed on the return, and the registry includes it.
    • Oregon is out. or_kicker is also a recurring surplus refund claimed on the return. It is in Oregon's refundable list for 2019+, at a nonzero rate in 2019 (17.171%), 2021 (17.341%), 2023 (44.28% of prior-year tax) and 2025 (9.863%).
    • The stated rationale doesn't separate them. The docs say these payments are in scope "because these payments currently reduce modeled state income tax through refundable-credit aggregation". That is equally true of the Oregon kicker.
  • Inconsistency with the consumer the docs name.
    • Where the dashboard's figure comes from. The dashboard computes "net of rebates" in match_flags as pe["siitax"] + pe["srebate"]. policyengine-taxsim computes PolicyEngine's srebate from ONE_TIME_REBATE_VARIABLES with a rebate-free twin simulation.
    • What that list contains. It includes or_kicker and co_tabor_cash_back but not co_sales_tax_refund, ma_covid_19_essential_employee_premium_pay_program or me_affordability_payment. Its comment explains the kicker: "PE credits it on the return year, while taxsimtest builds ... report it in srebate for the base year".
    • Where the two measures differ. state_income_tax_before_one_time_rebates therefore differs from the dashboard's PolicyEngine siitax + srebate in:
      • Colorado in every year with a sales refund;
      • Massachusetts in 2015–2021 (premium pay);
      • Maine in 2025;
      • Oregon in 2019, 2021, 2023 and 2025.
    • New York matches on the net measure, because policyengine-taxsim zeroes the inflation refund in both paths.
    • The new taxsim.md sentence presents this output as the reconciliation contract for that column, which a reader will take as the same measure.
  • Severity: should. The documentation is misleading, and a definition that differs by state makes a cross-state comparison output unreliable.
  • Fix:
    • One rule. State a single inclusion rule in the registry description, for example "payments a comparator reports outside the return-year state tax".
    • Oregon. Either add or_income_tax_rebate_adjustment for or_kicker, or document why Oregon is out while Colorado's recurring refund is in.
    • Reconcile the two lists. Either have policyengine-taxsim read this registry, or list the state-by-state differences in taxsim.md and state-rebates.md instead of calling this output the dashboard's contract.
    • The name. Consider a name without "one-time" (for example state_income_tax_before_rebates), since the scope already includes recurring and non-rebate payments.

S3. The new 2022 Colorado case pins a household net income that includes a FAMLI premium not in effect until 2023

  • Where: policyengine_us/tests/policy/baseline/gov/states/tax/income/state_income_tax_before_one_time_rebates.yaml:67-82, Case 5, household_net_income: 73535.80.
  • The arithmetic (single filer, $100,000 of wages, 2022):
    • Federal tax. 1,027.50 + 3,780 + 0.22 × 45,275 = 14,768.
    • FICA. 7,650.
    • Colorado tax. 0.044 × 87,050 − 234 = 3,596.20.
    • Net income. 100,000 − 14,768 − 7,650 − 3,596.20 = 73,985.80.
    • The difference. The expected 73,535.80 is $450 lower: co_employee_famli_contribution = 0.0045 × 100,000. policyengine_us/parameters/gov/states/co/tax/payroll/famli/employee_rate.yaml:3 starts at 2023-01-01: 0.0045 and extrapolates back to 2022.
  • The law. C.R.S. § 8-13.3-507: "Beginning on January 1, 2023, for each employee, an employer shall remit to the fund ... premiums". There was no FAMLI premium in 2022.
  • A pending fix breaks this case. Open PR Start CO FAMLI, Paid Leave Oregon and WA Cares at their program dates, and add 2019–2024 Washington PFML premiums #10154 ("Start CO FAMLI ... at their program dates") adds 0000-01-01: 0 to that file. Whichever merges second, this case then fails at 73,985.80.
  • The comment doesn't explain the value. It lists wages, taxable income, state tax and the refund, so the expectation can't be reproduced from it.
  • Severity: should. A new test asserts a value that contradicts the statute. It is not critical, because the FAMLI backdating predates this PR and this PR claims no Colorado payroll result.
  • Fix:

Suggestions

G1. Virginia: the wording predates #10277

  • What Limit the Virginia rebate to tax after the refundable EITC #10277 changed. On main, va_rebate is already limited to "the tax liability after every deduction, subtraction and credit under Chapter 3", including the refundable EITC: min(amount, max(T − C − S − refundable, 0)). CI's merged tree included it.
  • What that means for this PR. For a calculated rebate, va_income_tax_rebate_adjustment now equals va_rebate. The max − max form matters only when va_rebate is supplied as an input, as in all four Virginia cases.
  • The docs are now incomplete. docs/validation/state-rebates.md:64-67 says the "face rebate can exceed its effect on final tax" and lists only other nonrefundable credits, the spouse adjustment and the zero floor.
  • Fix (optional):

G2. California: an optional bound on the new input

  • Where: ca_out_of_state_municipal_bond_interest.py.
  • What it does now. The documentation says the input is "a subset" of tax_exempt_interest_income, but nothing enforces that. A value above the person's tax-exempt interest is added to California AGI in full.
  • The trade-off. A min_ against tax_exempt_interest_income would protect against inconsistent inputs. It would also silently drop the addition for callers who supply only the California input.
  • Fix (optional): pick one behavior and say so in the docstring.

G3. Colorado years are inconsistent within state-rebates.md

  • The two statements.
    • The table (line 85) gives a possible contribution in "2015–2025".
    • The limits section (lines 119-120) says the refund "remains active in the modeled 2016–2020 period".
  • What the model does. in_effect starts 2021-01-01: true and extrapolates back to 2015.
  • Fix (optional): use one range, or explain why 2015 is treated differently.

G4. PR body

  • Stale status. It still starts with "WIP:" and says "CI test jobs were skipped for this draft PR". The PR is ready for review, and the full suite ran green at 00:16 UTC on 2026-10-21.
  • Fix (optional): update both, and fix the Axiom line (S1).

Verified correct

  • Hand-checks (18 expectations).
    • state_income_tax_before_one_time_rebates.yaml:
      1. CO 2021: 1,000 − (100 + 56 + 750) = 94; adjustment 56 + 750 = 806; pre-rebate 900.
      2. CO negative: 0 − 906 = −906; −906 + 806 = −100.
      3. VA 2021 (inputs va_rebate 250, refundable 10):
        • Nonrefundable credits. min(20 + 250, 120) = 120.
        • Tax. max(120 − 120 − 30, 0) − 10 = −10.
        • Adjustment. max(120 − 20 − 30, 0) − 0 = 70.
        • Pre-rebate. 60.
      4. MA 2021:
        • 62F. 0.140312 × (2,000 − 500 − 100) = 196.4368.
        • Tax. 2,000 − 100 − 500 − 196.4368 = 1,203.5632.
        • Adjustment. 696.4368.
        • Pre-rebate. 1,900.
      5. CO 2022:
        • State tax. 0.044 × 87,050 = 3,830.20; − 234 = 3,596.20.
        • Net income. It reconciles to 73,535.80 only with the $450 FAMLI premium (S3).
    • state_income_tax_rebate_adjustment.yaml:
      6. SC residual:
      • Rebate. min(800, 500 − 100 − 50) = 350.
      • Adjustment. max(500 − 100, 0) − max(500 − 450, 0) = 350.
      1. SC raw override: max(100 − 80, 0) − max(100 − 100, 0) = 20.
      2. VA:
        • Other credits exhaust the tax: max(100 − 120, 0) = 0, so 0.
        • 2024 fully applied: (1,000 − 100 − 50) − (1,000 − 100 − 200 − 50) = 850 − 650 = 200.
      3. MA two units: 500 + 500 + 100 = 1,100 and 500 + 200 = 700.
      4. Mixed states: CO 56 + 750 = 806; MA 500 + 100 = 600; FL 0. The other states' inputs are masked by defined_for.
      5. MT joint: 1,250 + 750 = 2,000.
      6. Out-of-year cases (AZ, HI, IL, NM, ID, DE, IN 2022; MA 62F 2022; ME 2026; SC 2022; VA 2023): each rebate is absent from that year's dated list, so 0.
    • ca_out_of_state_municipal_bond_interest.yaml:
      13. Single filer: 100,000 + 12,500 = 112,500; with the full 50,000, 150,000.
      14. Joint: 5,000 + 7,500 + 1,000 of other additions = 13,500, so 113,500.
      15. Dependent: the child's 7,500 is excluded, so the addition is 12,500.
    • is_tax_unit_dependent.yaml:
      16. IL: 2,425 × 1. CA: 144 + 446 = 590.
      17. UT: 1,941 (personal_exemption.yaml 2023). NY: 1,000 × 1 (tax_unit_child_dependents counts qualifying relatives through is_child_dependent). AZ: the 17+ tier is $25, well below the reduction start.
    • Docs:
      18. Alaska example: 50,000 + 2,622 = 52,622 of federal AGI. Energy relief is in household_benefits, not in gross income.
  • References:
    • Alaska.
      • DOR 23-002: "an energy relief payment of $662.19 that was part of the $3,284 disbursement ... the portion of the dividend that is taxable is $2,621.81".
      • IR-2023-23, footnote 1: "Only for the supplemental Energy Relief Payment received in addition to the annual Permanent Fund Dividend".
      • 2022 Schedule 1: line 8g is "Alaska Permanent Fund dividends".
      • Notice 2023-56: § 3.04, "Disaster Relief", is on file page 6.
    • California. FTB Pub. 1001 shows "Non-California bonds" on file page 7 (2023) and file page 8 (2025). Schedule CA line 2, column C matches the docstring.
  • Code:
    • The 14 refundable states. Each xx_refundable_credits uses adds = "gov.states.xx.tax.income.credits.refundable", and each xx_income_tax adds before-refundable tax and subtracts it. The registry variables read the same dated list, so inclusion matches tax.
    • Montana. mt_income_tax_rebate is zero for anyone who isn't head or spouse, and it is summed over members exactly as mt_refundable_credits is.
    • tax_unit_non_dep_add passes TaxUnit-level variables to add, so ca_hsa_addition, ca_educator_expense_addition and ca_other_additions are unchanged.
    • Callers. No reform reads ca_additions.
  • Collateral effects:
    • No existing output changes. The California input defaults to 0, and every other edit is a docstring.
    • state_property_tax_credits.yaml is untouched.
    • Partner tests. No file under tests/policy/baseline/partners/** changes, and Household API Partners passed.
  • Batch lessons:
  • Changelog. There are four top-level fragments. added for the two new outputs and the new input, and changed for the documentation, are the right types.

Axiom line assessment

Invalid: axiom: pending: hub files parity (S1).

  • Recommended form: axiom: TheAxiomFoundation/rulespec-us#<ca> queued; n/a: the rebate comparison outputs and the Alaska and tax-unit role documentation change no calculated tax or benefit.
  • Why California isn't n/a. The input encodes R&TC § 17143's addition of other-state bond interest to California AGI. rulespec-us main (cea8379e0b) has no § 17133 or § 17143 module and no Schedule CA addition.
  • <ca> is a new open, pe-parity-labelled, dispatch-ready issue with the target, corpus, outputs and companion cases listed under S1.
  • The rest is a valid n/a. The comparison outputs and documentation are reporting and housekeeping, with no policy change.

CI status

28 pass, 4 skipping, 0 pending, 0 failed. I re-checked gh pr checks 10310 at the end of this review, and the head is still bda3538997.

Branch status

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

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

The new outputs are pure additions, follow each state's credit lists without double counting, and 18 expectations hand-check; no partner files are touched. Three should-address items: (1) the 'pending: hub files parity' axiom line is invalid (the CA input encodes R&TC 17143, so it needs a queued issue; the rest is n/a); (2) the one-time rebate registry is inconsistent (it has CO TABOR but not the OR kicker) and differs from policyengine-taxsim's list in CO/MA/ME/OR, though taxsim.md calls it the dashboard contract; (3) the CO 2022 case pins a $450 FAMLI premium that C.R.S. 8-13.3-507 starts in 2023, and #10154 would break it. Four suggestions. Full report in the comment above.

This branch has not been deployed

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants