Repository navigation
Test 28-percent rate gain with a Form 4952 election against the 2025 worksheets - #9893
Merged
Merged
Conversation
…rm 6251 line 10 26 U.S.C. 1(h)(1) says the tax "shall not exceed" the sum of the subparagraph (A) to (F) amounts, and Schedule D Tax Worksheet line 47 takes the smaller of line 45 and line 46 (the tax on all of line 1). capital_gains_tax now stops at tax_on_taxable_income_at_main_rates (new, line 46) less income_tax_main_rates. The cap binds where gain taxed at 15 percent falls in the 12 percent bracket: up to $3.75 single and $7.50 joint in 2025, and $28.50 and $57 in 2026. The Biden and Harris capital gains reforms apply the same limit, and the additional_tax_bracket reform figures line 46 on its own schedule. regular_tax_before_credits was a second Schedule D Tax Worksheet (2017 numbering) whose result was min(worksheet tax including the gains, income_tax_main_rates excluding them). Form 6251 line 10 then added capital_gains_tax. Whenever the worksheet path fell below the main-rates path, line 10 understated the regular tax: before #9781 that happened for 18 dataset tax units in 2025 ($328M weighted), because dwks10 ignored the Form 4952 line 4g election and because the worksheet took line 28 as min(line 21, line 22) instead of their sum. regular_tax_before_credits is now income_tax_main_rates plus capital_gains_tax, the regular tax income_tax_before_credits charges, and Form 6251 line 10 uses it alone. Tests: real-unit regression cases for tax units 1101624, 1004930, 1156248 and 1032460; line 45/46 boundary cases for 2025 and 2026 (single, joint, head of household, Form 2555); the existing worksheet property tests now compare with line 47 and Foreign Earned Income Tax Worksheet line 25 exactly. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Section 3 of Rev. Proc. 2025-32 modifies 2025 items (only .01, the standard deduction, and .02, section 179); the 2026 adjusted items are section 4. The 2026 AMT breakpoints are section 4.10 (PDF page 16) and the 2026 additional standard deduction for the aged or blind is section 4.14(3) (PDF page 18). Also give the dependent standard deduction citation its section number (4.14(2)). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…47 limit test_regular_tax_before_credits.py transcribes the 2025 Schedule D Tax Worksheet, lines 1 to 47, with every skip instruction, the Form 4952 election, capital gain distributions, short-term losses and Schedule D lines 18 and 19. The regular rates come from the 2025 Tax Computation Worksheet rows. 2026 runs on Rev. Proc. 2025-32's tables and capital gains amounts. The model matches lines 10, 13, 14, 21, 44, 46 and 47 for a grid, households at the 0 percent amount, and Hypothesis draws. Properties, 2018 to 2035: - 0 <= regular tax <= line 46; - 0 <= capital_gains_tax <= line 46 - income_tax_main_rates; - regular_tax_before_credits = main rates + capital gains tax; - income_tax_before_credits = regular + AMT; - the AMT is line 9 less line 10; - without net capital gain, the regular tax is line 46; - the worksheet lines (dwks10, dwks13, line 18 <= 21 <= 14); - the tax is monotone in wages, gains and dividends; - results are independent of batch order. The limit binds at the top of the 12 percent bracket in every year and filing status, with the published band widths for 2018 to 2026. The monotonicity check sets aside households with capital gain distributions and a Schedule D net loss, a known defect it found. Gross income adds non_sch_d_capital_gains outside the loss limit, while net_capital_gain nets them (a 2018 single filer's tax falls $0.12 with $1 more gain). No dataset tax unit has both. It is left for a follow-up. Also adds 2026 joint, separate and non-binding line 45/46 YAML cases, from the same Rev. Proc. 2025-32 figures as the Axiom companion tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #9831 (amounts_in_brackets, the shared bracket walk that tax_at_main_rates and amount_taxed_below_rate use) and #9788 (capital gain distributions netted inside the capital loss limit). No textual conflicts: the additional_tax_bracket reform's tax_on_taxable_income_at_main_rates override calls tax_at_main_rates, so it takes #9831's walk. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s netting - test_additional_tax_bracket_unset_bracket.py (#9831) listed regular_tax_before_credits among the variables the reform replaces. On this branch the reform replaces tax_on_taxable_income_at_main_rates instead, and regular_tax_before_credits is income_tax_main_rates plus capital_gains_tax. The default-system leak check now covers the three replaced variables, and the finite and monotone checks cover the reform's three taxes: income_tax_main_rates, tax_on_taxable_income_at_main_rates and regular_tax_before_credits. - test_regular_tax_before_credits.py set aside households with capital gain distributions and a Schedule D net loss in the monotonicity property. #9788 nets the distributions inside the capital loss limit in AGI, so the property now holds for every household. The exclusion is gone, and the minimized counterexample (2018 single, $50,000 of distributions, a $40,273 short-term loss, $1 more long-term gain) is an explicit example; it failed before #9788 (the regular tax fell by $0.12) and passes now. Both files pass after the merge: 9 and 6 tests. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
… capital gains tax PolicyEngine/salt-amt-calculator's API reads regular_tax_before_credits as its regular tax, so the meaning change belongs in the release note. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…unts Three links pointed into the wrong section of Rev. Proc. 2025-32: - the capital gains rate thresholds linked page 12, which is section 4.02 (kiddie tax). Section 4.03 and its 2026 amounts (49,450, 98,900, 66,200, 545,500, 613,700, 579,600 and 306,850) are on page 13; - the child tax credit amount ($2,200) and refundable maximum ($1,700) linked page 13, which holds sections 4.03 and 4.04. Section 4.05 and its amounts are on page 14. The other parameter links to the Rev. Proc. land on the page that has the 2026 amounts or on the page where their section starts. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Brings in #9841 (28 percent rate gain and unrecaptured section 1250 gain netted against losses), #9801, #9790, #9779, #9768, #9741 and #9630. Only test_section_911_tax_stacking.py changed on both sides, in different functions, and git merged it without conflict. #9841 passes Schedule D lines 18 and 19 entered already netted through unchanged, and this branch's capital_gains_tax, reforms and tests read the section 911 amounts and Schedule D inputs that #9841 keeps. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…worksheets - rate_gain_with_form_4952_election.yaml: the reproducer (no taxable income; taxable income below the gain; half and all of a collectibles gain elected; no election) and an election above the 24% bracket that leaves 28% on line 9 only, with Schedule D Tax Worksheet lines. - test_section_1h_regular_tax.py: for every household and year (2018 to 2030) with elections and both gains, the regular tax is at most line 46 figured from the rate parameters, exactly zero with no taxable income, and not negative; and electing more lowers it by at most the 12/15 rate overlap plus 3% of the smaller of the extra amount elected and the unrecaptured section 1250 gain. Pins the intended exception where an election moves gain from 28 to 25 percent (section 1(h)(1)(E)(ii) measures its excess with net capital gain, which section 1(h)(2) reduces): $68,874 to $67,374 on $50,000 elected. A 2025 differential compares Form 6251 Part III and line 11 after an election with nonzero Schedule D lines 18 and 19. - test_form_6251_part_iii.py: the Schedule D Tax Worksheet transcription takes amounts, Form 4952 lines 4g and 4e, and the Foreign Earned Income Tax Worksheet footnote's four modifications. - test_section_911_tax_stacking.py and test_form_4952_section_911_interaction.py: households with 28-percent rate and unrecaptured section 1250 gain are now compared with the forms (through the Schedule D Tax Worksheet and Form 6251 Part III with the AMT capital gain excess), including rows that reach modification 4, and kept in the monotonicity properties, with the corrected election bound in place of #9800's overlap-only bound. - Harris, Biden 2025 budget and additional tax bracket reforms: a case each where the line 47 limit binds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…tests # Conflicts: # policyengine_us/tests/test_form_4952_section_911_interaction.py # policyengine_us/tests/test_section_911_tax_stacking.py
- test_section_1h_regular_tax.py: the grid reaches 28% tax after an election only if some elected household has a positive Schedule D Tax Worksheet line 42; a positive capital gains tax could be all at 25%. - test_section_911_tax_stacking.py, test_form_4952_section_911_interaction.py: floor the unrecaptured section 1250 gain after modification 4 at zero, so a household whose line 9 the excess exhausts does not count as reaching it. - test_form_6251_part_iii.py: the transcription, not the model, leaves out the short-term loss netting (#9841 added it to the model). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Hand-off to the US + core hub (session local_01265359, 2026-10-06)
|
Contributor
Author
|
US + core hub merge audit, us#9893 at
|
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.
Tests only, following #9852 (the 26 U.S.C. 1(h)(1) "shall not exceed" cap, Schedule D Tax Worksheet line 47), which has merged. No formula, parameter or existing expected output changes.
Why
The 2025 reproducer for 28-percent rate gain with a Form 4952 line 4g election (single, Texas, age 45; all gain collectibles):
*At the exact rates the model uses. For the $5,000 row the IRS Tax Table, which taxes the middle of each $50 row, gives $503.
#9792 fixed these (subparagraph (F) taxes only what is left of taxable income, and subparagraph (A) taxes gain below the top of the 24% bracket at the regular rates). Rows 3 and 5 are 37,067, not 39,067 and 41,449: worksheet line 19 is $197,300, so line 21 is all $184,250 of taxable income. The tests that should have caught the bug excluded 28-percent rate and unrecaptured section 1250 gain from their tax comparisons. This PR adds the cases and widens those tests.
Invariants (tested)
income_tax_before_creditsby at mostrate_overlap+ 3% × min(extra elected, unrecaptured section 1250 gain).test_section_1h_regular_tax.pychecks all three for every generated household with elections and both gains in 2018, 2022, 2025, 2026 and 2030: line 46 is figured from the rate parameters, and at zero taxable income the regular tax and the capital gains tax must be exactly zero. The base PR'stest_regular_tax_before_credits.pychecks 1 and 2 against the model's own line 46 and its 2025/2026 worksheet; 3 is new here, andtest_form_4952_section_911_interaction.pychecks it with exclusions too. Property 3 replaces the bound in #9800's test ("the tax falls by at most the 12/15 rate overlap"), which is false once 28-percent rate and unrecaptured section 1250 gain are included.Intended exception: an election can lower the tax by moving gain from 28 to 25 percent. Section 1(h)(1)(E) taxes at 25 percent the unrecaptured section 1250 gain less "(ii) the excess (if any) of (I) the sum of the amount on which tax is determined under subparagraph (A) plus the net capital gain, over (II) taxable income". Section 1(h)(2) reduces net capital gain by the amount elected. When the top of the 24% bracket sets the subparagraph (A) amount, an election shrinks that excess, so more of the 1250 gain is taxed at 25% and less 28-percent rate gain at 28%. The worksheet does the same on lines 35 to 42.
test_election_can_move_gain_from_28_to_25_percentworks the case:The 12/15 exception (#9800's
test_election_can_lower_the_tax_where_15_percent_exceeds_12) still holds: where the 0% amount ends below the top of the 12% bracket and taxable income goes past it, electing gain there lowers line 47 by 3% per dollar.Differential (2025)
schedule_d_tax_worksheet_lines_2025(test_form_6251_part_iii.py) now takes amounts, Form 4952 lines 4g and 4e on worksheet lines 3 and 4, and the four modifications of the Foreign Earned Income Tax Worksheet footnote. It was checked locally against an independent transcription written only from the IRS text: equal on lines 10, 13, 14, 21, 39, 42 and 45 to 47 for 75,207 random cases with elections, capital gain excesses and both gains (largest difference 1e-10).test_regular_tax_before_credits.pycompares the regular tax line by line with its own transcription, elections included, for 2025 and 2026.test_section_1h_regular_tax.pyadds Form 6251 Part III after an election: a grid (5 statuses × 9 incomes × up to 5 elections) and a Hypothesis search compare the AMT with Part III and line 11 fed by the worksheet after the election, anddwks10,dwks13anddwks19with lines 10, 13 and 21 (for filers with dividends or gains; the model sets its worksheet lines to zero otherwise). Households have no short-term loss: the transcription takes Schedule D lines 18 and 19 as entered, without the 1(h)(4)(B) and 1(h)(6) netting that Net losses against 28 percent rate gain and unrecaptured section 1250 gain (26 U.S.C. 1(h)(4), 1(h)(6)) #9841 added to the model.test_section_911_tax_stacking.py: households with 28-percent rate or unrecaptured section 1250 gain now go through the Foreign Earned Income Tax Worksheet with the Schedule D Tax Worksheet (capital gain excess from line 10, modifications 1 to 4) and Form 6251 line 7 with Part III lines 13 to 15 after the AMT capital gain excess, and lines 20 and 27 from the regular-tax worksheet. They are in the grid, the strategies and the invariants.test_form_4952_section_911_interaction.py: the same for elections and exclusions together; the monotonicity property covers every household, with the corrected election bound.rate_gain_with_form_4952_election.yaml(the table above, with worksheet lines); the line 47 limit binding in the Harris and Biden 2025 budget reforms and in the additional tax bracket reform's own schedule (28% against its 25% bracket in 2026).Tests run (local, foreground, one file at a time)
On this branch after merging main at 843bcda (#9852):
test_section_1h_regular_tax.py9 passed;test_section_911_tax_stacking.py10;test_form_4952_section_911_interaction.py5;test_form_6251_part_iii.py9;test_regular_tax_before_credits.py(Cap the regular tax at the tax on all taxable income (26 U.S.C. 1(h)(1)) and use it on Form 6251 line 10 #9852's) 6.rate_gain_with_form_4952_election.yaml6 passed;additional_tax_bracket_reform.yaml10;capital_gains_tax_increase.yaml2;harris_capital_gains.yaml6.Review
Independent reviews of this commit: r1 (Opus 5.5) APPROVE with four non-blocking findings, all fixed (one added grid rows that reach footnote modification 4 in both section 911 test files); r2 (Opus 5.5) delta APPROVE with nits, fixed; r3 (GPT-6.1 Sol) REQUEST_CHANGES on a trim that had dropped this file's line 46, zero and sign properties as duplicates of the base PR's: the base PR's random test omits 2022 and trusts the model's line 46, and nothing else checks the exact zero. The properties are restored; r4 (Opus 5.5) APPROVE once two diffs it could not run came out clean (the file differs from the r2-approved version only in its docstring, and nothing else changed). Since r4: the rebase onto #9852, a docstring wording nit from r4, removal of a NaN workaround in the additional tax bracket YAML case that #9831 made unnecessary, and docstrings that now describe #9841's netting correctly. Final hard-tier review of this PR's diff (GPT-6.1 Sol): APPROVE, with five low-severity notes, all fixed in d4056a0: two reach assertions tightened (a positive worksheet line 42 after an election; the modification 4 reach floored at zero), one inherited docstring corrected, and the two figures above.
CI cost
The Python files are top-level
policyengine_us/tests/modules, so they run in the Rest job'sremaininggroup (239 s in run 37198184683, per the Makefile). They are property and differential tests over generated households, which YAML cannot express; the YAML cases are in YAML. Local measurements (macOS, load about 30, onepytestprocess per file, not Linux CI):test_section_1h_regular_tax.py(new)test_section_911_tax_stacking.pytest_form_4952_section_911_interaction.pytest_form_6251_part_iii.pySo the
remaininggroup gains about 15 s of test time; the widened files cost no measurable time or memory. The group-level peak in one CI process was not measured locally; this PR's CI timing report will show it.Impact
No model output change: this PR adds and widens tests, adds YAML cases and a changelog fragment. For context on #9852's cap, an independent run of the same mechanism on the default dataset (base: main with #9792 and #9800, before #9841): 2025, 2 tax units changed, −$0.06 weighted federal income tax (largest cut $1.23); 2026, 34 units (44,140 weighted), −$616,041 federal income tax (−$616,045 capital gains tax; largest cut $47.29, joint). After the cap no unit's regular tax exceeds line 46.
axiom: n/a: test-only change; no policy logic modified
🤖 Generated with Claude Code