Skip to content

Limit Kansas itemized deductions to the four K.S.A. 79-32,120(a) categories at each year's shares - #10095

Merged
MaxGhenis merged 6 commits into
mainfrom
ks-itemized-deductions-closed-list
Nov 10, 2026
Merged

MaxGhenis merged 6 commits into
mainfrom
ks-itemized-deductions-closed-list

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 11, 2026 •

Copy link
Copy Markdown
Contributor

What changes

K.S.A. 79-32,120(a) defines the Kansas itemized deduction as four federal itemized categories, each at a share that has changed by year:

Tax years Charitable contributions (§170) Medical care (§213) Qualified residence interest (§163(h)) Real and personal property taxes (§164(a))
2015–2017 100% — 50% 50%
2018 100% 50% 50% 50%
2019 100% 75% 75% 75%
2020 on 100% 100% 100% 100%

Main computes itemized_deductions_less_salt - claim_of_right_deduction + real_estate_taxes; #10038 added the subtraction. That gives 100% of every other federal itemized deduction except SALT in every year, including investment interest (inside interest_deduction), casualty and theft losses, and pre-2018 miscellaneous deductions. This PR adds four dated share parameters under gov.states.ks.tax.income.deductions.itemized.rate and sums only those categories. Qualified residence interest is federal deductible mortgage interest and points (deductible_mortgage_interest) plus deductible mortgage insurance premiums, which 26 U.S.C. 163(h)(3)(E) treats as qualified residence interest. Investment interest, the rest of interest_deduction, does not count. Personal property taxes have no separate input, so only real_estate_taxes enters, uncapped by the federal SALT limit as before.

It also applies (a)(1): before 2021, only a filer who itemizes on the federal return may take the Kansas itemized deduction; from 2021 any filer may take the larger of the two. Main let every filer take the larger in every year. The new federal_itemization_required parameter is true from 2015 to 2020.

K.S.A. 79-32,120(b) reduces the total by income taxes deducted federally, and by depreciation on property that also took one of the eight Kansas amortization deductions it lists (K.S.A. 79-32,221 to 79-32,256). None of the four categories includes income taxes or depreciation, and the model has none of those amortization deductions, so (b) changes nothing here.

The parameters start in 2015. The federal model does not run before 2015, so the 2013–2014 Kansas rules are not modeled.

Sources

Statute and form text, each quote checked against its source:

  • K.S.A. 79-32,120(a)(1)(A): "For all tax years prior to tax year 2021, if federal taxable income of an individual is determined by itemizing deductions ..."
  • (a)(1)(B): "regardless of whether or not such individual's federal taxable income is determined by itemizing deductions"
  • (a)(2): "(B) 50% of the amount of qualified residence interest as provided in section 163(h)"
  • (a)(2): "(C) 50% of the amount of taxes on real and personal property as provided in section 164(a)"
  • 2019 Schedule S line C2: "Real estate taxes from line 5b of federal Schedule A"
  • 26 U.S.C. 163(h)(3)(E)(i): premiums for qualified mortgage insurance "shall be treated for purposes of this section as interest which is qualified residence interest."
  • K.S.A. 79-32,109(a)(1): the federal code applies "as the same may be or become effective at any time, or from time to time, for the taxable year."

Overlap

#10038 merged on 2026-11-07 and edited the same lines of ks_itemized_deductions.py: the reference tuple and the return. The merge of main at 7cc2e5450a keeps this PR's formula, which already leaves out the claim of right deduction, and keeps both PRs' YAML cases. #10038's case now sets home_mortgage_interest: 9_000 in place of interest_deduction: 9_000, with its expectations unchanged.

#10070 (open) edits the same lines too. It conflicts with this head in the same 8 files it conflicts with on main, including both Kansas files. In either landing order, the merged file keeps this PR's formula, which already leaves out wagering losses, and drops #10070's formula edit and reference line. Each PR's YAML cases are kept.

This PR also deletes the file's KNOWN_UNMARKED line in test_source_conflicts.py, because it replaces the comment that quoted the 2021 instructions.

Tests

  • ks_itemized_deductions.yaml: 14 cases, 11 new (2017, 2018, 2019, 2021 and 2025 shares; 2019, 2021, 2025 and 2026 mortgage insurance premiums, including the 2019 AGI phase-out; a 2025 direct deductible_mortgage_interest input), each with its arithmetic. Main's 3 cases keep their expectations. The Kansas formula no longer reads interest_deduction, so unit tests 1 and 2 now enter mortgage interest as deductible_mortgage_interest_tax_unit, and 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 enters it as home_mortgage_interest. Main's formula fails 6 of the 14: Cases 3, 4, 5, 6, 8 and 13.
  • ks_taxable_income.yaml: 6 cases, 3 new on the federal itemizing condition. 1 fails with main's formula.

CI: the Pull request run at 9a2ccc7705, 42643458213, passed all 36 jobs. It tested this head merged into main at 3e5a1dff6b.

Invariants

  • The Kansas itemized deduction = Σ share × category over the four categories (qualified residence interest includes deductible mortgage insurance premiums), each share in [0, 1], so it never exceeds charity + medical + qualified residence interest + property taxes.
  • Investment interest, casualty and theft losses and miscellaneous deductions never change it.
  • Before 2021, a filer who does not itemize federally always gets the Kansas standard deduction; from 2021 the larger deduction applies.

Microsimulation impact

We estimate that limiting Kansas itemized deductions to the four statutory categories lowers them by $167.8m (1.6%) in 2026 on the default dataset, and raises Kansas income tax by $9.2m (0.17%). We compared main at ba9bfdea75 with this branch at 9a2ccc7705.

Kansas itemized deductions fall for 189 sampled tax units (187,463 weighted), by at most $41,384. Kansas taxable income and Kansas income tax rise for 171 sampled tax units (175,793 weighted), and no tax unit's Kansas income tax rises by more than $2,309. Household net income falls by the same $9.2m. Federal income tax and household benefits don't change.

Output Main total Change Records changed (weighted)
ks_itemized_deductions $10.54bn −$167.8m 189 (187,463)
ks_taxable_income $104.19bn +$165.9m 171 (175,793)
ks_income_tax $5.56bn +$9.2m 171 (175,793)
state_income_tax $577.3bn +$9.2m 171 (175,793)
income_tax $2,595.3bn $0 0 (0)
household_net_income $17,198.7bn −$9.2m 167 (171,995)
household_benefits $2,060.5bn $0 0 (0)

impact: Kansas income tax +$9.2m (+0.17%) in 2026 (171 sampled tax units, 175,793 weighted), as Kansas itemized deductions fall by $167.8m; federal income tax and household benefits don't change
axiom: TheAxiomFoundation/rulespec-us#1672 queued, extended with two comments (2015–2019 shares, the pre-2021 federal-itemizer condition, mortgage-insurance companion cases, and #10038's claim of right case); it waits on historical corpus and version verification, ingestion of missing sources, and a signed-release re-pin as needed.

🤖 Generated with Claude Code

…gories

Kansas allows charitable contributions (100%), medical care, qualified
residence interest and real and personal property taxes (50% in 2015-2017
without medical, 50% in 2018, 75% in 2019, 100% from 2020). Main summed every
federal non-SALT itemized deduction, including investment interest, casualty
losses, pre-2018 miscellaneous deductions and the 2020-2021 non-itemizer
charitable deduction, at 100% in all years. Before 2021, (a)(1)(A) lets only
federal itemizers itemize in Kansas.

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
This PR replaces the comment that quoted the 2021 K-40 instructions, so the
file no longer has a conflict-sounding comment and the ratchet in
test_source_conflicts.py requires its line to go.

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: 644db58a8544cb666b16c2591b732cacc95a5a35

This continues an earlier session that pushed nothing to this PR. The commit from 2026-10-11 is kept unchanged; this session added a clean merge of origin/main and one fix.

Blocker: primary sources not re-read

This session's network policy refused www.oregon.gov and web.archive.org, and I stopped trying further hosts after two refusals. So I could not fetch K.S.A. 79-32,120 for any year or the K-40 instructions, and I have not verified the share table, the 2015/2018/2019/2020/2021 effective dates or the PR's quotes. I removed the verbatim statute quotes from the PR body. Before landing, someone with access should confirm the table against each year's statute, and settle a citation question: the item that assigned this PR says 79-32,120(b), while the PR, its code comments, and #10038 and #10070 all cite (a). I could not check which subsection holds the list.

The brief also asks how the statute changed from 2013. I couldn't read it, and the parameters start in 2015. The federal model raises ParameterNotFoundError (a retirement-contribution limit) for 2013 and 2014, so no Kansas household runs in those years either way.

Items

  1. Still needed? Yes; main still returns itm_deds_less_salt + uncapped_property_taxes.
  2. Fix made: the merge with main brought in the source-conflict guard, which failed because this PR deletes the comment quoting the 2021 K-40 instructions that KNOWN_UNMARKED counted. I deleted that line (policyengine_us/tests/code_health/test_source_conflicts.py, formerly line 169), as the guard asks.
  3. Body corrected: it said 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 Kansas test still holds here. It doesn't: that case enters interest_deduction: 9_000 and expects ks_itemized_deductions: 12_000, and this formula ignores interest_deduction, so it computes 3,000. Whichever 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 and this lands second moves that input to deductible_mortgage_interest_tax_unit. Both Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 and Count gambling winnings in federal gross income and fix the wagering losses deduction #10070 also conflict with this PR in ks_itemized_deductions.py's reference tuple (lines 10–14 here) and the return (lines 30–35 here). I did not touch either PR.
  4. Right? Read on this branch: ks_itemized_deductions.py:30-35 is the weighted sum of charitable_deduction, medical_expense_deduction, deductible_mortgage_interest_tax_unit (federal deductible mortgage interest after the acquisition-debt caps) and real_estate_taxes. ks_taxable_income.py:23-26 lets a unit itemize in Kansas only if it itemizes federally while federal_itemization_required is true.
  5. Reviews: none.
  6. Defect reproduced: with main's two formulas restored, 4 of 7 itemized cases and 1 of 6 taxable-income cases fail. Case 7 (2021 non-itemizer charity) passes on main too, so it guards nothing today; I kept it as a regression case.

Hand computations

File, case Computation Expected
itemized 3 (2025) 3,000 + 2,000 + 9,000 + 5,000 19,000
itemized 4 (2019) 1,000 + 0.75 × (2,000 + 8,000 + 4,000) 11,500
itemized 5 (2018) 1,000 + 0.5 × 14,000 8,000
itemized 6 (2017) 1,000 + 0.5 × (8,000 + 4,000) 7,000
itemized 7 (2021) 2,000 + 4,000 6,000
taxable 4 (2019, no federal itemizing) 50,000 − 3,000 − 2,250 44,750
taxable 5 (2019, federal itemizer) 50,000 − 9,000 − 2,250 38,750
taxable 6 (2021, no federal itemizing) 50,000 − 9,000 − 2,250 38,750

Tests run (merged head)

  • ks_itemized_deductions.yaml 7 passed; ks_taxable_income.yaml 6 passed; ks_integration.yaml 2 passed; ks_2025_integration.yaml 13 passed; gov/irs/salt_refund_integration.yaml 38 passed.
  • test_source_conflicts.py: 103 passed after the fix (1 failed before).
  • ruff format --check, ruff check on both variables: clean.

Mutants

  • Medical share 1 from 2015: 1 itemized failure, case 6 (killed).
  • federal_itemization_required false from 2015: 1 taxable-income failure, case 4 (killed).

CI

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

Judgment calls

  • No Hypothesis test. The formula is a weighted sum of four inputs, and the YAML cases pin each year's shares and the itemizing gate; CLAUDE.md asks for YAML where it can exercise the behavior. The invariants are in the body.
  • may_itemize uses tax_unit_itemizes, PolicyEngine's federal itemizing choice, for "federal taxable income is determined by itemizing", as other states' formulas do.

Proposed follow-up

  • Kansas itemized deduction for 2013–2014. Once the federal model runs before 2015, add the 2013–2014 shares from K.S.A. 79-32,120 as amended in 2013, with YAML cases from those years' K-40 instructions.

Generated by Claude Code

@MaxGhenis
MaxGhenis marked this pull request as ready for review November 3, 2026 21:40
@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 (9a2ccc7).
⚠️ Report is 91 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main    #10095   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            1         2    +1     
  Lines           37        35    -2     
  Branches         0         1    +1     
=========================================
- Hits            37        35    -2     
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

MaxGhenis commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

This interim note is superseded by the final review: #10095 (comment)


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: REQUEST_CHANGES
644db58
METHODOLOGY_CHOICE: no

The PR's head has not moved past 644db58a85. The share table, the federal itemizing gate and the property-tax handling match K.S.A. 79-32,120(a) as the hub read it. One input is wrong: the formula reads qualified residence interest from a variable that leaves out mortgage insurance premiums and ignores a direct deductible_mortgage_interest input. Main counts both, so the PR makes Kansas worse for those filers. A one-line change fixes both. I tested the fix, and every existing case and integration file still passes with it.

Findings

P1. Qualified residence interest leaves out mortgage insurance premiums

policyengine_us/variables/gov/states/ks/tax/income/deductions/ks_itemized_deductions.py:26-28 reads only deductible_mortgage_interest_tax_unit, which is interest and points after the acquisition-debt caps. 26 U.S.C. 163(h)(3)(E) treats qualified mortgage insurance premiums as qualified residence interest, and K.S.A. 79-32,120(a) counts "qualified residence interest as provided in section 163(h)". The 2019 Schedule S line C4, as the hub read it, adds "any mortgage insurance premiums you paid and reported on federal Schedule A". On main, interest_deduction adds deductible_mortgage_insurance_premiums, so main counts the premiums (at 100%). The premiums are federally deductible in 2007–2021 and again from 2026 (gov.irs.deductions.itemized.interest.mortgage_insurance_premiums.in_effect). So the PR drops them from the Kansas deduction in 2015–2021 and from 2026 on. Main has no such gap, so this PR introduces it.

The PR body also states this wrongly: "Qualified residence interest is deductible_mortgage_interest_tax_unit, not interest_deduction."

P2. A direct deductible_mortgage_interest input no longer reaches Kansas

deductible_mortgage_interest is a person-level variable that callers and tests set directly (for example tests/core/test_state_tax_head_spouse_swap_invariance.py, and nc_mortgage_and_property_tax_before_limitation.py reads it). When it is an input, deductible_mortgage_interest_tax_unit still computes from home_mortgage_interest and gives 0. Main reads it through interest_deduction.

Witness for P1 and P2. I ran a single filer in Kansas through Simulation on main (9f0242f6), on this PR, and on this PR with the fix below:

Case Statute Main PR PR + fix
2019, $8,000 interest + $1,000 premiums, wages $40,000 0.75 × 9,000 = 6,750 9,000 6,000 6,750
2021, $1,000 premiums 1,000 1,000 0 1,000
2026, $8,000 interest + $1,000 premiums 9,000 9,000 8,000 9,000
2025, deductible_mortgage_interest: 9_000 input 9,000 9,000 0 9,000

(The values are ks_itemized_deductions. Main gives 9,000 in 2019 because it applies 100%, which is the defect this PR fixes.)

Fix: replace lines 26–28 with:

        # 26 U.S.C. 163(h)(3)(E) treats deductible mortgage insurance premiums
        # as qualified residence interest.
        qualified_residence_interest = add(
            tax_unit,
            period,
            ["deductible_mortgage_interest", "deductible_mortgage_insurance_premiums"],
        )

nc_mortgage_and_property_tax_before_limitation.py reads the same two variables. With this change on a scratch copy of the head:

  • ks_itemized_deductions.yaml: 7 passed.
  • ks_taxable_income.yaml: 6 passed.
  • ks_2025_integration.yaml: 13 passed.
  • ks_integration.yaml: 2 passed.
  • The four probe cases above: 4 passed.

Cases 1 and 2 still pass with their deductible_mortgage_interest_tax_unit: 9 input, because the person-level variable derives from it.

Add these cases to ks_itemized_deductions.yaml (I hand-computed each from (a)(4) and (a)(5) and 163(h)(3)(E); the 2019 case's wages keep AGI under the $100,000 premium phase-out start):

# K.S.A. 79-32,120(a)(4) counts qualified residence interest under section
# 163(h), and 163(h)(3)(E) treats mortgage insurance premiums as such interest
# (2019 Schedule S, line C4).
- name: Case 8, 2019 mortgage insurance premiums count at the 75% share.
  period: 2019
  input:
    state_code: KS
    employment_income: 40_000
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    # 0.75 * (8,000 + 1,000)
    ks_itemized_deductions: 6_750

- name: Case 9, 2025 a direct deductible mortgage interest input counts.
  period: 2025
  input:
    state_code: KS
    deductible_mortgage_interest: 9_000
  output:
    ks_itemized_deductions: 9_000

Then change the body sentence to: "Qualified residence interest is federal deductible mortgage interest and points (deductible_mortgage_interest) plus deductible mortgage insurance premiums, which 26 U.S.C. 163(h)(3)(E) treats as qualified residence interest. Investment interest, the rest of interest_deduction, does not count."

P3. The parameter reference title says "lines C3"

policyengine_us/parameters/gov/states/ks/tax/income/deductions/itemized/rate/qualified_residence_interest.yaml:14: 2017 Form K-40 instructions, Schedule S Part C, lines C3 should read line C3. I could not open the 2017 booklet to confirm the line numbers in this file or the other rate files.

P3. The body leaves out (b) and has no quotes

The adopting session removed the statute quotes. From the hub's reading, the body can carry these, each under 25 words:

  • (a)(1)(A): "For all tax years prior to tax year 2021, if federal taxable income of an individual is determined by itemizing deductions ..."
  • (a)(1)(B): "regardless of whether or not such individual's federal taxable income is determined by itemizing deductions"
  • (a)(2): "(B) 50% of the amount of qualified residence interest as provided in section 163(h)"
  • (a)(2): "(C) 50% of the amount of taxes on real and personal property as provided in section 164(a)"
  • 2019 Schedule S line C2: "Real estate taxes from line 5b of federal Schedule A"

Add this sentence: "K.S.A. 79-32,120(b) reduces the total by income taxes deducted federally and by depreciation claimed for certain credits. None of the four categories includes income taxes, and the model has no input for that depreciation, so (b) changes nothing here."

The hand-off's "Draft, so Pull request CI skips it" is out of date. The PR is not a draft, and the Pull request run at this head concluded success, with every job passing (none were cancelled or skipped).

Overlap with #10038 and #10070

Both PRs conflict with this one at the same places in ks_itemized_deductions.py: the reference tuple (this PR's lines 10–14) and the return (this PR's lines 30–35). I confirmed this from their diffs. If this PR lands first, the second PR to land must make these changes:

The open-PR search for ks_itemized_deductions, ks_taxable_income and "kansas itemized" found no other overlap.

Mutants

I ran each mutant in its own process on a scratch worktree, then restored it and confirmed git status was clean.

Mutant Killed by
Medical share 1 from 2015 (hand-off) itemized Case 6, 2017
federal_itemization_required false from 2015 (hand-off) taxable Case 4, 2019 non-itemizer
2018 medical share 0.75 itemized Case 5, 2018
Federal itemizing gate still applied in 2021 (false moved to 2022) taxable Case 6, 2021, and taxable unit test 2
Mine: qualified residence interest steps to 75% in 2018 itemized Case 5, 2018

None survived. P1 survives the existing suite, since no case enters mortgage insurance premiums. Case 8 above kills it.

Checked and sound

  • Defect reproduced. I restored main's two formulas over the head and ran the PR's tests. 4 of 7 itemized cases failed (Cases 3, 4, 5, 6) and 1 of 6 taxable cases failed (Case 4), matching the body. Itemized Case 7 passes on main too.

  • Share table. Against the hub's reading of (a)(2)–(a)(5), year by year:

    • charity is 100% from 2015;
    • medical is 0 in 2015–2017, 50% in 2018, 75% in 2019 and 100% from 2020;
    • qualified residence interest and property taxes are 50% in 2015–2018, 75% in 2019 and 100% from 2020.

    Every parameter date and value matches.

  • Hand values. I computed each before reading the tests:

    • 2017: 1,000 + 0.5 × (8,000 + 4,000) = 7,000;
    • 2018: 1,000 + 0.5 × 14,000 = 8,000;
    • 2019: 1,000 + 0.75 × 14,000 = 11,500;
    • 2025: 3,000 + 2,000 + 9,000 + 5,000 = 19,000.

    The tests carry the same values. In full-household runs, a single filer with $2,000 charity, $14,000 mortgage interest and $9,000 real estate taxes gets 13,500 in 2018 (2,000 + 0.5 × 23,000) and 19,250 in 2019 (2,000 + 0.75 × 23,000). Single and joint filers both run, with no cycle through tax_unit_itemizes in 2018–2021.

  • The itemizing gate. ks_taxable_income.py:23-26 applies tax_unit_itemizes only while federal_itemization_required is true, which is 2015–2020. That matches (a)(1)(A) and (B). The filer takes the larger deduction only when allowed. tax_unit_itemizes compares federal income_tax only, as 35 other state formulas already rely on.

  • The SALT cap. "As provided in section 164(a)" names the deductible taxes. The cap sits in 164(b)(6), and line C2 takes line 5b, before line 5e's cap. The formula adds real_estate_taxes uncapped. Sound.

  • (b)'s income tax reduction. None of the four inputs includes income taxes, so the reduction cannot reach the total. I agree.

  • Personal property taxes. Main has no input for them. personal_property is a property value (quantity_type = STOCK), not a tax. The body already states the limit. Follow-up: add a personal property tax input and count it in Kansas (a) and line C3.

  • Medical. medical_expense_deduction is net of the federal AGI floor, which matches section 213 and federal Schedule A line 4.

  • Charity. charitable_deduction is the section 170 itemized amount, after ceilings and the 2026 floor. It does not include the 2021 non-itemizer deduction (Case 7).

  • 2013–2014. A 2014 Kansas household raises ParameterNotFoundError on gov.irs.gross_income.retirement_contributions.limit[401k], so the body's "does not run before 2015" holds.

  • Tests run at the head. ks_itemized_deductions.yaml 7 passed; ks_taxable_income.yaml 6 passed; test_source_conflicts.py 103 passed. The KNOWN_UNMARKED deletion is right, since the counted comment is gone.

  • Readers. ks_taxable_income → ks_income_tax_before_credits. Both variables are also listed in the state_itemized_deductions and state_taxable_incomes aggregates, which only report them. No other reader changes.

  • Scope. The PR touches nothing under .github/ or tests/policy/baseline/partners/. The changelog fragment is a top-level .fixed.md. The impact: and axiom: lines are present.

  • Baseline. The diff moves outputs at baseline in every year:

    • investment interest, casualty and theft losses and miscellaneous deductions leave the Kansas total;
    • 2018–2019 shares fall below 100%;
    • before 2021, federal non-itemizers lose the Kansas itemized deduction;
    • with the PR as it stands, mortgage insurance premiums drop from 2026.

    The hub's population check should compare ks_income_tax on the data years, split by whether the unit has deductible_mortgage_insurance_premiums or investment interest.

What I couldn't check


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

K.S.A. 79-32,120(a) counts qualified residence interest "as provided in
section 163(h)", and 26 U.S.C. 163(h)(3)(E) treats deductible mortgage
insurance premiums as such interest. Reading deductible_mortgage_interest
(person level) also lets a direct input of it reach Kansas, as it did on
main through interest_deduction. Adds 2019, 2021, 2025 and 2026 cases and
fixes the "lines C3" reference title.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: a3606f3a2ee52fec442c1fcb246b258bf32d78d9 (one commit on top of 644db58a85; the branch had not moved when I pushed, and the last push before mine was 2026-11-03T21:21Z).

What the item asked and how I resolved it

  1. Qualified residence interest (r1 P1 and P2). policyengine_us/variables/gov/states/ks/tax/income/deductions/ks_itemized_deductions.py:26-32 now reads add(tax_unit, period, ["deductible_mortgage_interest", "deductible_mortgage_insurance_premiums"]) under the requested 163(h)(3)(E) comment. I read the code this depends on:

    • deductible_mortgage_interest (person) is deductible_mortgage_interest_tax_unit × home_mortgage_interest_share; the tax-unit variable is interest plus points after the acquisition-debt caps. So the sum equals the tax-unit figure when computed, and a direct person-level input now reaches Kansas.
    • deductible_mortgage_insurance_premiums is in_effect × premiums × (1 − AGI phase-out); in_effect is true 2007–2021 and from 2026. Kansas inherits both.
    • No double count. interest_deduction = deductible_interest_expense (= deductible_mortgage_interest + non_mortgage_interest) + deductible_mortgage_insurance_premiums. The Kansas sum takes the first and third, neither of which contains the other. ks_taxable_income.py is the only formula reader of ks_itemized_deductions.
  2. Cases (ks_itemized_deductions.yaml), each hand-computed before running, with the arithmetic in comments:

    • Case 8 (:97), 2019, wages 40,000, interest 8,000, premiums 1,000: AGI under the 100,000 phase-out start, 0.75 × 9,000 = 6,750.
    • Case 9 (:109), 2025, deductible_mortgage_interest: 9_000: 1 × 9,000 = 9,000.
    • Case 10 (:120), 2026, interest 8,000 + premiums 1,000, AGI 0: 1 × 9,000 = 9,000.
    • Case 11 (:132), 2021, premiums 1,000 alone, AGI 0: 1,000.
    • At 644db58a85 (old formula, new cases): 4 failed, 7 passed. Case 8 gave 6,000, Case 9 gave 0, Case 10 gave 8,000, Case 11 gave 0, matching r1's table.
  3. Mutants, each in its own processes running both ks_itemized_deductions.yaml and ks_taxable_income.yaml, restored from a backup after each (git diff --stat checked after every restore):

    Mutant Killed by
    Medical share 1 from 2015 itemized Case 6 (2017)
    federal_itemization_required false from 2015 taxable Case 4 (2019 non-itemizer)
    2018 medical share 0.75 itemized Case 5 (2018)
    Itemizing gate through 2021 (false moved to 2022) taxable unit test 2 and taxable Case 6 (2021)
    Qualified residence interest at 75% from 2018 itemized Case 5 (2018)
    Drop deductible_mortgage_insurance_premiums from the add itemized Cases 8, 10 and 11

    None survived.

  4. Reference title. qualified_residence_interest.yaml:13: "lines C3" is now "line C3". (The brief said :14; the title is on line 13 and the href on 14.) The other Kansas itemized titles have no singular/plural slip: property_taxes.yaml says "lines C1 and C2" (two lines) and charitable_contributions.yaml says "line C4"; the medical, federal-itemization and 2018/2019/2025 titles name no single line or say "lines 1 to 4".

  5. Body. Replaced the qualified residence interest sentence with the review's wording; added the (b) sentence; added the statute quotes (the review's five plus 163(h)(3)(E)(i) and 79-32,109(a)(1), each under 25 words); rewrote the Overlap section as the item gave it; updated the test counts; added the CI line. The impact: and axiom: lines are unchanged. The stale "Draft, so Pull request CI skips it" line was in r0's hand-off comment, not in the body, so there was nothing to delete there; the body now carries the CI result instead.

Tests run at the new head (one file per process)

  • ks_itemized_deductions.yaml: 11 passed
  • ks_taxable_income.yaml: 6 passed
  • ks_2025_integration.yaml: 13 passed
  • ks_integration.yaml: 2 passed
  • test_source_conflicts.py: 103 passed
  • ruff format --check and ruff check on ks_itemized_deductions.py: clean.

CI

The Pull request run at a3606f3a2e concluded success: 36 of 36 jobs succeeded, with none cancelled or skipped, including Household API Partners. No partner test changed.

Judgment calls

  • Sources not read here. This session's proxy refused ksrevisor.gov, law.cornell.edu and web.archive.org (403 on CONNECT). The statute quotes in the body are the hub's and r1's readings, and the body says so. I quoted only text the brief or r1 gave verbatim.
  • Case 11 at 2021 relies on premiums being deductible through 2021 (in_effect true until 2022), as the brief described. With AGI 0 the phase-out is not exercised; Case 8 keeps AGI under the start rather than testing the phase-out itself, since that belongs to the federal variable's own tests.
  • Branch. I pushed to the PR branch ks-itemized-deductions-closed-list as the brief named, not to the session's default branch.

Proposed follow-up

  • Kansas: count personal property taxes in the itemized deduction. K.S.A. 79-32,120(a) counts taxes on real and personal property, and the model has no personal property tax input, so only real_estate_taxes enters. Add the input and include it here and in line C3.

Generated by Claude Code

@MaxGhenis

MaxGhenis commented Nov 4, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

This interim note is superseded by the final review: #10095 (comment)


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
a3606f3
METHODOLOGY_CHOICE: no

The head has not moved past a3606f3a2e, which is one commit on 644db58a85. That commit makes Kansas qualified residence interest add(tax_unit, period, ["deductible_mortgage_interest", "deductible_mortgage_insurance_premiums"]). It closes round 1's P1 and P2, and it is right. I read the statutes and booklets this time instead of relying on the hub's reading:

  • K.S.A. 79-32,120 counts "qualified residence interest as provided in section 163(h)".
  • 26 U.S.C. 163(h)(3)(E)(i) treats qualified mortgage insurance premiums as such interest.
  • The 2015, 2017, 2019 and 2021 Kansas booklets add the premiums to that line.
  • The 2018 booklet predates the retroactive federal extension.
  • The 2025 booklet reserves the premium line, because the federal deduction had lapsed.

The code applies this correctly in every year, filing status and entity I tried. Cases 8–11 return my hand values, every test file passes, and CI is green.

The findings below are a wrong merge rule in the body's Overlap section (P2), a misdescribed statute trigger in the (b) sentence, one surviving mutant, two page anchors and a missing comment (all P3). None changes a model output. The P2 and the (b) P3 are body edits the hub can make when it fills the impact: and axiom: lines. The other P3s are small and don't block landing.

Findings

P2. The Overlap rule fails if #10038 or #10070 lands first

Where: PR body, "Overlap" section, first paragraph.

The body says: "Whichever lands second drops its own formula edit and keeps its YAML cases. #10038's new case then sets home_mortgage_interest: 9_000 in place of interest_deduction: 9_000."

That is right only when this PR lands first. If #10038 (or #10070) lands first, this PR is "second". It must then keep its own formula and drop the other PR's subtraction. It must also make the edit to #10038's case, which by then is on main.

Failure scenario: a session that follows the sentence literally after #10038 or #10070 lands would drop the closed list. Five of this PR's cases then fail. If it keeps the formula but leaves #10038's case alone, as the body assigns that edit to #10038, that case fails instead.

What I ran: I merged #10038's head (6906894550) and #10070's head (9b392f2ec3) into scratch copies of this head. The only conflicts were in ks_itemized_deductions.py and ks_itemized_deductions.yaml. Then I ran policyengine-core test ks_itemized_deductions.yaml -c policyengine_us on each resolution:

Merged tree ks_itemized_deductions.py Cases Result
+ #10038 #10038's (the body's rule if #10038 lands first) this PR's 11 + #10038's 5 failed, 7 passed: Cases 3, 4, 5, 6 and 8
+ #10038 this PR's this PR's 11 + #10038's, unchanged 1 failed: #10038's case gives 3,000, expects 12,000
+ #10038 this PR's this PR's 11 + #10038's, with home_mortgage_interest: 9_000 12 passed
+ #10070 #10070's (the body's rule if #10070 lands first) this PR's 11 + #10070's 2 5 failed, 8 passed: Cases 3, 4, 5, 6 and 8
+ #10070 this PR's this PR's 11 + #10070's 2, unchanged 13 passed

So the right resolution is the same in either order.

Replace the paragraph with:

#10038 and #10070 edit the same lines of ks_itemized_deductions.py: the reference tuple and the return. In either landing order, the merged file keeps this PR's formula, and the other PR's formula edit and reference line are dropped. The closed list already leaves out the claim of right deduction and wagering losses. Each PR's YAML cases are kept. #10038's new case must set home_mortgage_interest: 9_000 in place of interest_deduction: 9_000. If #10038 lands first, this PR makes that edit when it resolves the conflict.

P3. The (b) sentence names the wrong trigger for the depreciation reduction

Where: PR body, the paragraph that starts "K.S.A. 79-32,120(b) reduces the total…".

The body says (b) reduces the total "by depreciation claimed for certain credits". (b) reduces it by "the amount of all depreciation deductions claimed for any real or tangible personal property upon which the deduction allowed by K.S.A. 79-32,221" or seven other sections "is or has been claimed". I read all eight sections on ksrevisor.gov:

  • 79-32,221, -227, -232, -237, -249, -250, -255 and -256 are each headed "accelerated depreciation, deduction".
  • Each is an amortization deduction from Kansas AGI, for refineries, pipelines, carbon dioxide capture equipment and the like.
  • Several sit beside credits, but the trigger is the deduction.

The conclusion still holds.

Replace the paragraph with:

K.S.A. 79-32,120(b) reduces the total by income taxes deducted federally, and by depreciation on property that also took one of the Kansas amortization deductions in K.S.A. 79-32,221 to 79-32,256. None of the four categories includes income taxes or depreciation, and the model has none of those amortization deductions, so (b) changes nothing here.

P3. No case pins the deductible premium amount, so a raw-premium mutant survives

Where: policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/ks_itemized_deductions.yaml, Cases 8, 10 and 11 (lines 97–140).

Each premium case has AGI under $100,000 and falls in a year when premiums are deductible. So no case tells deductible_mortgage_insurance_premiums apart from the raw mortgage_insurance_premiums input.

Failure scenario: a later edit swaps in the raw input. Kansas would then count premiums in 2022–2025 and above the AGI phase-out, and every case would still pass. My mutant M7 below does exactly that and survives all 17 cases in the two files. The federal variable's own tests pin the years and the phase-out, but nothing pins that Kansas reads the deductible amount. The hand-off's judgment call left the phase-out to the federal tests.

Add after Case 11. I computed each by hand before running. Both pass at the head. Run against M7, Case 12 gives 9,000 (expects 8,000) and Case 13 gives 6,750 (expects 6,375).

# 163(h)(3)(E)(iv) ends the premium deduction after 2021, and P.L. 119-21
# sec. 70108 restores it only from 2026 (2025 Kansas Schedule A line 8d is
# reserved).
- name: Case 12, 2025 mortgage insurance premiums do not count.
  period: 2025
  input:
    state_code: KS
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    # 1 * 8,000
    ks_itemized_deductions: 8_000

# 163(h)(3)(E)(ii) cuts premiums by 10% for each $1,000, or fraction of one,
# of AGI above $100,000.
- name: Case 13, 2019 premiums after the AGI phase-out count at the 75% share.
  period: 2019
  input:
    state_code: KS
    employment_income: 104_500
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    # AGI exceeds 100,000 by 4,500: 5 steps, so premiums fall 50% to 500.
    # 0.75 * (8,000 + 500)
    ks_itemized_deductions: 6_375

P3. Two 2025 references open page 14, but the charity and interest rules are on page 15

In the 2025 booklet, page 14 holds the Kansas Schedule A lines for medical care and taxes. Page 15 holds:

  • "Kansas allows 100% of the qualified residence interest paid as provided in section 163(h)…";
  • line 8d "(Reserved for future use)";
  • "Kansas allows 100% of the charitable contributions that qualify as deductions in section 170…".

Fixes:

  • policyengine_us/parameters/gov/states/ks/tax/income/deductions/itemized/rate/charitable_contributions.yaml:14: href: https://www.ksrevenue.gov/pdf/ip25.pdf#page=14 becomes href: https://www.ksrevenue.gov/pdf/ip25.pdf#page=15.
  • policyengine_us/variables/gov/states/ks/tax/income/deductions/ks_itemized_deductions.py:13: the href covers pages 14–15. Under the reference rules, put # PDF pages 14-15 on the line above "https://www.ksrevenue.gov/pdf/ip25.pdf#page=14",.

The other titles and pages check out (see below).

P3. Case 7 has no arithmetic, and the body's source caveat is now out of date

  • The body says each of the 9 new cases carries "its arithmetic". Case 7 (ks_itemized_deductions.yaml:84-92) has none. Above ks_itemized_deductions: 6_000, add # 2,000 + 4,000; the 300 non-itemizer deduction does not count.
  • The body says its quotes are "as the hub read the sources (this build session's network refused ksrevisor.gov, law.cornell.edu and web.archive.org)". I confirmed every quote against ksrevisor.gov, govinfo.gov, law.cornell.edu and the ksrevenue.gov booklets, so the parenthetical can become "confirmed against the sources in the round 2 review".

Mutants

Each mutant ran on a scratch copy of the head. Each test file ran in its own process (ks_itemized_deductions.yaml, then ks_taxable_income.yaml, plus my extra cases for M7 and M8). After each mutant I restored the copy with git checkout -- . and confirmed git status was clean.

Mutant Killed by (got vs expected)
M1 medical share 1 from 2015 (hand-off) itemized Case 6, 2017: 9,000 vs 7,000
M2 federal_itemization_required false from 2015 (hand-off) taxable Case 4, 2019: 38,750 vs 44,750
M3 2018 medical share 0.75 (hand-off) itemized Case 5, 2018: 8,500 vs 8,000
M4 itemizing gate through 2021 (hand-off) taxable unit test 2 (76,000 vs 74,000) and taxable Case 6 (44,250 vs 38,750)
M5 qualified residence interest 75% from 2018 (hand-off) itemized Case 5, 2018: 10,000 vs 8,000
M6 drop deductible_mortgage_insurance_premiums from the add (hand-off) itemized Case 8 (6,000 vs 6,750), Case 10 (8,000 vs 9,000) and Case 11 (0 vs 1,000)
M7 raw mortgage_insurance_premiums in place of deductible_mortgage_insurance_premiums (mine) survives all 11 itemized and 6 taxable cases. Only my extra cases kill it: W1 (2019, AGI 104,500) 6,750 vs 6,375; W2 (2025) 9,000 vs 8,000; W5 (2019 separate, AGI 52,000) 750 vs 450; W7 (2019, AGI 200,000) 6,750 vs 6,000. See the P3 above.
M8 deductible_mortgage_interest_tax_unit in place of person-level deductible_mortgage_interest (mine) itemized Case 9, 2025: 0 vs 9,000 (and my W6, a spouse's direct input: 0 vs 9,000)

The hand-off's six all die on the cases it names. My M8 confirms that Case 9 is the only case guarding round 1's P2. My M7 survives, which is the P3 above.

Against the fix brief

The commit does every item the round 2 brief asked for:

  • the add with the 163(h)(3)(E) comment;
  • Cases 8–11 with hand values in comments;
  • the six mutants;
  • the "line C3" title;
  • the body sentences, quotes, Overlap section and CI line, with impact: and axiom: kept;
  • the five test runs.

Both body errors above came from the brief's own wording, copied verbatim:

  • The Overlap rule compresses round 1's advice, which applied only "If this PR lands first".
  • "Depreciation claimed for certain credits" is round 1's wording, written when that review couldn't reach the statute.

Checked and sound

Sources confirmed in this session:

  • K.S.A. 79-32,120 (ksrevisor.gov; current text, history ending L. 2021, ch. 93):
    • (a)(1)(A) limits the Kansas itemized deduction to federal itemizers for "all tax years prior to tax year 2021", and (a)(1)(B) drops the limit from 2021.
    • (a)(2)–(a)(5) give the shares: 2015–2017 charity 100%, interest and property taxes 50%, no medical; 2018 charity 100%, the rest 50%; 2019 charity 100%, the rest 75%; 2020 on, 100% each.
    • Every parameter date and value matches, and so does the body's table.
  • K.S.A. 79-32,109(a)(1): the code applies "as the same may be or become effective at any time, or from time to time, for the taxable year."
  • 26 U.S.C. 163(h)(3)(E) (govinfo.gov, 2024 edition):
    • (i) matches the body's quote;
    • (ii) cuts premiums by 10% for each $1,000 ($500 for a separate filer), or fraction, of AGI above $100,000 ($50,000);
    • (iv) ends them after 2021.
  • 163(h)(3)(F)(i)(III) (law.cornell.edu, current text): it turns (E)(iv) off, and P.L. 119-21 § 70108(b) applies it to "taxable years beginning after December 31, 2025". gov.irs.deductions.itemized.interest.mortgage_insurance_premiums matches all of this: in_effect is true 2007–2021 and from 2026, the start is 100,000 (50,000 separate), the step is 1,000 (500), and the rate is 0.1.
  • K-40 booklets (ksrevenue.gov):
    • 2015 and 2017: Schedule S line C3 is "Qualified residence interest and mortgage insurance premiums… Enter 50%".
    • 2018: page 2 gives "100% charitable contributions, 50% qualified medical expenses, 50% qualified residential interest, and 50% real and personal property taxes" for federal itemizers.
    • 2019: page 2 gives the same with 75% in place of 50%. Line C2 matches the body's quote, and line C4 adds premiums.
    • 2021: Kansas Schedule A line 8d is "Mortgage insurance premiums".
    • 2025: line 8d is reserved.
    • The 2017 titles ("line C3", "line C4", "lines C1 and C2"), the 2018 and 2019 page-2 references, and the 2025 medical reference ("lines 1 to 4", page 14) are right.

The qualified residence interest rule:

  • The code path. deductible_mortgage_interest is deductible_mortgage_interest_tax_unit × home_mortgage_interest_share. The share sums to 1 across head and spouse, or splits evenly when no filer reports interest. So the Kansas sum equals the tax-unit figure: interest and points on both residences after the acquisition-debt caps.
  • No double count. deductible_mortgage_insurance_premiums is a tax-unit amount after the phase-out and the years. interest_deduction = deductible_mortgage_interest + non_mortgage_interest (= investment_interest_expense) + deductible_mortgage_insurance_premiums. Kansas takes the first and third, so nothing counts twice and investment interest stays out.
  • Mixed entities. Core's add sums the person-level variable over members and reads the tax-unit one directly.

Hand values. I derived these from the statute and the federal rules. The brief had already given the expected values, and the delta diff showed the YAML before I wrote mine down. Then I ran them. All passed at the head.

Case Hand computation Head 644db58a85 main
8, 2019, AGI 40,000 0.75 × (8,000 + 1,000) 6,750 6,000 9,000
9, 2025, direct input 1 × 9,000 9,000 0 9,000
10, 2026, AGI 0 1 × (8,000 + 1,000) 9,000 8,000 9,000
11, 2021, premiums alone 1 × 1,000 1,000 0 1,000
W1, 2019, AGI 104,500 premiums 1,000 − 50% = 500; 0.75 × 8,500 6,375 6,000 8,500
W2, 2025 premiums barred; 1 × 8,000 8,000 8,000 8,000
W3, 2018, AGI 40,000 0.5 × 9,000 4,500 4,000 9,000
W4, 2026 joint, 6,000 + 500 and 2,000 + 500 9,000 9,000 8,000 9,000
W5, 2019 separate, AGI 52,000 4 steps of $500: 1,000 − 40% = 600; 0.75 × 600 450 0 600
W6, 2025 joint, spouse's direct input 9,000 9,000 9,000 0 9,000
W7, 2019, AGI 200,000 premiums fully phased out; 0.75 × 8,000 6,000 6,000 8,000
W8, 2021 joint, tax-unit input 9,000 split 4,500 and 4,500; sum 9,000 9,000 9,000 9,000
W9, 2025, only a dependent reports 9,000 head's share 1 9,000 9,000 9,000
W10, 2017, AGI 40,000 0.5 × 9,000 4,500
W11, 2019 federal itemizer, AGI 104,500, interest 20,000, premiums 1,000, taxes 5,000 itemized 0.75 × 20,500 + 0.75 × 5,000 = 19,125; taxable 104,500 − 19,125 − 2,250 83,125
W12, 2019 non-itemizer, wages 40,000, interest 2,000, premiums 1,000, taxes 2,000 itemized 3,750, but the gate forces the 3,000 standard deduction; 40,000 − 3,000 − 2,250 34,750
My cases W1–W12 (all pass at the head; run with policyengine-core test <file> -c policyengine_us)
- name: W1, 2019 single, AGI 104,500, premiums phase out by 50 percent.
  period: 2019
  input:
    state_code: KS
    employment_income: 104_500
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    deductible_mortgage_insurance_premiums: 500
    ks_itemized_deductions: 6_375

- name: W2, 2025 single, premiums are not qualified residence interest.
  period: 2025
  input:
    state_code: KS
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    ks_itemized_deductions: 8_000

- name: W3, 2018 single, premiums count at 50 percent.
  period: 2018
  input:
    state_code: KS
    employment_income: 40_000
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    ks_itemized_deductions: 4_500

- name: W4, 2026 joint, both spouses report interest and premiums.
  period: 2026
  input:
    people:
      head:
        age: 40
        home_mortgage_interest: 6_000
        mortgage_insurance_premiums: 500
      spouse:
        age: 40
        home_mortgage_interest: 2_000
        mortgage_insurance_premiums: 500
    tax_units:
      tax_unit:
        members: [head, spouse]
    marital_units:
      marital_unit:
        members: [head, spouse]
    households:
      household:
        members: [head, spouse]
        state_code: KS
  output:
    filing_status: [JOINT]
    ks_itemized_deductions: 9_000

- name: W5, 2019 separate filer, AGI 52,000, premiums phase out by 40 percent.
  period: 2019
  input:
    state_code: KS
    filing_status: SEPARATE
    employment_income: 52_000
    mortgage_insurance_premiums: 1_000
  output:
    deductible_mortgage_insurance_premiums: 600
    ks_itemized_deductions: 450

- name: W6, 2025 joint, a direct deductible mortgage interest input on the spouse.
  period: 2025
  input:
    people:
      head:
        age: 40
      spouse:
        age: 40
        deductible_mortgage_interest: 9_000
    tax_units:
      tax_unit:
        members: [head, spouse]
    marital_units:
      marital_unit:
        members: [head, spouse]
    households:
      household:
        members: [head, spouse]
        state_code: KS
  output:
    ks_itemized_deductions: 9_000

- name: W7, 2019 single, AGI 200,000, premiums fully phased out.
  period: 2019
  input:
    state_code: KS
    employment_income: 200_000
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    ks_itemized_deductions: 6_000

- name: W8, 2021 joint, tax-unit input split evenly between filers, sum unchanged.
  period: 2021
  input:
    people:
      head:
        age: 40
      spouse:
        age: 40
    tax_units:
      tax_unit:
        members: [head, spouse]
        deductible_mortgage_interest_tax_unit: 9_000
    marital_units:
      marital_unit:
        members: [head, spouse]
    households:
      household:
        members: [head, spouse]
        state_code: KS
  output:
    deductible_mortgage_interest: [4_500, 4_500]
    ks_itemized_deductions: 9_000

- name: W9, 2025 head and dependent, only the dependent reports interest.
  period: 2025
  input:
    people:
      head:
        age: 40
      child:
        age: 17
        is_tax_unit_dependent: true
        home_mortgage_interest: 9_000
    tax_units:
      tax_unit:
        members: [head, child]
    marital_units:
      head_marital_unit:
        members: [head]
      child_marital_unit:
        members: [child]
    households:
      household:
        members: [head, child]
        state_code: KS
  output:
    deductible_mortgage_interest: [9_000, 0]
    ks_itemized_deductions: 9_000

- name: W10, 2017 single, premiums count at 50 percent.
  period: 2017
  input:
    state_code: KS
    employment_income: 40_000
    home_mortgage_interest: 8_000
    mortgage_insurance_premiums: 1_000
  output:
    ks_itemized_deductions: 4_500

- name: W11, 2019 single federal itemizer with phased-out premiums, full Kansas taxable income.
  period: 2019
  input:
    state_code: KS
    employment_income: 104_500
    home_mortgage_interest: 20_000
    mortgage_insurance_premiums: 1_000
    real_estate_taxes: 5_000
  output:
    tax_unit_itemizes: true
    ks_itemized_deductions: 19_125
    ks_taxable_income: 83_125

- name: W12, 2019 single federal non-itemizer with premiums takes the Kansas standard deduction.
  period: 2019
  input:
    state_code: KS
    employment_income: 40_000
    home_mortgage_interest: 2_000
    mortgage_insurance_premiums: 1_000
    real_estate_taxes: 2_000
  output:
    tax_unit_itemizes: false
    ks_itemized_deductions: 3_750
    ks_taxable_income: 34_750

From 2020 on (W2, W4, W6, W8 and W9), the head now matches main for mortgage interest and premiums. Main differs only where the PR fixes the 2018–2019 shares. W11 and W12 run the full household through tax_unit_itemizes in 2019 without a cycle.

2018 premiums. The head counts them at 50% (W3). The 2018 booklet's line C4 takes interest "from lines 8a through 8c of federal Schedule A". That booklet went to print while the premium deduction had lapsed, and its caution says not to rely on the referenced line numbers. P.L. 116-94 later restored 2018 premiums, and 79-32,109(a)(1) adopts the code "as the same may be or become effective". So the head follows the statute. Main counts these premiums too, at 100%. I see no source conflict to register.

Defect reproduction.

  • I ran the head's test files against main's formulas. ks_itemized_deductions.yaml failed 5 of 11 (Cases 3, 4, 5, 6 and 8; Case 8 gives 9,000), and ks_taxable_income.yaml failed 1 of 6 (Case 4).
  • Against 644db58a85, Cases 8–11 fail with 6,000, 0, 8,000 and 0, as the hand-off says.
  • The round 2 defect does not reproduce at main, because main counts premiums and a direct input at 100%.

Tests at the head, one file per process:

  • ks_itemized_deductions.yaml: 11 passed;
  • ks_taxable_income.yaml: 6 passed;
  • ks_2025_integration.yaml: 13 passed;
  • ks_integration.yaml: 2 passed;
  • test_source_conflicts.py: 103 passed.

ruff format --check and ruff check on the three changed Python files are clean. Main's files hold 2 and 3 cases, so the body's "9 new" and "3 new" are right.

Readers:

  • ks_itemized_deductions is read by ks_taxable_income and listed in gov.states.household.state_itemized_deductions.
  • ks_taxable_income is read by ks_income_tax_before_credits and listed in gov.states.household.state_taxable_incomes.
  • No policy formula reads either list's sums. state_itemized_deductions has no reader, and taxsim_state_taxable_income is read only by its alias state_taxable_income.
  • The new parameters have no reader outside these two formulas, and no other state reads a Kansas variable.
  • The commit only adds reads of deductible_mortgage_interest and deductible_mortgage_insurance_premiums. It changes neither.
  • A defect identical on main is the missing personal property tax input, already proposed as a follow-up.

Baseline and the population check. The diff moves Kansas outputs at baseline.

  • Against 644db58a85, round 2 restores premiums (2015–2021 and 2026 on) and any deductible_mortgage_interest value that doesn't come from home_mortgage_interest.
  • Against main, for 2020 and later, the code gives ks_itemized_deductions(main) − ks_itemized_deductions(head) = non_mortgage_interest summed over the unit's members + casualty_loss_deduction + misc_deduction. This holds whenever interest_deduction is computed rather than set as an input.
  • So on the data years, the hub can assert that identity for every Kansas unit, then compare ks_income_tax. Changes should appear only for Kansas itemizers with investment interest, casualty losses or miscellaneous deductions. A unit that breaks the identity points to an input set directly on interest_deduction (see "What I couldn't check").
  • Earlier years add the share and federal-itemizer effects.

Body claims checked:

  • the share table;
  • main's formula (charity + interest + medical + casualty + misc, plus uncapped real_estate_taxes);
  • the qualified residence interest and investment interest sentences;
  • "no separate input" for personal property taxes (only personal_property, a property value, exists);
  • the (a)(1) gate and federal_itemization_required (true 2015–2020);
  • the seven quotes, each 25 words or fewer and each matching its source;
  • the KNOWN_UNMARKED deletion;
  • the test counts;
  • the CI line;
  • the invariants.

The exceptions are the Overlap rule, the (b) trigger, and Case 7's arithmetic, above. The impact: and axiom: lines are present.

The hand-off. Its six mutants and their killing cases match my runs. Its account of the stale "Draft" line is right: that line was in the first hand-off comment, never in the body.

CI. Pull request run 41839103590 is the only workflow run at a3606f3a2e. It concluded success, with 36 of 36 jobs success and none cancelled or skipped, including Household API Partners. All 38 check runs at the commit, including codecov patch and project, are success.

Scope. Nothing is under .github/ or tests/policy/baseline/partners/. The changelog fragment is a top-level .fixed.md.

Open-PR overlap. I searched open PRs for ks_itemized_deductions, ks_taxable_income, "kansas itemized", federal_itemization_required, qualified_residence_interest, "kansas mortgage", deductible_mortgage_insurance_premiums and deductible_mortgage_interest.

METHODOLOGY_CHOICE: no. The statute's cross-reference to 163(h) settles premiums, and 79-32,109(a)(1) settles the federal years. tax_unit_itemizes stands in for whether the filer actually itemized, which is a modeling input, not a reading of the law.

What I couldn't check

  • Population impact. I have no population data, so I ran no microsimulation.
  • How the data supplies mortgage interest. The head reads mortgage interest only through deductible_mortgage_interest, which a direct input or home_mortgage_interest feeds, plus the premiums. A value set directly on interest_deduction no longer reaches Kansas. Case 3 relies on this. Its interest_deduction: 13_000 input counts in full on main (24,000). The head takes only the separate deductible_mortgage_interest_tax_unit: 9_000 input (19,000). I couldn't see whether the data years set interest_deduction directly. If they do, those units lose their mortgage interest in Kansas. The identity check above would catch them.
  • Unreachable sources. web.archive.org reset the connection. A uscode.house.gov URL I guessed returned 404. I didn't need either, because ksrevisor.gov, govinfo.gov and law.cornell.edu served the text.
  • Kansas guidance on 2018 premiums. I found no Kansas Department of Revenue guidance on claiming them after P.L. 116-94. The 2018 reading above rests on the statute.
  • The 2016 booklet. I didn't confirm its Part C line text. 2015 and 2017 match, and the current statute's (a)(2) covers 2016.

Generated by Claude Code

@DTrim99

DTrim99 commented Nov 4, 2026

Copy link
Copy Markdown
Collaborator

PR Review: Limit Kansas itemized deductions to the four K.S.A. 79-32,120(a) categories at each year's shares

PR #10095 (author: MaxGhenis), head a3606f3a2e. This is the first review. The diff covers 11 files (+275/−18):

  • a new ks_itemized_deductions formula;
  • the pre-2021 federal-itemizer gate in ks_taxable_income;
  • five new parameters under gov.states.ks.tax.income.deductions.itemized;
  • 12 new YAML cases;
  • one KNOWN_UNMARKED line removed.

No partner test file is touched.

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/ks/tax/income/deductions/itemized/
  • V/ = policyengine_us/variables/gov/states/ks/tax/income/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/

Summary

The model change is right in every claimed year. I read K.S.A. 79-32,120 on ksrevisor.gov myself. Its history ends at L. 2021, ch. 93, so no later amendment applies. I also read the 2015, 2017, 2018, 2019 and 2025 K-40 booklets. The statute defines the Kansas itemized deduction as a closed list of four federal amounts:

  • charitable contributions under § 170;
  • medical care under § 213;
  • qualified residence interest under § 163(h);
  • real and personal property taxes under § 164(a).

Each has a dated share:

  • (a)(2), 2015–2017: 100 / 0 / 50 / 50;
  • (a)(3), 2018: 100 / 50 / 50 / 50;
  • (a)(4), 2019: 100 / 75 / 75 / 75;
  • (a)(5), 2020 on: 100% each.

(a)(1)(A) limits the Kansas itemized deduction to federal itemizers "for all tax years prior to tax year 2021", and (a)(1)(B) removes that limit from 2021. Every parameter date and value matches. The formula reads the right federal amounts. The gate is applied correctly and vectorizes. All 12 new expectations and the 5 retained ones check out by hand.

No critical findings. Two should items:

  • S1. The axiom: line is not a valid form.
  • S2. The 2025 references open page 14, but the charitable rule (and the interest rule) is on page 15.

Five suggestions: pin the deductible premium amount, add Case 7's arithmetic, fix two PR-body paragraphs, and track the missing personal property tax input.

CI and branch. CI is 38/38 green at this head. The merge with main is clean (6 behind, 4 ahead), and main has not touched any file this PR changes.

Critical

None.

Should address

S1. The axiom: line is not one of the valid forms.

Where. The PR body ends with axiom: (hub files pe-parity before landing). CLAUDE.md "Axiom Parity" allows only <legal id> encoded-correct | <rulespec PR> encoded | <rulespec issue> queued | n/a: <reason>. This is a policy fix, so n/a doesn't apply, and no valid line appears elsewhere in the body.

Related issue, but not a match. rulespec-us#1672 (OPEN, pe-parity) covers the TY2020+ four-category list and TY2021 independent itemization, but it was filed for #10070. It does not match this head:

Fix. Choose one:

  • File a dispatch-ready pe-parity issue (or extend Add taxsim-emulating structural reform #1672) with the module path, the corpus citation for (a)(1)–(a)(5), the verbatim text, and companion rows drawn from the statute and booklets. Those rows should cover the 2017, 2018, 2019 and 2025 shares, the 2019 federal-itemizer gate and the premium cases. Then put axiom: <issue> queued in the body.
  • If the encoder has already run, use encoded with the rulespec-us PR.

S2. The 2025 references open page 14, but the charitable and interest rules are on page 15.

What the booklet shows. I extracted the 2025 K-40 booklet (ip25.pdf); PDF page numbers equal the printed numbers here.

  • Page 14 holds the Kansas Schedule A medical lines (1–4) and the taxes lines (5–7). That includes "Kansas allows 100% of the expenses for medical care…" and the statement that the $40,000 federal cap "does not apply for Kansas purposes".
  • Page 15 holds:
    • "Kansas allows 100% of the qualified residence interest paid as provided in section 163(h)…" (lines 8a–9, with 8d "Reserved for future use");
    • "Kansas allows 100% of the charitable contributions that qualify as deductions in section 170…" (lines 10–13);
    • line 14, the total.

Problems.

  • P/rate/charitable_contributions.yaml:13-14: "2025 Form K-40 instructions, Kansas Schedule A" points to ip25.pdf#page=14. That page doesn't show the charitable rule, so the reference doesn't corroborate the 2020-on value.
  • V/deductions/ks_itemized_deductions.py:13: "https://www.ksrevenue.gov/pdf/ip25.pdf#page=14" cites the Kansas Schedule A, which spans pages 14–15. CLAUDE.md says a variable's multi-page href takes a # PDF pages 14-15 comment above it.

Fix.

  • Change the charitable href to https://www.ksrevenue.gov/pdf/ip25.pdf#page=15.
  • Add # PDF pages 14-15 above line 13 of the variable.
  • The medical reference ("lines 1 to 4", page 14) is right as is.

Suggestions

G1. No case pins that Kansas reads the deductible premium amount, not the raw premiums.

Gap. Cases 8, 10 and 11 all fall in years when premiums are deductible, and all have AGI under $100,000. So swapping deductible_mortgage_insurance_premiums for the raw mortgage_insurance_premiums input would pass every test. That swap would be wrong in two ways:

  • it would count premiums in 2022–2025, when 163(h)(3)(E)(iv) ends the deduction and the 2025 Kansas line 8d is reserved;
  • it would count them above the phase-out.

It isn't a should, because deleting the term does fail Cases 8, 10 and 11.

Suggested cases (hand-computed from gov.irs.deductions.itemized.interest.mortgage_insurance_premiums):

  • 2025: home_mortgage_interest: 8_000, mortgage_insurance_premiums: 1_000. Expect 1 × 8,000 = 8,000.
  • 2019: employment_income: 104_500 and the same inputs. AGI is 4,500 over the $100,000 start, which is ⌈4,500 / 1,000⌉ = 5 steps × 10% = 50%. Premiums are 500, so expect 0.75 × (8,000 + 500) = 6,375.

G2. Case 7 has no arithmetic. The body says each new case carries its arithmetic, but T/ks_itemized_deductions.yaml Case 7 has none. Add # 2,000 + 4,000; the 300 non-itemizer deduction does not count. above ks_itemized_deductions: 6_000.

G3. The body misdescribes the (b) depreciation trigger. The body says (b) reduces the total "by depreciation claimed for certain credits". The statute reduces it by depreciation on property "upon which the deduction allowed by K.S.A. 79-32,221, 79-32,227, … or 79-32,256 … is or has been claimed". Those sections grant amortization deductions; 79-32,221, for example, allows "a deduction from Kansas adjusted gross income with respect to the amortization of the amortizable costs of a new refinery". The conclusion still holds (no input, no effect), so only the wording needs to change.

G4. The body's Overlap rule only works if this PR lands first.

Problem. "Whichever lands second drops its own formula edit" means that if #10038 or #10070 (both still open) lands first, this PR would drop its closed-list formula. Its share cases would then fail.

Correct rule, in either order:

G5. Personal property taxes are still missing. The statute counts "taxes on real and personal property", and every booklet has a personal-property line: 2017 C2, 2018/2019 C3 and 2025 line 6. The model has no input for them, so only real_estate_taxes enters. This isn't a regression, since main had the same gap. The author has proposed a follow-up; please open it as an issue so it is tracked.

Verified correct

Statute and booklets, all read directly:

  • K.S.A. 79-32,120 (ksrevisor.gov). The (a)(1)(A)/(B) gate and the (a)(2)–(a)(5) shares match the body's table and every parameter.

    • P/federal_itemization_required.yaml: true 2015, false 2021.
    • P/rate/medical_expenses.yaml: 0 / 0.5 / 0.75 / 1.
    • P/rate/qualified_residence_interest.yaml and P/rate/property_taxes.yaml: 0.5 / 0.75 / 1.
    • P/rate/charitable_contributions.yaml: 1.
  • K.S.A. 79-32,109(a)(1) adopts the federal code "as the same may be or become effective at any time, or from time to time, for the taxable year". So Kansas inherits:

    • the 2018 premium extension (P.L. 116-94);
    • the 2026 restoration (P.L. 119-21 § 70108);
    • the 2026 0.5% charitable floor, which charitable_deduction applies inside the § 170 amount.

    The § 68 limitation is a separate section and is correctly not applied to the four components.

  • 2017 booklet, page 14, Schedule S Part C:

    • C1 real estate taxes and C2 personal property taxes at 50%;
    • C3 "Qualified residence interest and mortgage insurance premiums…" at 50%;
    • C4 gifts to charity at 100%;
    • no medical line.

    The titles "line C3", "line C4" and "lines C1 and C2" are right.

  • 2015 booklet: line C3 takes "qualified interest on mortgage from lines 10 through 13 of federal Schedule A", which includes the premium line.

  • 2018 and 2019 booklets, page 2:

    • 2018 gives "100% charitable contributions, 50% qualified medical expenses, 50% qualified residential interest, and 50% real and personal property taxes";
    • 2019 gives the same with 75% in place of 50%;
    • both say "You may only itemize your deductions on your Kansas return if you itemized deductions on your federal return".
  • 2018 and 2019 Part C (page 14): real estate taxes come "from line 5b of federal Schedule A", which is before the cap. That supports leaving property taxes uncapped.

  • 2018 premiums. The 2018 booklet's C4 omits premiums only because the deduction had lapsed when it was printed, and its caution says not to rely on the line numbers. Counting them at 50% follows the statute, so there is no source conflict to register.

  • 2025 booklet, pages 6–7: "You may itemize your deductions on your Kansas return even if you did not itemize your deductions on your federal return."

Formula and entities:

  • What the formula reads:
    • charitable_deduction and medical_expense_deduction are TaxUnit, after ceilings and floors.
    • deductible_mortgage_interest is a Person-level value, deductible_mortgage_interest_tax_unit × home_mortgage_interest_share, which sums back to the unit total.
    • deductible_mortgage_insurance_premiums is TaxUnit, net of in_effect and the phase-out.
    • real_estate_taxes is Person-level.
  • No double count. interest_deduction = deductible_interest_expense + deductible_mortgage_insurance_premiums. The formula takes the mortgage part and the premiums, so neither counts twice, and investment interest stays out. Casualty, miscellaneous and wagering losses are excluded, as the closed list requires.
  • The gate. In V/ks_taxable_income.py:23-26, the scalar if on a parameter is fine, and True & ndarray gives an array. Reading tax_unit_itemizes for a state gate is the existing pattern (MO, NE, OK, MD, NM). tax_liability_if_itemizing is federal income_tax only, so there is no cycle.
  • Microdata. Populace treats interest_deduction as formula-owned and maps it from home_mortgage_interest, so Kansas still gets mortgage interest from the data.
  • No NH Add New Hampshire state supplementation for 2026 #10156 risk. There is no new state-gated int or enum key.

Mutation check. Removing each new value or guard fails at least one test:

  • medical 2015 = 0 → Case 6;
  • the 2018 values → Case 5;
  • the 2019 values → Case 4;
  • the 2020 values → Case 3 and unit tests 1–2;
  • federal_itemization_required 2021 = false → taxable unit test 2 and Case 6;
  • the may_itemize & guard → taxable Case 4;
  • premiums in the add → Cases 8, 10 and 11;
  • Person-level interest → Case 9.

Hand-checks (17 expectations):

Case Arithmetic Expected
Itemized unit test 1, 2021 1×0 + 1×2 + 1×9 + 1×8 19 ✓
Itemized unit test 2, 2022 12 + 9 + 2 23 ✓
Itemized Case 3, 2025 3,000 + 2,000 + 9,000 + 5,000; the 13,000 interest_deduction input and 1,000 casualty loss ignored 19,000 ✓
Itemized Case 4, 2019 1,000 + 0.75 × (2,000 + 8,000 + 4,000) 11,500 ✓
Itemized Case 5, 2018 1,000 + 0.5 × 14,000 8,000 ✓
Itemized Case 6, 2017 1,000 + 0 × 2,000 + 0.5 × (8,000 + 4,000) 7,000 ✓
Itemized Case 7, 2021 2,000 + 4,000; 300 non-itemizer amount excluded 6,000 ✓
Itemized Case 8, 2019 AGI 40,000 < 100,000; 0.75 × (8,000 + 1,000) 6,750 ✓
Itemized Case 9, 2025 1 × 9,000 (Person-level input) 9,000 ✓
Itemized Case 10, 2026 premiums back in effect, AGI 0; 8,000 + 1,000 9,000 ✓
Itemized Case 11, 2021 1 × 1,000 1,000 ✓
Taxable unit test 1, 2021 std 10,000 > itm 9,000; 90,000 − 10,000 − 4,000 76,000 ✓
Taxable unit test 2, 2021 no gate in 2021; 90,000 − 12,000 − 4,000 74,000 ✓
Taxable unit test 3, 2022 13,000 − 10,000 − 4,000 < 0 0 ✓
Taxable Case 4, 2019 non-itemizer gate forces std; 50,000 − 3,000 − 2,250 44,750 ✓
Taxable Case 5, 2019 itemizer 50,000 − 9,000 − 2,250 38,750 ✓
Taxable Case 6, 2021 non-itemizer 50,000 − 9,000 − 2,250 38,750 ✓

The 2019 Kansas standard deduction (single $3,000) and exemption ($2,250), and the 2021 single standard deduction ($3,500), match the booklets.

Housekeeping:

  • Changelog. changelog.d/ks-itemized-deductions-closed-list.fixed.md is a top-level fragment with the right type.
  • Tests. They are in variable-named YAML files.
  • Parameters. Descriptions follow the house template, and the units (/1, bool) are right.
  • Ratchet. The KNOWN_UNMARKED removal is required, because the file's conflict-sounding comment is gone. The new comments match no CONFLICT_COMMENT_RE pattern, and main has not changed test_source_conflicts.py since the merge base, so the merged tree passes the ratchet.

Axiom line assessment

Invalid (S1). axiom: (hub files pe-parity before landing) is not one of the four forms. rulespec-us#1672 is OPEN and pe-parity, but it was filed for #10070 and is scoped to TY2020+. It leaves out the 2015–2019 shares, the pre-2021 federal-itemizer gate and the premium rule, and its companion cases are #10070's. A queued issue matching this head, or an encoded PR, is needed.

CI status

gh pr checks 10095, re-checked just before writing, shows 38/38 passing at a3606f3a2e, including codecov and Household API Partners. None is pending or failed.

Branch status

The branch is 6 commits behind main and 4 ahead. git merge-tree --write-tree PolicyEngine/main a3606f3a2e is clean. Since the merge base, main has changed none of this PR's files: no Kansas files and not test_source_conflicts.py (it changed only source_conflicts/candidates.yaml in that area).

Sibling note. #10038 and #10070 (both open) edit the same lines of ks_itemized_deductions.py. See G4 for the resolution that works in either landing order.

Verdict: REQUEST_CHANGES

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Requesting changes: 0 critical, 2 should (the axiom line isn't a valid form, and rulespec-us#1672 doesn't cover the 2015-2019 shares, the gate or premiums; the 2025 charitable reference points to page 14, but the section 170 rule is on page 15). The formula and every parameter match K.S.A. 79-32,120 in each claimed year. Full report in the comment above.

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

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


Generated by Claude Code

#10038 changed ks_itemized_deductions.py to subtract the federal claim of
right deduction from the federal total and added a case for it. The
resolution keeps this branch's closed-list formula, which already leaves
that deduction out. Its reference tuple already has the statute line #10038
added. Both branches' YAML cases stay. #10038's case now sets
home_mortgage_interest: 9_000 in place of interest_deduction: 9_000, because
the closed list reads qualified residence interest, not the federal
interest total. Its expectations (14,000 and 12,000) are unchanged.

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

The 2025 K-40 booklet gives the Kansas Schedule A medical and tax rules on
page 14 and the qualified residence interest and charitable rules on page
15. The charitable share's reference now opens page 15 and names lines 10
to 13. The variable's Schedule A reference spans both pages, so it gets a
"PDF pages 14-15" comment.

No case told the deductible mortgage insurance premiums apart from the raw
premium input. Case 12 (2025, when 163(h)(3)(E)(iv) bars premiums) and Case
13 (2019, AGI 4,500 above the phase-out start) now do. Case 7 gets its
arithmetic.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review
9a2ccc7

This round merges main and resolves DTrim99's review 5775421275 (comment 6641541106) at a3606f3a2e. I read the hub's last review (comment 6635396280) too, and its findings overlap DTrim99's suggestions. No one else pushed to the branch while I worked.

1. Main

7cc2e5450a merges main at ba9bfdea75. Two files conflicted, and I applied the review's G4 rule:

2. Findings

I checked each finding against its source before relying on it. G1's gap shows in mutant MA below: only the two new cases catch it.

  • S1: the axiom line. The review saw axiom: (hub files pe-parity before landing). The body now reads rulespec-us#1672 queued, extended with a comment (2015–2019 shares, the pre-2021 federal-itemizer condition, and mortgage-insurance companion cases), so the line changed after the review. I kept it and edited no issue. This session can't read rulespec-us, so I couldn't check the extension. Draft text under "Axiom" below.
  • S2: the 2025 page anchors. I fetched the 2025 K-40 booklet (ip25.pdf). File page 14 holds the medical care rule (section 213) and the property tax rule. File page 15 holds qualified residence interest (section 163(h)), line 8d "(Reserved for future use)" and the charitable rule (section 170).
    • charitable_contributions.yaml:13-14 now opens page 15, titled "Kansas Schedule A, lines 10 to 13".
    • ks_itemized_deductions.py:13 gains # PDF pages 14-15 above the ip25 href.
    • The medical reference stays on page 14, and no other Kansas file cites ip25.
  • G1: the premium amount. I added the hub's Cases 12 and 13 (ks_itemized_deductions.yaml:146-154 and :158-169), worked by hand from the code:
    • Case 12, 2025. mortgage_insurance_premiums.in_effect is false from 2022 until 2026, so the deductible premiums are 0 and the deduction is 1 × 8,000 = 8,000.
    • Case 13, 2019. AGI 104,500 exceeds the 100,000 start by 4,500, so ceil(4,500 / 1,000) = 5 steps × 0.1 = 50%. That makes the premiums 500 and the deduction 0.75 × (8,000 + 500) = 6,375. The case also asserts deductible_mortgage_insurance_premiums: 500.
    • I read 26 U.S.C. 163(h)(3) on LII: (E)(ii) reduces by "10 percent of such amount for each $1,000", (E)(iv) ends premiums after December 31, 2021, and (F)(i)(III) turns (E)(iv) off. Section 70108(b) of P.L. 119-21 applies that to "taxable years beginning after December 31, 2025".
  • G2: Case 7. # 2,000 + 4,000; the 300 non-itemizer deduction does not count. sits above its expectation (ks_itemized_deductions.yaml:92).
  • G3: the (b) trigger. I read K.S.A. 79-32,120(b) on ksrevisor.gov. It reduces the total by depreciation on property "upon which the deduction allowed by K.S.A. 79-32,221" or seven other sections "is or has been claimed". 79-32,221 and 79-32,256 each grant "a deduction from Kansas adjusted gross income" for amortization; the hub's round 2 read all eight. No Kansas file models them. The body's (b) sentence now says this.
  • G4: the overlap rule. Applied in the merge above. The body's Overlap section now says Model the claim of right repayment deduction and credit (26 U.S.C. 1341) and North Carolina's rules that depend on it #10038 merged and how the merge resolved it. It gives the rule for Count gambling winnings in federal gross income and fix the wagering losses deduction #10070, which conflicts with this head in the same 8 files it conflicts with on main, both Kansas files among them.
  • G5: personal property taxes. Not in this PR, and I opened no issue, as the brief asks. Proposed follow-up below.

3. Source conflicts

No finding turns on two sources that disagree, so I added no candidates.yaml entry.

  • The page anchors were citation errors.
  • The premium rule follows K.S.A. 79-32,120's cross-reference to section 163(h). The 2025 booklet reserves line 8d, which fits the federal lapse, so it agrees.

Rulings for the hub: none. No change reverses a reading in the body, candidates.yaml or an earlier review.

4. Axiom

The body's line stays as it is. If the hub's rulespec-us#1672 extension doesn't already carry them, this addendum covers this round's new companion rows:

Addendum for #10095 at 9a2ccc7705, module us-ks/statutes/79-32,120.yaml (or the path the extension names). Two more companion rows, computed from K.S.A. 79-32,120(a)(4)-(5), 26 U.S.C. 163(h)(3)(E) and the 2019 and 2025 K-40 booklets:

  • TY2025, single, home mortgage interest $8,000 and mortgage insurance premiums $1,000: premiums are not qualified residence interest after 2021 ((E)(iv); 2025 Kansas Schedule A line 8d is reserved). Kansas itemized deduction: $8,000.
  • TY2019, single, AGI $104,500, the same inputs: (E)(ii) cuts the premiums 50% to $500. Kansas itemized deduction under (a)(4): 0.75 × $8,500 = $6,375.

I couldn't check the corpus citation or the module path, because rulespec-us is outside this session's access.

5. Mutants

One process each on ks_itemized_deductions.yaml, restoring the files after each (git status clean).

Mutant Undoes Result Killed by
MA: raw mortgage_insurance_premiums in place of deductible_mortgage_insurance_premiums at ks_itemized_deductions.py:32 (the hub's M7) G1 2 failed, 12 passed Case 12 (9,000 vs 8,000) and Case 13 (6,750 vs 6,375)
MB: #10038's case back to interest_deduction: 9_000 G4 1 failed, 13 passed #10038's case (3,000 vs 12,000)
MC: main's formula (#10038's version) in place of this PR's the PR as a whole, for the body's count 6 failed, 8 passed Cases 3 (24,000 vs 19,000), 4 (15,000 vs 11,500), 5 (15,000 vs 8,000), 6 (15,000 vs 7,000), 8 (9,000 vs 6,750) and 13 (8,500 vs 6,375)

Under MA, the other 12 cases, including all 11 from before this round, pass. That reproduces G1.

S2 and G2 change a reference and a comment, so no test can fail on them. I checked S2 against the booklet instead.

6. Tests at 9a2ccc7705

One file per process.

File Result
ks_itemized_deductions.yaml 14 passed
ks_taxable_income.yaml 6 passed
ks_2025_integration.yaml 13 passed
ks_integration.yaml 2 passed
ks_income_tax_before_credits.yaml 8 passed
test_source_conflicts.py 103 passed

ruff format --check and ruff check pass on ks_itemized_deductions.py.

CI: Pull request run 42643458213 at 9a2ccc7705 was queued when I posted this. I didn't wait for it, so I can't yet say whether it skipped or cancelled any job.

7. Body

Every section now matches this head:

Judgment calls

  • Who changed the axiom line. The line changed between DTrim99's review and this round, presumably by the hub. I treated S1 as handled by that change and drafted only an addendum.
  • Reference title. I renamed the charitable reference "Kansas Schedule A, lines 10 to 13" to match the medical reference's "lines 1 to 4" style.
  • The (b) range. The new (b) sentence names the eight sections as "K.S.A. 79-32,221 to 79-32,256", the span (b) lists, rather than all eight numbers.
  • Mutant MC. It swaps in main's formula, which tells the body how many cases need this PR. It isn't a mutant of a fix from this round.

Follow-up issue proposed

  • Add a personal property tax input for Kansas itemized deductions. K.S.A. 79-32,120(a) counts "taxes on real and personal property" under section 164(a), and every K-40 booklet has a personal property line. The model has only real_estate_taxes, so Kansas itemizers with personal property taxes get too small a deduction.

DISMISSAL MAP

  • Critical: none in the review.
  • S1 (axiom line not a valid form): the body's line changed after the review to rulespec-us#1672 queued, extended with a comment, and I kept it. Addendum drafted above; I couldn't check rulespec-us from this session.
  • S2 (2025 references open page 14): fixed at charitable_contributions.yaml:13-14 (page 15, lines 10 to 13) and ks_itemized_deductions.py:13 (# PDF pages 14-15).
  • G1 (no case pins the deductible premiums): fixed with Cases 12 and 13 (ks_itemized_deductions.yaml:146-154, :158-169); mutant MA dies on both.
  • G2 (Case 7 has no arithmetic): fixed at ks_itemized_deductions.yaml:92.
  • G3 (body misdescribes the (b) trigger): fixed in the body.
  • G4 (the overlap rule works only if this PR lands first): applied in the merge at 7cc2e5450a, with the body updated; mutant MB dies on 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.
  • G5 (personal property taxes missing): not in this PR; follow-up proposed above.

Generated by Claude Code

@DTrim99

DTrim99 commented Nov 9, 2026

Copy link
Copy Markdown
Collaborator

PR Review (round 2): Limit Kansas itemized deductions to the four K.S.A. 79-32,120(a) categories at each year's shares

PR #10095 (author: MaxGhenis), head 9a2ccc7705. Our round 1 review (2026-11-04, head a3606f3a2e) requested changes for two should items (S1, S2) and five suggestions (G1–G5).

Since then the branch has gained:

Path prefixes:

  • P/ = policyengine_us/parameters/gov/states/ks/tax/income/deductions/itemized/
  • V/ = policyengine_us/variables/gov/states/ks/tax/income/
  • T/ = policyengine_us/tests/policy/baseline/gov/states/ks/tax/income/

Summary

Both should items are fixed, and so are G1–G4.

  • S1. The axiom: line now names rulespec-us#1672 as queued. The issue is OPEN and labelled pe-parity. Its 2026-11-05 extension comment covers this PR's scope:

    • the 2015–2019 shares;
    • the pre-2021 federal-itemizer condition;
    • mortgage insurance premiums.

    It also carries companion rows for every round 1 expectation, plus the two boundary rows that are now this PR's Cases 12 and 13.

  • S2. The 2025 charitable reference now opens page 15, and the variable's 2025 href has a # PDF pages 14-15 comment. I re-extracted the 2025 booklet to confirm both.

The model change is the same as in round 1. At the head, ks_itemized_deductions.py differs from a3606f3a2e only by that comment. The other changes are references, YAML cases, and the merge.

Both merge blocks are resolved correctly. The new and changed expectations all check out by hand.

CI and branch. CI is 38/38 green at the head. The merge with current main is clean.

Open items. Nothing critical or should is open. Three suggestions remain:

Prior findings

Finding Status Evidence
S1. The axiom: line was not a valid form Fixed The body now ends axiom: TheAxiomFoundation/rulespec-us#1672 queued, extended with a comment (...). #1672 is OPEN and pe-parity. Its extension comment (MaxGhenis, 2026-11-05) is dispatch-ready; details under Axiom.
S2. The 2025 references opened page 14, but the charitable rule is on page 15 Fixed P/rate/charitable_contributions.yaml:13-14 is now "2025 Form K-40 instructions, Kansas Schedule A, lines 10 to 13" → ip25.pdf#page=15. V/deductions/ks_itemized_deductions.py:13 gains # PDF pages 14-15 above the #page=14 href, as CLAUDE.md requires. I re-extracted ip25.pdf: file page 15 has "Kansas allows 100% of the charitable contributions that qualify as deductions in section 170 of the federal internal revenue code", lines 10–13 and line 14, the total. Page 14 keeps the medical rule (lines 1–4) and the taxes rule (lines 5–7). The fix is reference-only, so no test can pin it.
G1. No case told deductible premiums from raw premiums Fixed New Cases 12 (2025) and 13 (2019, AGI 104,500) at T/ks_itemized_deductions.yaml:146-169. If deductible_mortgage_insurance_premiums is swapped for raw mortgage_insurance_premiums, Case 12 gives 9,000 (expected 8,000) and Case 13 gives 0.75 × 9,000 = 6,750 (expected 6,375). Both fail.
G2. Case 7 had no arithmetic Fixed # 2,000 + 4,000; the 300 non-itemizer deduction does not count. now sits at T/ks_itemized_deductions.yaml:92.
G3. The body misdescribed the (b) depreciation trigger Fixed The body now says (b) reduces the total by depreciation "on property that also took one of the eight Kansas amortization deductions it lists (K.S.A. 79-32,221 to 79-32,256)". That matches the statute's "upon which the deduction allowed by K.S.A. 79-32,221 … is or has been claimed".
G4. The Overlap rule worked only if this PR landed first Fixed Merge 7cc2e5450a applies the rule in the order that actually happened, with #10038 landing first. The body's Overlap section now describes that merge and gives the rule for #10070, which is still open. If #10038's case goes back to interest_deduction: 9_000, it gives 3,000 (expected 12,000), so the input change is pinned.
G5. Personal property taxes have no input Open (suggestion, deferred) The author proposes a follow-up, but no policyengine-us issue exists (searches for "Kansas personal property" and "personal property tax input" return nothing). The #1672 extension does tell the encoder to include personal property taxes ("Encode the law, not the PE-US value"). Not a regression: main has the same gap.

New findings

Critical

None.

Should address

None.

Suggestions

N1. Two stale lines in the PR body.

  • Tests section. It says "the Pull request run at 9a2ccc7705, 42643458213, is pending". It has now finished, 38/38 passing.
  • Sources section. "confirmed against the sources in the round 2 review" points to a review that a reader of this PR can't identify. The PR has one posted round 1 review (DTrim99, 2026-11-04). Name the review by date, or drop the clause.

Also fill the impact: line before landing.

N2. A short addendum to rulespec-us#1672. The extension comment was written at a3606f3a2e. It still matches this head's behavior, because:

  • the formula is unchanged;
  • its "law-derived boundary rows" 1 and 2 (2025: 8,000; 2019 at AGI 104,500: 6,375) are exactly the new Cases 12 and 13.

The PR's YAML now also carries #10038's case from the merge:

  • 2025: home mortgage interest 9,000, real estate taxes 3,000, claim of right deduction 5,000;
  • expected Kansas itemized deduction 12,000.

Neither #1672 nor #10038's issue (rulespec-us#1657, which covers 26 U.S.C. 1341 and N.C. Gen. Stat. 105-266.2) has this row. The author drafted an addendum in the hand-off comment but could not post it. Posting it, with the claim-of-right row added under the "only the listed Kansas components" invariant, would keep the issue row-for-row with the head. This isn't a should, because the round's changes were not material to behavior.

G5 (carried over). Open the proposed issue for a Kansas personal property tax input, so the gap is tracked in this repository as well as in #1672.

Verified

Merge block 1: T/ks_itemized_deductions.yaml.

Merge block 2: V/deductions/ks_itemized_deductions.py.

Fix commit 9a2ccc7705. It touches only:

  • the charitable reference;
  • the variable's reference comment;
  • three YAML additions: Case 7's comment and Cases 12 and 13.

All other PR files are byte-identical to round 1 in their PR-side changes:

  • the changelog;
  • federal_itemization_required;
  • the medical, property tax and qualified residence interest shares;
  • ks_taxable_income.py and its YAML;
  • the KNOWN_UNMARKED deletion.

Law behind the new comments:

  • Case 12. 26 U.S.C. 163(h)(3)(E)(iv)(I) ends premiums "paid or accrued after December 31, 2021". P.L. 119-21 § 70108(b) restores them for "taxable years beginning after December 31, 2025". The 2025 Kansas Schedule A line 8d reads "(Reserved for future use)" (ip25.pdf page 15, re-extracted).
  • Case 13. (E)(ii) reduces the premiums by "10 percent of such amount for each $1,000 … (or fraction thereof)" of AGI over $100,000. The model applies np.ceil(excess / increment) × rate in deductible_mortgage_insurance_premiums.py:40-43.

Hand-checks of every new or changed expectation:

Case Arithmetic Expected
Itemized Case 7, 2021 (comment added) 2,000 + 4,000; the 300 non-itemizer amount excluded 6,000 ✓
Itemized Case 12, 2025 premiums in_effect false 2022–2025, so 0; 1 × 8,000 8,000 ✓
Itemized Case 13, 2019, deductible_mortgage_insurance_premiums AGI 104,500; ⌈4,500 / 1,000⌉ = 5 × 10% = 50%; 1,000 × 0.5 500 ✓
Itemized Case 13, 2019, ks_itemized_deductions 0.75 × (8,000 + 500) 6,375 ✓
#10038 case, 2025, itemized_deductions_less_salt 9,000 + 5,000 14,000 ✓
#10038 case, 2025, ks_itemized_deductions 9,000 + 3,000; the claim of right excluded 12,000 ✓

New cases kill the obvious mutants.

PR body counts are correct:

  • ks_itemized_deductions.yaml: 14 cases, 11 new.
  • ks_taxable_income.yaml: 6 cases, 3 new.
  • Main's formula fails Cases 3 (24,000), 4 (15,000), 5 (15,000), 6 (15,000), 8 (9,000) and 13 (8,500), and taxable Case 4. Case 12 passes under both formulas, since premiums are 0 in 2025.

Main-side guards on the tree merged with current main:

Round 1 verification still holds. The formula and parameters are unchanged, so the round 1 statute, booklet and 17-expectation checks stand.

Axiom

Valid. The line is axiom: TheAxiomFoundation/rulespec-us#1672 queued, extended with a comment (2015–2019 shares, the pre-2021 federal-itemizer condition, and mortgage-insurance companion cases); ….

Issue status. #1672 is OPEN and labelled pe-parity, last updated 2026-11-05.

What the extension comment contains:

  • Module and corpus. The target module is us-ks/statutes/79/79-32/120.yaml, with corpus citations: the existing us-ks/statute/2026/79-32-120/block-1 candidate, plus historical expressions flagged for verification.
  • Verbatim law:
    • the historical (a)(2)–(a)(4) shares;
    • (a)(1)(A)–(B);
    • 163(h)(3)(E)(i)–(iv);
    • P.L. 115-123, 116-94 and 116-260, the premium extensions;
    • P.L. 119-21 § 70108;
    • K.S.A. 79-32,109(a)(1).
  • Required outputs. The potential amount is kept separate from the election.
  • Companion rows. All 17 round 1 expectations, plus three boundary rows from the statute. Two of those boundary rows equal this head's new Cases 12 and 13.
  • Blocker. It is recorded: verifying and ingesting the historical signed sources, then re-pinning.

Match with the head. The issue matches the head's behavior. The one row it lacks is #10038's merged claim-of-right case, covered by suggestion N2.

CI

gh pr checks 10095, re-checked just before writing, shows 38/38 passing at 9a2ccc7705. None is pending or failed.

Branch

The branch is 6 commits behind main and 6 ahead. git merge-tree --write-tree PolicyEngine/main 9a2ccc7705 is clean (tree 08d78c9445).

Main's 6 newer commits touch none of this PR's files and nothing it reads:

Sibling note. #10070 (open) edits the same lines of ks_itemized_deductions.py. The body gives the rule for resolving it in either landing order: keep this formula, drop #10070's formula edit, and keep both PRs' YAML.

Verdict: APPROVE

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Round 2: both shoulds (axiom line; 2025 page anchors) are fixed, the mortgage-insurance mutant is now pinned, both merge resolutions are correct, and CI is green. Three optional suggestions remain.

@MaxGhenis
MaxGhenis merged commit 630eeb9 into main Nov 10, 2026
38 checks passed
@MaxGhenis

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10095 at 9a2ccc77053577d8fd2e71160b4bab889c8d425a (squash): Limit Kansas itemized deductions to the four K.S.A. 79-32,120(a) categories at each year's shares.

  • Reviews:
    • An independent review requested changes at 644db58a85, and a second approved a3606f3a2e.
    • DTrim99 requested changes at a3606f3a2e: the form of the axiom: line, the coverage of Encode Kansas's closed itemized deduction list and exclusion of wagering losses (PE #10070) TheAxiomFoundation/rulespec-us#1672, and the page of the 2025 charitable reference. 9a2ccc7705 answers each and adds two mortgage insurance premium cases, and DTrim99 approved 9a2ccc7705.
    • An independent review of the changes since a3606f3a2e agreed at 9a2ccc7705. It recomputed each new expected value from the parameter files and found that the merge of main keeps both sides in the three files main also changed.
  • Gates checked live at merge:
    • gh pr checks exits 0;
    • the latest Pull request run at the head, 42643458213, passed all 36 jobs and tested this head merged into main at 3e5a1dff6b;
    • MERGEABLE;
    • not a draft;
    • no reviewer's latest review requests changes;
    • the semantic-overlap guard: main has changed no area this PR touches since 3e5a1dff6b;
    • the merge pins the head.
  • Impact (default dataset, 2026, 9a2ccc7705 against main at ba9bfdea75): we estimate that the change lowers Kansas itemized deductions by $167.8m and raises Kansas income tax by $9.2m (0.17%), across 171 sampled tax units (175,793 weighted). Federal income tax and household benefits don't change.
  • Next: Count gambling winnings in federal gross income and fix the wagering losses deduction #10070 edits the same Kansas lines and keeps this PR's formula when it next merges main.
  • Axiom: TheAxiomFoundation/rulespec-us#1672 queued, now with a companion row for the claim of right case.

🤖 Generated with Claude Code

@MaxGhenis
MaxGhenis deleted the ks-itemized-deductions-closed-list branch November 10, 2026 04:42
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