Repository navigation
Add back income excluded under sections 911, 931 and 933 in modified AGIs (student loan interest and 11 siblings) - #9817
Merged
Conversation
Section 221(b)(2)(C) defines modified AGI as adjusted gross income determined "(i) without regard to this section and sections 85(c), 911, 931, and 933, and (ii) after application of sections 86, 135, 137, 219, and 469". The model got three parts wrong: - Section 931 (specified_possession_income) was subtracted like any other above-the-line deduction. It is now in excluded_alds with section 933. - Section 911: income inputs are net of foreign_earned_income_exclusion, and nothing added it back. The formula now adds it, split between spouses like the tax unit's deductions. - Unemployment compensation was dropped entirely (excluded_gross_income_ sources), a misreading of "without regard to section 85(c)": only the 2020 exclusion is disregarded, so all of it counts. The formula reads the untaxed amount, which also avoids the cycle through AGI that the old exclusion side-stepped. The parameter is removed. excluded_alds also follows the amendment history: section 222 until 2020 (Pub. L. 116-260) and section 199 from 2005 through 2017 (Pub. L. 108-357, Pub. L. 115-97). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ten provisions phase out on "adjusted gross income increased by any amount excluded from gross income under section 911, 931, or 933": 24(b)(1) (CTC, and the 2021 ARPA phase-out through Schedule 8812 line 3), 25A(d)(2) (education credits), 25B(e) (saver's credit), 25E(b)(3) and 30D(f)(10)(C) (clean vehicle credits), 151(d)(5)(C) (senior deduction), 163(h)(4)(C) (car loan interest), 164(b)(7)(B) (SALT cap), 224(b)(2)(B) (tips) and 225(b)(2)(B) (overtime). Only the senior deduction added the exclusions back; the rest read plain AGI. agi_plus_section_911_931_933_exclusions computes the amount once (Schedule 1-A line 3), and every one of them reads it. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Taxable Social Security, 26 U.S.C. 86(b)(2)(A): determined without regard to section 911. The revoked-deductions list already covered sections 931 and 933; the 911 exclusion is netted out of the income inputs, so taxable_ss_magi adds it back. - Net investment income tax, 26 U.S.C. 1411(d): adds back the section 911(a)(1) exclusion (Form 8960 line 13 worksheet). The model's one exclusion input also holds the housing amounts, which the worksheet leaves out; niit_magi documents that. - Medicare IRMAA, 42 U.S.C. 1395r(i)(4)(A): determined without regard to IRC sections 135, 911, 931 and 933, plus tax-exempt interest. All four are added back, for both the modeled and the provided lag years. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Differential: on every return, student-loan MAGI (built from gross income sources) equals AGI plus the student loan interest deduction, the section 911, 931 and 933 amounts and any unemployment compensation section 85(c) excluded; the shared modified AGI equals AGI plus the three amounts. Shifting income into an excluded category at otherwise fixed AGI raises student-loan MAGI and taxable_ss_magi by exactly the amount shifted, never raises the student loan interest deduction, and moves every phase- out the shared modified AGI feeds in one direction only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#9808 adds the section 1411(d) add-back with a separate, overridable section 911(a)(1) amount, which is more precise than adding the whole foreign_earned_income_exclusion input. Restore niit_magi and its tests to main and drop the NIIT assertion from the property test. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… 135 - Modified AGIs add back section 911 in full: Form 2555 lines 45 and 50 (Schedule 8812 line 2b, Schedule 1-A lines 2b and 2c, Pub. 970 Worksheet 4-1, Form 8936 lines 1c and 1d, Form 8962 Worksheet 1-1 line 3). New input section_911_excluded_income carries that amount; it defaults to foreign_earned_income_exclusion, which stays the section 911(f) stacking amount net of disallowed deductions. The shared modified AGI, student-loan MAGI, Social Security MAGI, IRMAA MAGI and the Medicaid/ACA modification list use it. - Clean vehicles: 30D(f)(10) and 25E(b) allow the credit when the lesser of this year's and the preceding year's modified AGI is within the limit. New inputs clean_vehicle_credit_prior_year_magi (defaults to this year's modified AGI) and clean_vehicle_credit_prior_year_filing_status (defaults to this year's status); each year is tested against its own status's limit (26 CFR 1.30D-4(b)(3), 1.25E-1(c)(3)). - IRMAA keeps adding back the section 135 exclusion wherever it is recorded, because AGI deducts it there: a bond in a child's name does not qualify (135(c)(1)(B), Form 8815), so an amount on a dependent is the filer's. A filer-only add-back would leave the dependent-recorded deduction in AGI and give 99,000 for 100,000 of wages. - Tests: the reviewer's reproductions as YAML cases, Hypothesis invariants for the gross and net section 911 amounts, the lesser-of test and the IRMAA identity on provided and computed AGI. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts resolved keeping both sides: - student_loan_interest_ald_magi: #9801's tax_unit_non_dep_add (with filer_amounts_recorded_on_dependents) for the tax unit deduction sum, and this branch's section_911_excluded_income add-back. - foreign_earned_income_exclusion documentation: both notes (modified AGIs add back section_911_excluded_income; NIIT adds back only the section 911(a)(1) part, niit_magi_section_911_addition). taxable_ss_magi and test_ald_determinism merged cleanly with both changes. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
42 U.S.C. 1395r(i)(4) reads the beneficiary's own return. After #9801, adjusted gross income sums person-level deductions over the head and spouse, plus every member for gov.irs.ald.filer_amounts_recorded_on_ dependents. The IRMAA MAGI now sums its person-level add-backs with the same rule (tax_unit_non_dep_add): - Tax-exempt interest counts for the head and spouse only. A dependent's interest is on the dependent's own return, like the rest of the dependent's income; main summed it over every member. - The section 135 exclusion counts for every member, because #9801 lists it: a bond in a child's name does not qualify (135(c)(1)(B), Form 8815), so an amount recorded on a dependent is the parent's exclusion, and AGI deducts it. Head and spouse only would give 99,000 for a parent with 100,000 of wages and 1,000 recorded on the child. - The 2015-2016 fallback applies the same rule with the benefit year's roles, reading stored values only. Tests: YAML cases for a dependent's tax-exempt interest in a modeled and an unmodeled lag year; the Hypothesis IRMAA identity now draws spouse and dependent tax-exempt interest and sets lag-year ages in both paths (age defaults to 40, which would make a child a second adult). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Section 164(b)(7)(B)(iv) and the 2025 Schedule A worksheet add Form 2555 lines 45 and 50 to SALT MAGI. An exclusion can reduce itemized deductions even when the choice to itemize holds, so fixed taxable income was an invalid premise. Preserve equality for election changes and unchanged deductions, assert nondecreasing taxable income for same-choice exclusion pairs, and reuse the grid simulation for a hand-calculated SALT regression and independent form comparisons. Production formulas are unchanged.
Contributor
Author
|
US + core hub merge audit, us#9817 at
|
MaxGhenis
added a commit
that referenced
this pull request
Oct 7, 2026
Resolve salt_cap.py by keeping #9817's 164(b)(7)(B)(iv) modified AGI and applying the separate-filer fraction to the reduction it produces. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 7, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The student loan interest deduction phases out on a modified AGI that, by statute, adds back income excluded under 26 U.S.C. 911, 931 and 933. PolicyEngine-US got three parts of that definition wrong. Auditing the other modified AGIs turned up the same gap in eleven more places. Each now adds back what its statute says.
Every modified AGI adds back the full section 911 amount, Form 2555 lines 45 and 50, as the IRS worksheets do. The rate stacking of #9761 keeps its smaller amount. The clean vehicle credits apply their test over two years. IRMAA sums person-level amounts over the same members as AGI does after #9801.
Statute
26 U.S.C. 221(b)(2)(C) (current text, Cornell LII; same as the pinned Axiom corpus row
us/statute/26/221/b/2):The amendment notes give the history of clause (i):
Clause (ii) lists no section 220.
The bug
student_loan_interest_ald_magibuilds MAGI from gross income sources, less every above-the-line deduction not inexcluded_alds. That list held only the deduction itself andpuerto_rico_income(933). Three things followed:specified_possession_incomeis ingov.irs.ald.deductions, so it was subtracted. A bona fide resident of Guam, American Samoa or the Northern Mariana Islands had a lower MAGI than the law gives, and a larger deduction in the phase-out range.foreign_earned_income_exclusionis not an above-the-line deduction. The income inputs are already net of it: its documentation says "Other income inputs are the amounts left after the exclusion", and Tax Form 2555 filers' income at stacked rates (26 U.S.C. 911(f)) #9761 adds it back only for the 911(f) rate stacking. Student-loan MAGI therefore never contained the excluded income. Adding that input back would not be enough, either. Tax Form 2555 filers' income at stacked rates (26 U.S.C. 911(f)) #9761 defines it as line 2c of the Foreign Earned Income Tax Worksheet: Form 2555 lines 45 and 50, less "any itemized deductions or exclusions you couldn't claim because they are related to excluded income" (2025 Form 1040 instructions, p. 37). Every modified AGI worksheet adds lines 45 and 50 without that subtraction:excluded_gross_income_sourcesremovedtaxable_unemployment_compensationfrom MAGI in every year ("Unemployment compensation is not considered in MAGI"). Clause (i) says "without regard to section 85(c)", the 2020-only exclusion of up to $10,200. It does not remove unemployment compensation, and Pub. 970 (2024) Worksheet 4-1 starts from total income (Form 1040 line 9), which includes it. So all unemployment compensation counts, including the 2020 excluded part. The exclusion probably also side-stepped a cycle (taxable UC depends on AGI, which includes this deduction). Readingunemployment_compensationinstead is cycle-free, and exact for every year the model supports (2015 on). It is not a claim about all years: TY2009, outside the supported range, had a separate $2,400 unemployment compensation exclusion.The fix
Section 911 amount. New input
section_911_excluded_incomeholds Form 2555 lines 45 and 50 (both spouses' forms on a joint return). Every modified AGI below adds back this amount. When not provided, it equalsforeign_earned_income_exclusion, which is exact when no deductions are disallowed.foreign_earned_income_exclusionkeeps #9761's meaning (worksheet line 2c), and the rate stacking still reads it. So does the default of #9808'sniit_magi_section_911_addition, which follows section 1411(d)'s own definition.Student-loan MAGI (
student_loan_interest_ald_magi.py,excluded_alds.yaml):specified_possession_incomejoinsexcluded_alds.section_911_excluded_incomeback, split between spouses like the tax unit's deductions.unemployment_compensationin place oftaxable_unemployment_compensation.excluded_gross_income_sources.yamlis removed; nothing else read it.excluded_aldsfollows the amendments, with §222 (capped_qualified_tuition_expenses_ald) through 2020 and §199 (domestic_production_ald) for 2005–2017.Shared modified AGI. New variable
agi_plus_section_911_931_933_exclusionsaddsadjusted_gross_income,section_911_excluded_income,specified_possession_incomeandpuerto_rico_income(Schedule 1-A line 3). Ten provisions define MAGI exactly this way, and each now reads it (table below).additional_senior_deduction_magialready computed this sum withforeign_earned_income_exclusion. It now reads the shared variable, so its results change only wheresection_911_excluded_incomeis provided and differs.Clean vehicles, two years. Sections 30D(f)(10)(A) and 25E(b)(1) deny the credit only if the lesser of this year's and the preceding year's modified AGI exceeds the limit. The model had no prior-year input, so the add-back alone would deny buyers the statute admits. For example, in 2024 a single filer has AGI of $140,000 and a $20,000 section 911 exclusion, so modified AGI is $160,000, but their 2023 modified AGI was $130,000. Two new inputs, matching Form 8936 lines 4 and 5, carry the preceding year:
clean_vehicle_credit_prior_year_magi(default: this year's modified AGI, with the add-backs);clean_vehicle_credit_prior_year_filing_status(default: this year's filing status).Each year's modified AGI is compared with the limit for that year's filing status. That is the special rule of 26 CFR 1.30D-4(b)(3) and 1.25E-1(c)(3) for a change in filing status; with one status in both years it is the statute's lesser-of test. The prior-year input is a modified AGI, so the add-backs apply in both years.
Why default to the current year: policyengine-core carries inputs forward to later years and uprates them from earlier years, but never fills an earlier year from a later one (
Simulation._calculate,auto_carry_over_input_variables). Readingperiod.last_yearin a single-year household or microsimulation would see zero income and pass every buyer. With the defaults, the current year alone decides unless the preceding year is supplied. A buyer whose prior-year modified AGI qualifies is admitted once that amount is provided. Without it, a buyer pushed over the limit by the add-back is denied, as on main for any buyer over the limit; the dataset has no section 911, 931 or 933 income, so no record is affected.Other MAGIs:
taxable_ss_magiadds back §911 (section_911_excluded_income).medicare_irmaa_magi_two_years_prioradds back §§135, 911, 931 and 933, and sums person-level amounts over the same members as AGI does (next paragraph).gov.hhs.medicaid.income.modification, read bymedicaid_magi,medicaid_magi_personandaca_magi) listssection_911_excluded_incomein place offoreign_earned_income_exclusion(Form 8962 Worksheet 1-1 line 3). Results change only where the new input is provided and differs.niit_magi(§1411(d)) is Add the section 911(a)(1) exclusion to NIIT modified AGI (26 U.S.C. 1411(d)) #9808's, now merged; this PR does not touch it.IRMAA and dependents. IRMAA reads the beneficiary's own return, joint with a spouse if any (42 U.S.C. 1395r(i)(4)). After #9801, AGI sums person-level deductions over the head and spouse, plus every member for amounts in
gov.irs.ald.filer_amounts_recorded_on_dependents. The IRMAA add-backs now use that same rule (tax_unit_non_dep_add):irs_gross_incomeleaves off. Main summed it over every member. This changes main's behavior, for households whose lag-year return has a dependent with tax-exempt interest.us_bonds_for_higher_ed): every member. Keep dependents' above-the-line deductions off the filer's AGI #9801 lists it infiler_amounts_recorded_on_dependents. A bond issued in the name of a child under 24 does not qualify (§135(c)(1)(B); Form 8815), so an amount recorded on a dependent is the parent's exclusion for the dependent's tuition, and AGI deducts it. An add-back over the head and spouse only would leave that deduction in place. A parent with $100,000 of wages and $1,000 recorded on the child has AGI of $99,000, and IRMAA MAGI would be $99,000 instead of $100,000. Reading the same parameter keeps the add-back equal to the deduction if the list changes.Audit of modified AGI definitions
I read each statute (Cornell LII, plus 42 U.S.C. 1395r for IRMAA) and each variable. Every "911" add-back below is Form 2555 lines 45 and 50.
student_loan_interest_ald_magictc_phase_out,ctc_arpa_uncapped_phase_outeducation_credit_phase_out,lifetime_learning_credit_phase_outsavers_credit_personused_clean_vehicle_credit_eligiblenew_clean_vehicle_credit_eligibleauto_loan_interest_deductionsalt_captip_income_deductionovertime_income_deductionadditional_senior_deduction_magitaxable_ss_maginiit_magimedicare_irmaa_magi_two_years_priormedicaid_magi,aca_magicdcc_ratequalified_adoption_assistance_expense(input)us_bonds_for_higher_ed(input)capped_qualified_tuition_expenses_aldFollow-ups found in the audit and left out of this PR:
tuition_and_fees_deductionis both an above-the-line and a below-the-line deduction in 2018–2019.ctc_credit_limit_worksheet_b_applies; the section 911(f) worksheet flags) checkforeign_earned_income_exclusion > 0. A filer whose disallowed deductions use up the whole section 911 amount still files Form 2555, but with a zero stacking amount.ma_gross_incomesubtractsforeign_earned_income_exclusionfrom federal gross income, which the input contract says is already net of it. I did not check this against Massachusetts law.Invariants
These hold for every household:
adjusted_gross_income+student_loan_interest_ald+ the 911, 931 and 933 amounts + (unemployment compensation − taxable unemployment compensation). The two sides are built by different routes: sources with the excluded deductions left in, against gross income minus every deduction.agi_plus_section_911_931_933_exclusions= AGI + the three amounts, andadditional_senior_deduction_magiequals it exactly.medicaid_magi= max(0, AGI +section_911_excluded_income) for these households.taxable_income_plus_section_911_exclusion−taxable_income) addsforeign_earned_income_exclusion, the amount net of disallowed deductions.taxable_ss_magirise by exactly the amount shifted;ctc_phase_out,education_credit_phase_out,lifetime_learning_credit_phase_out);salt_cap, both clean vehicle eligibilities);policyengine_us/tests/test_modified_agi_add_backs.pychecks these with Hypothesis (10 random batches of up to 15 households for each property, over 2020, 2024, 2025 and 2026, and IRMAA benefit years 2016, 2017 and 2026), fixed grids of households in each phase-out range shifted into each category, and a 2020 85(c) case.Review r1
An independent review of
361ccac6e1requested changes. Each finding, how it is handled, and a mutation check: the defect reinserted into the fixed code, and the tests that then fail.section_911_excluded_income(Form 2555 lines 45 and 50), used by the shared MAGI, student-loan, Social Security, IRMAA and Medicaid/ACA MAGIs. The reviewer's case is a YAML test (ctc_phase_out.yaml).section_911_excluded_income) and the property grid. Student-loan MAGI → 2. Social Security MAGI → 1. IRMAA → 2 and the IRMAA property. Medicaid/ACA → 1. No default to the stacking amount → 4.filer_amounts_recorded_on_dependents: it is the parent's exclusion), so the add-back must cover it. Restricting it to the head and spouse gives $99,000 of IRMAA MAGI for $100,000 of wages. The reviewer's principle does apply to tax-exempt interest, which now counts for the head and spouse only. The $101,000 is right under this convention: the supplied AGI is net of the parent's $1,000 exclusion.361ccac6e1the 18-file set was 121 cases, 29 failing and 92 passing on main, all 121 passing on the branch.The mutation checks found one gap, now closed. The property tests missed the current-year-only new-vehicle test, because no grid household was over the new-vehicle limit in the current year only. A grid household now buys a vehicle with a prior-year MAGI of $140,000, and the grid catches the mutation.
Tests: before and after
At
361ccac6e1(review r1 reran this): the 18 YAML files the PR changed held 121 cases. On main (d7a526fbe2), 29 failed and 92 passed; on the branch all 121 passed. The earlier description said 30 failed; that count included the NIIT case dropped when #9808 took NIIT. On main,test_modified_agi_add_backs.pyfailed 6 of 6 (the shared variable didn't exist), and the student-loan-only invariants failed 4 of 4.At approved head
ae35fea93b. The PR's own diff contains 20 YAML files and 141 cases. The 22-file regression set additionally includes unchanged NIIT and senior-MAGI files. Local verification during that round was limited: the machine ran out of memory during the round (swap 34.8 of 35.8 GB used, load average 100–500), and test processes stalled for tens of minutes. What ran:medicare_irmaa_magi_two_years_prior.yaml, 16 passed; the IRMAA property tests (provided AGI, computed AGI, grid), 7 passed.ruff format --checkandruff checkpass on every changed Python file.Not rerun locally on this head: the full 22-file set against the branch and main, the other property tests, and the folder runs. CI's full suite on this head is the check for those.
CI follow-up (
5669235456). The original Form 4952/§911test_gridfailed locally withAssertionError: more_excluded. Its fixed-taxable-income premise did not account for the SALT MAGI add-back required by §164(b)(7)(B)(iv) and the 2025 Schedule A worksheet, page 7. The corrected property retains equality for election changes and unchanged deductions, asserts nondecreasing taxable income for same-choice exclusion pairs, and reuses the existing grid simulation for two hand-calculated SALT cases and independent form comparisons. The complete interaction file passes all 5 tests;make formatpasses formatting and lint. No production formulas changed, so no microsimulation variables need rerunning.Local macOS/Python 3.13 measurements, including setup: the original grid-only failure took 306 seconds (peak memory unavailable because the sandbox denied the platform resource query); the corrected five-test file took 167 seconds with 2.10 GiB peak RSS. These are different selections and cold-start conditions, not a causal runtime comparison. Existing Linux CI reported 18:05 and 10.09 GiB peak RSS for the affected Rest Python subgroup; current-head CI is pending. The two new households add ten variant tax units to the reused grid, with no new simulation construction or CI job.
Merging main
main(atbab1a47c60) is merged in with a merge commit; no force-push.student_loan_interest_ald_magiandtaxable_ss_magitotax_unit_non_dep_add. Both files keep that and this PR'ssection_911_excluded_incomeadd-back; the student-loan file was the one textual conflict.niit_magi_section_911_addition. Theforeign_earned_income_exclusiondocumentation keeps both notes, the other textual conflict.taxable_uc_agiand the rest of Keep dependents' above-the-line deductions off the filer's AGI #9801 are untouched here.Impact
Exact-head run: the hub compared merge base
bab1a47c60against approved headae35fea93bfor 2024 and 2026 using the default dataset, with real microsimulation outputs compared record by record. Dollar figures are weighted totals; weighted counts describe changed records.student_loan_interest_ald_magi(summed over people)student_loan_interest_aldadjusted_gross_incomeandmedicaid_magiincome_taxmedicare_irmaa_magi_two_years_priorCTC, taxable Social Security, NIIT, Part B premium and new/used clean vehicle credits change by $0 in both years.
foreign_earned_income_exclusion,specified_possession_incomeandpuerto_rico_incomeare $0 on every record, so the §911/931/933 add-backs themselves move no outputs in this dataset. The student-loan deduction change comes from counting unemployment compensation in full. The 2026 IRMAA change reflects the dependent tax-exempt-interest correction.section_911_excluded_income,agi_plus_section_911_931_933_exclusionsandclean_vehicle_credit_prior_year_magiare new on this branch and have no base comparator; their missing comparison entries do not mean a measured $0 change.axiom: TheAxiomFoundation/rulespec-us#1496 queued (§221(b)(2)); TheAxiomFoundation/rulespec-us#1500 queued (§§30D(f)(10), 25E(b))
🤖 Generated with Claude Code