Skip to content

Add the 2015-2017 federal income tax brackets - #10637

Merged
MaxGhenis merged 3 commits into
mainfrom
federal-brackets-before-2018
Nov 3, 2026
Merged

MaxGhenis merged 3 commits into
mainfrom
federal-brackets-before-2018

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Nov 2, 2026 •

Copy link
Copy Markdown
Contributor

Adds the 2015-2017 federal income tax rates and bracket thresholds.

gov.irs.income.bracket started in 2018. backdate_parameters (policyengine_us/tools/parameters.py) copies each parameter's earliest value back to FIRST_MODELED_YEAR (2015), so 2015-2017 ran on the 2018 TCJA schedule. For example, a 2017 single filer with $43,950 of taxable income got $5,608.50 at the main rates; under the 2017 schedule it's $6,726.25.

Changes

  • parameters/gov/irs/income/bracket.yaml: pre-TCJA rates from 2015-01-01 (10/15/25/28/33/35/39.6%) and the 2015, 2016 and 2017 thresholds for each filing status, from Rev. Procs. 2014-61, 2015-53 and 2016-55 (section 3.01). All 90 thresholds (six brackets, five statuses, three years) and the seven rates match those tables as printed. 2015 is the earliest year because FIRST_MODELED_YEAR is 2015, so parameters have no values before it.
  • income_tax_main_rates.yaml: 25 hand-computed cases. Every filing status at $100k and $500k for 2017 and 2015, and at $500k for 2016. Each comment shows the amount and tax in every bracket.
  • test_main_rate_schedule_monotone.py: a property test that the main-rates tax never falls as taxable income rises, and rises by at most the top rate per dollar. It runs every filing status, 2015-2030, on a $250 grid plus ±$1 around each threshold, and also pins the pre-2018 and 2018 rate lists. It reads parameters from the package's shared system and calls tax_at_main_rates directly, so it builds no simulation: 81 cases in about 7 s after the import. A second test checks that each threshold and each rate exceeds the one before: amounts_in_brackets clamps each bracket to the one below, so a threshold typo leaves the tax monotone and only this test catches it.
  • taxable_income_taxed_below_25_percent, dwks19 and amt_tax_including_cg: the comments put the end of the amount taxed below 25 percent at the top of the 15 percent bracket before 2018 and of the 24 percent bracket from 2018. A 2017 case in taxable_income_taxed_below_25_percent.yaml pins it: $37,950 for a single filer with $43,950 of taxable income.
  • The three Rev. Proc. references give their page ranges.

Not in this PR

The basic standard deduction still starts in 2018 on main, so a 2017 run uses $12,000 for a single filer, not $6,350. End-to-end pre-2018 income_tax stays wrong until that's fixed. #10595 adds a 2017 value only, which backdating would then copy to 2015 and 2016 ($6,300). programs.yaml keeps verified_start_year: 2018.

Twelve states read a federal tax variable, so their 2015-2017 results can move too: AL, CA, IA, LA, MO, MT, NE, NM, NY, OK, OR and WV.

impact: none from 2018 on. The PR changes only 2015-2017 values: every rate and threshold for 2018, 2019, 2025, 2026, 2030 and 2036 matches main.
axiom: TheAxiomFoundation/rulespec-us#2016 queued

🤖 Generated with Claude Code

gov.irs.income.bracket started in 2018, so backdating to the first
modeled year (2015) gave 2015-2017 the TCJA schedule: 12/22/24/32/37%
rates and 2018 thresholds. Add the pre-TCJA rates (10/15/25/28/33/35/
39.6%) from 2015 and each year's thresholds from Rev. Procs. 2014-61,
2015-53 and 2016-55, with hand-computed YAML cases for each filing
status and a grid property that the main-rates tax never falls as
taxable income rises.

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

Copy link
Copy Markdown
Contributor Author

Ready for hub review

Head: 534a28b2e86dbba5668fece20b7ed56cb99549c9 (draft)

Item and resolution

  • Recheck at origin/main (4e71481). Confirmed. backdate_parameters (policyengine_us/tools/parameters.py:16) copies each parameter's earliest value back to FIRST_MODELED_YEAR = 2015 (tools/parameters.py:9; called from system.py:143 and again at :173 after reforms). Every 2015-2017 period read the 2018 rates (10/12/22/24/32/35/37) and 2018 thresholds. Test case: a 2017 single filer with $60k of wages in TX had taxable income of $43,950 and income_tax_main_rates of 5,608.50 before. After, it's 6,726.25 (932.50 + 4,293.75 + 1,500).
  • Earliest year: 2015, which is FIRST_MODELED_YEAR. Parameters have no values before it, and the backdating floor is the same for every node.
  • Rates. bracket.yaml:21-39 adds 2015-01-01 values of 0.10/0.15/0.25/0.28/0.33/0.35/0.396, citing IRC 1(a)-(d) as modified by 1(i) (:15-18), plus 1(j)(2) for 2018 on.
  • Thresholds. 2015, 2016 and 2017 values for all 5 statuses and brackets 1-6, with bracket 7 = .inf, citing Rev. Proc. 2014-61, 2015-53 and 2016-55 section 3.01 (bracket.yaml:73-78). Surviving spouse = joint.
  • YAML. 25 cases in income_tax_main_rates.yaml:105-436: every status at $100k and $500k for 2017 and 2015, and at $500k for 2016. Each comment lists every bracket's amount × rate.
  • Property. test_main_rate_schedule_monotone.py:53 runs every status, 2015-2030, on a $250 grid to $1.5M plus ±$1 around each threshold. It checks that tax never falls and rises by at most the top rate × Δincome. :67 pins the pre-2018 and 2018 rate lists. Python is needed because YAML can't state a for-all property. It reads policyengine_us.system.system and calls tax_at_main_rates, so no extra model or simulation is built. It lands in the Rest Python "policy" group.
  • Partner tests. Untouched. None of the partner YAML files has a 2015-2017 period.

Sources and judgment calls

  • I couldn't fetch the primary sources. irs.gov, web.archive.org and other mirrors are blocked from this session (403 / DNS). Checks I could make:
    • The repo's gov/irs/capital_gains/thresholds.yaml (which cites the same Rev. Procs) matches my 15% bracket tops and 35% bracket tops for every status, 2015-2017.
    • Separate = half of joint in every bracket; the 33% top is the same for single, joint and head of household.
    • Top-bracket cases match the Rev. Proc. table base amounts I recall (2017: single $121,505.25, joint $131,628, separate $65,814, head of household $126,950 over the 39.6% threshold; 2015 single $119,996.25).
    • Secondary tables (Bankrate, Tax Foundation) agree.
    • Please spot-check against the Rev. Proc. PDFs. I added no #page= anchors because I couldn't open the files to find the pages.
  • Branch. I used the brief's branch federal-brackets-before-2018, not the session's default.
  • No source conflict. The statute and the Rev. Procs agree, so no register entry or marker is needed.

Tests run

  • income_tax_main_rates.yaml: 32/32 pass (7 existing + 25 new).
  • test_main_rate_schedule_monotone.py: 81/81 pass in 6.5 s of test time; ~1.2 GB peak RSS for the process, mostly package import. Measured locally under load, not Linux CI.
  • Before/after sweep of existing tests. All 471 YAML cases dated 2015-2017 (from 129 files) were pulled into one scratch YAML and run in 6 chunks on base origin/main and on this head. 471/471 pass on both, with no failures unique to either side, so no existing pre-2018 test value moved.
  • Python files with 2015-2017 tax paths, all passing on this head: test_hi_credit_ordering 14, test_federal_credit_limit_order 17, core/test_pre_first_modeled_year_uprated_inputs 48, test_ctc_foreign_earned_income_exclusion 2, test_modified_agi_add_backs 13, unit/test_ms_mo_misc_deduction_once_properties 2, core/test_itemized_deduction_limitation_properties 13.
  • No full suites, no microsimulations.

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

Mutant YAML (32) Property (81)
M1 drop 2015 rates (fall back to TCJA) 25 fail 1 fail
M2 2016 joint 35% top 466,950 → 46,695 2 fail pass
M3 2015 rate 3 → −0.25 25 fail 16 fail
M4 remove running-bottom clamp + 2017 single bracket 3 below bracket 2 2 fail 1 fail
M5 2017 head of household 25% top 131,200 → 113,200 1 fail pass

All five were killed.

States that read the federal schedule

  • Direct reads of gov.irs.income.bracket outside federal: only id_itemized_deductions.py:56, and it's gated on the OBBB limitation (2026+), so it doesn't move. Same for the federal itemized_taxable_income_deductions_reduction.py:33.
  • States that read federal tax liability, so pre-2018 results move wherever their own code runs:
    • AL: al_federal_income_tax_deduction
    • CA: ca_federal_capped_cdcc
    • IA: ia_fedtax_deduction
    • LA: la_federal_tax_deduction
    • MO: mo_federal_income_tax_deduction
    • MT: federal tax deduction and the elderly credit
    • NE: ne_income_tax_before_refundable_credits and ne_elderly_disabled_credit
    • NM: nm_cdcc
    • NY: ny_recomputed_federal_agi
    • OR: or_federal_tax_liability_subtraction
  • Federal paths that move too: the capital gains tax via taxable_income_taxed_below_25_percent, the AMT comparison, and tax-limited nonrefundable credits.

CI

Not run: CI skips drafts.

Proposed follow-up

Add the 2015-2016 basic standard deduction (and check 2017). gov.irs.deductions.standard.amount starts in 2018 on main, so pre-2018 runs use $12,000 single and $24,000 joint. #10595 adds only 2017 ($6,350 single), which backdating would then copy to 2015-2016 (actually $6,300). End-to-end pre-2018 income_tax stays wrong until this lands, which is why programs.yaml keeps verified_start_year: 2018.


Generated by Claude Code

Section 3.01 of each Rev. Proc. starts on the linked page: Rev. Proc. 2016-55 on page 7, and 2015-53 and 2014-61 on page 5.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@MaxGhenis
MaxGhenis marked this pull request as ready for review November 3, 2026 00:31
@MaxGhenis

MaxGhenis commented Nov 3, 2026 •

Copy link
Copy Markdown
Contributor Author

Hub review in progress

Superseded: the final review is at #10637 (comment)


Generated by Claude Code

@MaxGhenis

Copy link
Copy Markdown
Contributor Author

Ready for hub review
VERDICT: APPROVE
ef66d86
METHODOLOGY_CHOICE: no

The PR head is still ef66d861ae. The only change since the hand-off head 534a28b2 is the #page= anchors on the three Rev. Proc. references in bracket.yaml (3 lines changed).

I found no P1 or P2. The PR fixes a defect that reproduces on main. Each 2015-2017 result I built moves toward the law, and nothing in 2018 or later can move. The findings below are P3s on docs, test strength and hand-off claims.

Findings

P3-1. Three docstrings and comments still say "top of the 24 percent bracket", which is now wrong for 2015-2017

  • policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/taxable_income_taxed_below_25_percent.py:13-16
  • policyengine_us/variables/gov/irs/tax/federal_income/capital_gains/dwks19.py:40-41
  • policyengine_us/variables/gov/irs/tax/federal_income/alternative_minimum_tax/amt_tax_including_cg.py:84

The code reads p.income.bracket and regular_rate_limit (0.25), so at this head the 2017 amount is the top of the 15% bracket. Witness: a 2017 single filer with $40,000 of wages and $20,000 of unrecaptured §1250 gain (TX). At this head taxable_income_taxed_below_25_percent = 37,950, capital_gains_excluded_from_taxable_income = 6,000 and capital_gains_tax = 1,500. On main they are 43,950, 0 and 0. The code is right. Only the text is stale. Suggested docstring for taxable_income_taxed_below_25_percent:

"The amount of taxable income that the regular rate schedule taxes at "
"a rate below 25 percent: taxable income up to the top of the 15 percent "
"bracket before 2018 and of the 24 percent bracket from 2018. Section "
"1(h)(1)(A) taxes capital gain at the regular rates up to this amount, or "
"taxable income less adjusted net capital gain if less. Schedule D Tax "
"Worksheet line 19. For a taxpayer excluding foreign earned income, "
"taxable income includes the excluded amount."

In dwks19.py:40 and amt_tax_including_cg.py:84, replace "the top of the 24 percent bracket" with "the top of the highest bracket taxed below 25 percent (15 percent before 2018, 24 percent from 2018)".

The PR has no test of this path before 2018. Suggested YAML case for taxable_income_taxed_below_25_percent.yaml (I computed it by hand: 2017 single 15% bracket top = 37,950):

- name: 2017 single filer, the pre-2018 schedule taxes income below 25 percent only to the 15 percent bracket top
  period: 2017
  input:
    filing_status: SINGLE
    taxable_income_plus_section_911_exclusion: 43_950
  output:
    taxable_income_taxed_below_25_percent: 37_950

P3-2. The property test can't detect threshold data errors

test_main_rate_schedule_monotone.py:53. amounts_in_brackets clamps each bracket top to the running bottom (tax_at_main_rates.py:17). So any set of non-negative rates gives a monotone tax, whatever the thresholds are. I ran three data mutants and the property test passed every one: M2 (2016 joint 35% top 466,950 → 46,695), M5 and my M6 (below). Only the YAML cases kill them. The test still does what the brief asked, but it guards rates, not thresholds. Suggested addition (I checked that it passes at this head for 2015-2030, every status; it would kill M2):

@pytest.mark.parametrize("year", YEARS)
@pytest.mark.parametrize("status", FILING_STATUSES)
def test_thresholds_and_rates_strictly_rise(year, status):
    bracket = bracket_node(year)
    keys = list(bracket.rates.__iter__())
    t = [float(bracket.thresholds[b][status]) for b in keys]
    r = rates(bracket)
    assert all(a < b for a, b in zip(t, t[1:])), (year, status, t)
    assert all(a < b for a, b in zip(r, r[1:])), (year, r)

P3-3. The Rev. Proc. reference titles leave out their multi-page ranges

bracket.yaml:73-78. The hub reports that the tables span pages 7-8 (2016-55), 5-7 (2015-53) and 5-6 (2014-61). Under CLAUDE.md's reference rule, the href opens the first page and the quoted title ends with the range. Replacement:

      - title: "2017 IRS data release, Rev. Proc. 2016-55 section 3.01 #page 7-8"
        href: https://www.irs.gov/pub/irs-drop/rp-16-55.pdf#page=7
      - title: "2016 IRS data release, Rev. Proc. 2015-53 section 3.01 #page 5-7"
        href: https://www.irs.gov/pub/irs-drop/rp-15-53.pdf#page=5
      - title: "2015 IRS data release, Rev. Proc. 2014-61 section 3.01 #page 5-6"
        href: https://www.irs.gov/pub/irs-drop/rp-14-61.pdf#page=5

P3-4. The hand-off's list of states that read federal tax misses two, and the PR body has no list

The brief asked for this list. I grepped variables/gov/states for reads of income_tax, income_tax_before_credits, income_tax_before_refundable_credits, regular_tax_before_credits, income_tax_main_rates, alternative_minimum_tax, capital_gains_tax and income_tax_non_refundable_credits. That finds the hand-off's ten states plus:

  • OK: ok/tax/income/credits/ok_federal_ctc.py:57 reads income_tax_non_refundable_credits.
  • WV: wv/tax/income/credits/liftc/wv_low_income_family_tax_credit_eligible.py:14 reads alternative_minimum_tax. AMT moves at this head (see the witnesses below).

Suggested body line: "States that read a federal tax variable, and so can move in 2015-2017: AL, CA, IA, LA, MO, MT, NE, NM, NY, OK, OR, WV."

P3-5. One mutant count in the hand-off is wrong

The hand-off says M2 fails 2 YAML cases. Rerun at this head, it fails 1 (31 pass). Surviving spouse has its own SURVIVING_SPOUSE values, so the 2016 surviving-spouse case doesn't see a joint-only edit. The mutant is still killed.

Checked and sound

The defect reproduces on main (0d933918). A 2017 single filer in TX with $60,000 of wages and $40,000 of long-term capital gain has taxable income 83,950. income_tax_main_rates is 5,608.50 on main (the 2018 schedule on 43,950 of ordinary income) and 6,726.25 at this head. All 25 new YAML cases fail on main (25 failed, 7 passed) and pass at this head (32 passed).

The rule. I wrote my own seven-rate calculator from the 90 thresholds and seven rates, which the hub compared against the Rev. Procs, before reading the test comments. It matches all 25 pre-2018 YAML expectations to the cent (0 mismatches). The code (tax_at_main_rates.py) applies the same schedule for every filing status and entity. Surviving spouse uses the joint values, as §1(a) applies one table to both. The YAML cases cover every status for 2015 and 2017 at $100k and $500k, and for 2016 at $500k. Every finite threshold is below $500k, so the $500k cases reach all 90.

Uprating. I dumped all 7 rates and 35 thresholds at 2015-2019, 2025, 2026, 2030 and 2036, at main and at this head. 105 of 378 values differ, all in 2015-2017. The values for 2018 and later are identical (e.g. 2036 single bracket 1 15,225, 2036 joint bracket 6 944,150 on both sides).

Fixed-date readers. tools/pinned_tbs.py pins EITC, CTC, credit and dependent-care-assistance parameters, never income.bracket. The fixed-instant reads in reforms/ and variables/ don't read the bracket before 2018:

  • trustees_core_thresholds.py:127 only checks for an end-year value and rewrites years after 2026.
  • capital_gains_tax_increase.py:67 reads rates["7"] at the period.
  • hi_reduced_itemized_deductions.py:35 reads the 2017 itemized limitation, not the bracket.

I found no prior-year reads of federal tax variables. or_tax_before_credits_in_prior_year uses the current year. The Python tests that read the bracket run 2018 on or 2024-2026 (test_additional_tax_bracket_unset_bracket.py, test_section_1h_regular_tax.py, test_pinned_tbs_sharing.py).

Impact. Nothing other than 2015-2017 periods can move, so impact is none for 2018 on.

2017 witnesses (TX, main → head; standard deduction 12,000/24,000 on both):

Case Taxable income Main Head Law with the 2017 std. deduction
Single, $50k wages 33,950 3,883.50 4,626.25 (+742.75) 5,638.75 on 39,600
Couple, $100k wages 67,900 7,767.00 9,252.50 (+1,485.50) 11,277.50 on 79,200
Couple, 2 kids, $100k 59,800 4,795.00 6,037.50 (+1,242.50) 9,732.50 − 2,000 CTC = 7,732.50
Single, $60k wages + $40k LTCG 83,950 11,608.50 12,726.25 the regular tax moves by the ordinary part only; capital_gains_tax is 6,000 on both
Couple, $300k wages + $200k LTCG (AMT) 476,000 regular 85,084 / AMT 25,425 regular 96,562 / AMT 13,947 total is unchanged: AMT absorbs the regular-tax rise
Couple, $250k wages, $40k property tax (AMT) 200,181 regular 36,622.44 / AMT 12,199.06 42,935.18 / 5,886.32 same
Single itemizer, $470k wages 424,187 124,154.95 123,796.90 (−358.05) head equals the 2017 schedule on that income

The law values use my hand calculation with the 2017 standard deduction (6,350 single, 12,700 joint) and the 2017 exemption of 4,050 per person, which main already has. Results for 2015 move the same way: single +749.25, couple +1,498.50.

The partly fixed year isn't made worse. At this head the tax is the pre-TCJA schedule applied to the model's taxable income. That income is at or below the true figure, because the model's standard deduction (the 2018 value) is larger, and the exemption and the uncapped pre-2018 SALT are already pre-TCJA. So the head's tax lies between main's and the law's wherever the 2018 schedule taxes less. Where the 2018 schedule taxes more (single taxable income of about $385k-$432k, as in the itemizer row), the head is lower and exact for itemizers. In every case I built, the head is closer to the law than main. For a typical filer the head still understates the 2017 tax by about 1,012.50 (single, $50k) and 2,025 (couple, $100k), against 1,755.25 and 3,510.50 on main.

Landing order with #10595. #10595 adds only 2017 standard deduction values (6,350 / 12,700 / 9,350), which I confirmed in its diff. The two PRs touch different files, and each moves 2017 toward the law alone, so either order works. programs.yaml:17 keeps verified_start_year: 2018, which is still accurate.

Capital gains. capital_gains_tax uses capital_gains/thresholds.yaml, which already has 2015-2017 values, so its 0/15/20% split doesn't move. Only the §1(h)(1)(A)(ii)(I) "taxed below 25 percent" amount moves, and it now follows the pre-2018 schedule (the §1250 witness in P3-1).

AMT. The AMT exemption has 2014-2017 values (amt/exemption/amount.yaml:43-62), so the tentative minimum tax doesn't move. Where AMT binds, the head shifts tax from AMT to regular tax and leaves the total unchanged, as the law does.

Tax-limited credits. In the two-child witness, CTC (2,000) isn't limited on either side.

Tests run at this head, one file per process:

  • income_tax_main_rates.yaml: 32 passed.
  • test_main_rate_schedule_monotone.py: 81 passed in 6.2 s of test time; 48.5 s wall clock including import; 1.19 GB peak RSS (local VM, not Linux CI).
  • The file sits under tests/policy/, so it falls in the Rest "policy" group. It reads policyengine_us.system.system through parameters(instant) and makes no update calls, so it leaves the shared object unchanged. Its grid runs from 0 to $1.5M plus ±1 around every finite threshold. The largest threshold through 2030 is below $1.5M, so the grid reaches every threshold in every year.

Existing 2015-2017 tests. I count 130 YAML files with a 2015-2017 period (496 such periods, 471 excluding the 25 new ones). That matches the hand-off. I sampled 12 tax-heavy files and all pass at this head: AR 2014-2020 (116), MT elderly credit (50), refundable CTC (15), itemized reduction (14), MS joint (12), IN refundable (9), AMT income less exemptions (8), MO itemized (8), itemized limitation (7), VA itemized (6), Medicare 2015-2016 integration (2), NC credit order (1). Python tests also pass: test_federal_credit_limit_order.py (17), test_hi_credit_ordering.py (14), core/test_pre_first_modeled_year_uprated_inputs.py (48). The first two need hypothesis, which uv sync doesn't install; I ran them with uv run --with hypothesis.

Mutants, each in its own worktree and process. All six are killed:

Mutant YAML Property test Killed by
M1: drop the 2015 rates 25 fail 1 fail both
M2: 2016 joint bracket 6 → 46,695 1 fail pass YAML (2016 joint $500k)
M3: 2015 rate 3 → −0.25 25 fail 16 fail both
M4: remove the clamp, and 2017 single bracket 3 → 30,000 2 fail 1 fail both
M5: 2017 HoH bracket 3 → 113,200 1 fail pass YAML
M6 (mine): 2016 HoH bracket 5 413,350 → 413,530 1 fail pass YAML (2016 HoH $500k, −$3.60)

I removed the worktrees afterward, and git status is clean.

Partner tests. The periods under tests/policy/baseline/partners/ are 2024 (1), 2025 (25), 2026 (591) and 2026-01 (6). None is in 2015-2017, and the PR touches no file there or under .github/.

Scope and overlap. The PR changes 4 files. Searching open PRs for bracket.yaml and income_tax_main_rates finds only #10637 and #9934. #9934 touches test_negative_main_rates_inverted_brackets.py, not these files.

Body. The impact: and axiom: lines are present. I checked each body claim: the backdating to FIRST_MODELED_YEAR = 2015 (tools/parameters.py:9,16-31), the 5,608.50 → 6,726.25 example, 25 cases, 81 property cases in about 7 s, #10595 adding 2017 only, and programs.yaml at 2018.

CI at this head, Pull request run 41553424628, checked while it was still running. Succeeded: Quick Feedback, PR validation, Package and compatibility, Household API Partners, all 7 Rest Python shards, and all 11 Baseline shards (irs, reform, states 1-4, household, ssa-usda, rest-a, rest-b, contrib-hhs). In progress: Microsimulation and the 9 Contrib shards. The hub should confirm their conclusions.

What I couldn't check

  • Primary sources. irs.gov, govinfo.gov and uscode.house.gov all refused the connection from this VM (CONNECT 403). I didn't read 26 U.S.C. 1 or the Rev. Procs myself. For the 90 thresholds and seven rates I rely on the hub's comparison, and I made no claim about the text of §1(i).
  • Population impact needs data I can't reach. By the parameter check above, it is limited to 2015-2017 periods.
  • The remaining 118 YAML files with 2015-2017 periods. I relied on CI's Baseline shards for them, and those succeeded.

Generated by Claude Code

Three comments said the amount taxed below 25 percent ends at the top of the 24 percent bracket; before 2018 it ends at the top of the 15 percent bracket. A 2017 case now pins that amount, a test checks that each threshold and rate exceeds the one before (the monotone-tax property can't catch a threshold typo), and the three Rev. Proc. titles give their page ranges.

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

Copy link
Copy Markdown
Contributor Author

US + core hub merge audit, us#10637 at 0074c0107ed2ff972db51ced4f7b6481fb5b4d35 (squash): Add the 2015-2017 federal income tax brackets.

@MaxGhenis
MaxGhenis deleted the federal-brackets-before-2018 branch November 3, 2026 04:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants