# Validation scenarios for the PolicyEngine oracle comparison.
#
# Each scenario is run through both Retirement Figures and PolicyEngine US
# and the outputs are diffed. See README.md for tolerance philosophy.
#
# Schema:
#   name:               human-readable scenario label
#   year:               tax year (int)
#   state:              two-letter state code
#   filing_status:      'mfj' or 'single'
#   default_tolerance:  default $ slack on each output diff
#   tolerances:         per-variable overrides (PE variable name)
#   people:             list of person dicts
#     age, wages, self_employment, pension, ira_distributions,
#     roth_conversion, social_security_gross, long_term_capital_gains,
#     short_term_capital_gains, qualified_dividends, ordinary_dividends,
#     taxable_interest      (all optional; default 0)

scenarios:

  - name: '01 - Single, no SS, simple IRA withdrawal (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0      # TX has no state income tax
    people:
      - {age: 55, ira_distributions: 50000}

  - name: '02 - MFJ, no SS, simple IRA withdrawal (TX)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      - {age: 60, ira_distributions: 80000}
      - {age: 58}

  - name: '03 - MFJ, SS + pension (OH)'
    year: 2026
    state: OH
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 25     # Ohio low brackets + retirement credit
    people:
      - {age: 67, social_security_gross: 30000, pension: 40000}
      - {age: 65, social_security_gross: 20000}

  - name: '04 - MFJ, SS + Roth conversion crossing SS phase-in (OH)'
    year: 2026
    state: OH
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 25
    people:
      - {age: 67, wages: 40000, social_security_gross: 60000, roth_conversion: 30000}
      - {age: 62}

  - name: '05 - SS taxation 50% threshold edge (single)'
    year: 2026
    state: FL
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Provisional = 24500 + 0.5*15000 = 32000 -> just past low tier (25000)
      # taxable SS = 0.5 * (32000 - 25000) = 3500, capped at SS*0.5
      - {age: 67, wages: 24500, social_security_gross: 15000}

  - name: '06 - LTCG + QD straddling 0%->15% threshold (MFJ)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      - {age: 55, wages: 60000, long_term_capital_gains: 40000, qualified_dividends: 5000}
      - {age: 55}

  - name: '07 - Ohio resident, working-age (OH)'
    year: 2026
    state: OH
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 25
    people:
      - {age: 50, wages: 120000}
      - {age: 48, wages: 50000}

  - name: '08 - No income tax state baseline (NV)'
    year: 2026
    state: NV
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0      # NV has no state income tax
    people:
      - {age: 45, wages: 100000}

  - name: '09 - High-tax state (CA)'
    year: 2026
    state: CA
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 100    # CA progressive brackets; first PE/RF audit
    people:
      - {age: 50, wages: 200000}
      - {age: 48, wages: 100000}

  - name: '10 - Large Roth conversion into 32% bracket (MFJ)'
    year: 2026
    state: OH
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 25
    people:
      - {age: 60, pension: 50000, roth_conversion: 300000}
      - {age: 60}

  - name: '11 - Two seniors MFJ, MAGI in enhanced-senior phase-out window'
    year: 2026
    state: OH
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 25
    people:
      # baseMagi ~$170k -- inside the $150k-$250k phase-out window for
      # both seniors. Pins the per-person 6c phase-out semantics.
      - {age: 67, pension: 50000, ira_distributions: 80000, social_security_gross: 30000}
      - {age: 65, pension: 30000, social_security_gross: 20000}

  - name: '12 - Single 67 standard deduction edge (no SS)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Income just above single std-ded ($16,100) + age-65 add ($2,050)
      # + OBBB senior ($6,000) = $24,150. Tests the deduction stack at
      # the edge.
      - {age: 67, pension: 25000}

  # ---------------------------------------------------------------------
  # Round-2 scenarios (added 2026-06-05 - corner cases that historically
  # produced engine bugs, including the MFJ-62/67-Ohio SS-torpedo case
  # that drove the simulateConversion fix earlier this session)
  # ---------------------------------------------------------------------

  - name: '13 - MFJ 62/67 Ohio Roth conversion (SS-torpedo regression)'
    year: 2026
    state: OH
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 50
    people:
      # The exact scenario the user reported when the standalone Roth
      # calculator showed wrong newMagi for a $30k conversion. PE
      # oracle pins: taxable SS at $51,000 cap, AGI $121,000 post-
      # conversion. If RF ever regresses on the SS-torpedo math this
      # scenario will flag it loudly.
      - {age: 67, wages: 40000, social_security_gross: 60000, roth_conversion: 30000}
      - {age: 62}

  - name: '14 - NIIT trigger edge (MFJ, high MAGI, cap gains)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Wages $200k + LTCG $80k -> AGI $280k -> $30k over the $250k MFJ
      # NIIT threshold. NIIT = 3.8% × min(NII=$80k, MAGI-threshold=$30k)
      #              = 3.8% × $30k = $1,140 of federal tax surcharge.
      # Tests both engines' NIIT pathway at a clean threshold-crossing.
      - {age: 55, wages: 200000, long_term_capital_gains: 80000}
      - {age: 55}

  - name: '15 - Pennsylvania (fully exempts IRA/pension)'
    year: 2026
    state: PA
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 50
    people:
      # PA does not tax qualified retirement distributions for taxpayers
      # 59.5+. Expected PA state tax: 0 (or very small if any income is
      # treated as PA-taxable). Tests RF's retirement_income_exempt
      # path against PE's PA implementation.
      - {age: 67, pension: 40000, ira_distributions: 30000, social_security_gross: 30000}
      - {age: 65, pension: 20000, social_security_gross: 20000}

  - name: '16 - Mississippi (Roth conversion taxable per audit)'
    year: 2026
    state: MS
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 50
    people:
      # MS exempts qualified retirement distributions but NOT Roth
      # conversions (per the June 2026 state-tax audit). Tests the
      # include_roth_conversion=False handling in RF.
      - {age: 67, pension: 30000, roth_conversion: 50000}
      - {age: 65}

  - name: '17 - Iowa (age-55 retirement exemption gate)'
    year: 2026
    state: IA
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 50
    people:
      # IA exempts retirement income for filers 55+. This scenario has
      # the primary at 60 (eligible) - exemption should apply.
      - {age: 60, pension: 50000, ira_distributions: 20000}
      - {age: 58}

  - name: '18 - North Carolina retirement scenario'
    year: 2026
    state: NC
    filing_status: mfj
    default_tolerance: 1
    # Tolerance bumped to $75 after the NC std-ded inflation opt-out
    # fix (2026-06-05); remaining ~$56 is a rate-basis disagreement
    # between RF (3.99% statutory) and PE (effective ~4.22%). Small
    # enough to leave; verify NC's published TY2026 rate when it's out.
    tolerances:
      state_income_tax: 75
    people:
      # NC: 4.25% flat (2025) -> 3.99% (2026). Taxes retirement income
      # at the flat rate. Tests the basic flat-rate path.
      - {age: 67, pension: 50000, social_security_gross: 30000}
      - {age: 65, social_security_gross: 20000}

  - name: '19 - IRMAA tier crossing (MFJ MAGI just past $342k)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # MAGI exactly above the $342k MFJ tier3 threshold. Tests that
      # both engines agree on AGI/MAGI when ordinary income is the
      # entire base (no SS feedback). The harness doesn't compare
      # IRMAA surcharges (PE's IRMAA is partial - see PE issue #8587
      # and the open IRMAA issues #8529/8095/8089/7967), but the
      # MAGI value should match exactly so the planner's IRMAA cap
      # logic has the right base.
      - {age: 67, wages: 0, pension: 0, ira_distributions: 343000}
      - {age: 65}

  # ---------------------------------------------------------------------
  # Round-3 scenarios (added 2026-06-05) - state diversity + corner cases.
  # Eight new states (IL, MA, NY, GA, CT, MN, MI, MD) plus three tax
  # phenomena not yet exercised: LTCG-dominant retirement, mandatory RMD
  # at age 73+, mixed LTCG+QD straddling the 0%->15% threshold.
  # State-tax tolerances are generous ($75) because RF and PE diverge on
  # many state implementations; the federal lines should still match
  # exactly.
  # ---------------------------------------------------------------------

  - name: '20 - Illinois retiree (fully exempts retirement income)'
    year: 2026
    state: IL
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 75
    people:
      # IL fully exempts qualified retirement distributions at ANY age
      # (no age gate, unlike PA's 59.5). Expected IL tax: $0.
      # Tests retirement_income_exempt_states['Illinois'] in RF.
      - {age: 68, pension: 45000, ira_distributions: 25000, social_security_gross: 30000}
      - {age: 66, pension: 20000, social_security_gross: 20000}

  - name: '21 - Massachusetts flat 5% retiree'
    year: 2026
    state: MA
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 75
    people:
      # MA flat 5%, no general retirement exclusion (some government
      # pensions excluded; ordinary 401k/IRA / private pensions are
      # taxable). MA fully exempts SS. Tests the simple flat-rate
      # path on retirement income.
      - {age: 67, pension: 40000, ira_distributions: 20000, social_security_gross: 30000}
      - {age: 65, social_security_gross: 20000}

  - name: '22 - New York $20k retirement income exclusion'
    year: 2026
    state: NY
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 100
    people:
      # NY excludes up to $20,000 of retirement income PER SPOUSE 59.5+
      # (Tax Law §612(c)(3-a)). NY also fully exempts SS. Both spouses
      # qualify here. Tests the per-person exclusion.
      - {age: 67, pension: 50000, ira_distributions: 25000, social_security_gross: 30000}
      - {age: 65, pension: 30000, social_security_gross: 20000}

  - name: '23 - Georgia retirement exemption ($65k/$130k MFJ at 65+)'
    year: 2026
    state: GA
    filing_status: mfj
    default_tolerance: 1
    # Tolerance widened 2026-06-05: PE applies the $65k cap PER SPOUSE
    # to PER-SPOUSE income (per O.C.G.A. §48-7-27(a)(5) implementation);
    # RF aggregates because calc_state_tax doesn't take per-spouse args.
    # For this lopsided household (primary $110k qualifying, spouse
    # $30k), RF over-exempts ($130k vs PE's $95k actual). Closing this
    # would require an API change to track per-spouse income.
    tolerances:
      state_income_tax: 800
    people:
      # GA exempts up to $65k of retirement income per filer 65+
      # ($130k MFJ both 65+). Lower limit ($35k) for 62-64. Tests the
      # generous senior exemption.
      - {age: 67, pension: 60000, ira_distributions: 50000, social_security_gross: 30000}
      - {age: 65, pension: 30000, social_security_gross: 20000}

  - name: '24 - Connecticut partial SS exemption (income-limited)'
    year: 2026
    state: CT
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 150
    people:
      # CT exempts SS for federal AGI below $100k single / $150k MFJ
      # (phased thereafter). Tests RF's social_security_partial_tax_states
      # path. This household has AGI well below the threshold so SS
      # should be fully exempt in CT.
      - {age: 67, pension: 40000, social_security_gross: 25000}
      - {age: 65, social_security_gross: 18000}

  - name: '25 - Minnesota partial SS exemption (high income, partial)'
    year: 2026
    state: MN
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 200
    people:
      # MN has an income-tiered SS subtraction. At higher income, the
      # exemption phases out. Tests the partial-state path with income
      # above the typical full-exemption threshold.
      - {age: 67, pension: 60000, ira_distributions: 40000, social_security_gross: 35000}
      - {age: 65, social_security_gross: 25000}

  - name: '26 - Michigan tiered retirement (born 1959+, post-2026 reforms)'
    year: 2026
    state: MI
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 200
    people:
      # MI has phasing in (over 4 years 2023-2026) full retirement
      # income deduction matching federal. By 2026, retirees born 1945+
      # can deduct retirement income fully (or claim std exemptions).
      # This pair: primary age 67 (born 1959), spouse 65 (born 1961).
      # Both qualify under the new MI rules. Tests the complex tiered
      # logic.
      - {age: 67, pension: 40000, ira_distributions: 30000, social_security_gross: 30000}
      - {age: 65, social_security_gross: 20000}

  - name: '27 - Maryland age-65 pension exclusion'
    year: 2026
    state: MD
    filing_status: mfj
    default_tolerance: 1
    # MD Form 502 line 22+ investigation 2026-06-05: identified PE's
    # missing piece as the MD Senior Tax Credit per Md. Tax-Gen
    # §10-754. Added to RF; this scenario now matches PE exactly at $0.
    # Tolerance restored to $1.
    tolerances:
      state_income_tax: 1
    people:
      # MD allows a pension exclusion (up to ~$36k for 2026, age 65+,
      # for "qualified" retirement income — pension and 401(k), NOT
      # IRA per audit findings). MD also fully exempts SS. Tests the
      # IRA-vs-pension distinction in MD's exclusion.
      - {age: 67, pension: 40000, ira_distributions: 20000, social_security_gross: 30000}
      - {age: 65, social_security_gross: 20000}

  - name: '28 - LTCG-heavy retiree (low ordinary, big realized gains)'
    year: 2026
    state: FL
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Retiree drawing primarily from taxable brokerage: small Social
      # Security + large LTCG. LTCG fits below single zeroMax ($49,450)
      # so most is 0% rate; portion above is 15%. Tests pure LTCG
      # stacking with SS at low/no taxability.
      - {age: 70, social_security_gross: 25000, long_term_capital_gains: 60000}

  - name: '29 - Single 73 mandatory RMD-driven withdrawal'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Single 73 with no other income except SS + a large IRA
      # withdrawal at typical RMD scale. Tests SS taxability at the
      # 85% cap, single-filer enhanced senior deduction phase-out
      # if MAGI crosses $75k, and high-income SS-torpedo math from
      # the single side.
      - {age: 73, ira_distributions: 75000, social_security_gross: 30000}

  - name: '30 - LTCG + QD straddling 0%->15% threshold (single)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Single filer with ordinary income that fills part of the
      # bracket, then LTCG+QD that straddles the $49,450 zeroMax.
      # Validates that calculate_capital_gains_tax in RF correctly
      # splits LTCG across the 0% and 15% bands when ordinary
      # taxable income partially fills below the zeroMax.
      - {age: 55, wages: 40000, long_term_capital_gains: 20000, qualified_dividends: 5000}

  # --------------------------------------------------------------------------
  # Head of Household and Qualifying Surviving Spouse
  #
  # Added with phase 2 of docs/filing-statuses-design.md, which makes "every
  # federal scenario matches PolicyEngine exactly" a condition of merging
  # rather than a follow-up.
  #
  # All of these sit in NO-INCOME-TAX states on purpose. The state tables are
  # single/married only until phase 3, so RF deliberately degrades HoH and QSS
  # to Single at the state level. Putting these scenarios in a taxing state
  # would produce a known, scheduled state mismatch and bury the federal
  # assertion these exist to make.
  #
  # Scenarios 33-35 pin the three values that LOOK like bugs. If one of them
  # starts failing, read validation/policyengine/HOH_FINDINGS.md before
  # changing a constant.
  # --------------------------------------------------------------------------

  - name: '31 - HoH, SS + IRA withdrawal (TX)'
    year: 2026
    state: TX
    filing_status: hoh
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # The ordinary case the feature exists for: a 67-year-old supporting a
      # qualifying person. Exercises the HoH standard deduction (24,150) and
      # the wider 10%/12% bands against the single-filer SS taxability bases,
      # which HoH shares.
      - {age: 67, ira_distributions: 55000, social_security_gross: 28000}

  - name: '32 - HoH, LTCG straddling the 0%->15% breakpoint (TX)'
    year: 2026
    state: TX
    filing_status: hoh
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # HoH's 0% LTCG ceiling is 66,200 against Single's 49,450. A filer whose
      # gains straddle the SINGLE breakpoint but sit below the HoH one should
      # owe no LTCG tax at all - the cheapest possible way to catch the LTCG
      # table being read from the wrong status.
      - {age: 60, wages: 30000, long_term_capital_gains: 25000}

  - name: '33 - HoH high income, the 24% ceiling that is NOT Single (TX)'
    year: 2026
    state: TX
    filing_status: hoh
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # LOOKS LIKE A TYPO, IS NOT. HoH's 24% ceiling is 201,750 - exactly $25
      # BELOW Single's 201,775 - while the 22% and 35% ceilings match Single
      # exactly. HOH_FINDINGS.md records the TY2025 form of this quirk, where
      # the 24% ceiling DID match; that is no longer true at TY2026, which is
      # the year the engine's base table holds. Income here is placed above
      # the ceiling so the $25 is actually walked.
      - {age: 58, wages: 260000}

  - name: '34 - QSS, the SS torpedo at the SINGLE base (FL)'
    year: 2026
    state: FL
    filing_status: qss
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # THE ONE THAT MATTERS MOST. A QSS filer uses the SINGLE Social Security
      # taxability bases of 25,000 / 34,000, not JOINT's 32,000 / 44,000,
      # while using the joint rate schedule and the joint 32,200 standard
      # deduction. Provisional income here lands between the two sets of
      # bases, so an implementation that treats QSS as "MFJ minus a spouse"
      # reports zero taxable SS and this scenario fails.
      - {age: 66, ira_distributions: 22000, social_security_gross: 26000}

  - name: '35 - QSS at 65, the additional deduction that is SMALLER (FL)'
    year: 2026
    state: FL
    filing_status: qss
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # ALSO NOT A BUG. A QSS filer aged 65+ receives the MARRIED age-65
      # additional standard deduction of 1,650 - four hundred dollars LESS
      # than the 2,050 a Single filer of the same age gets. Income is kept
      # clear of the OBBB senior phase-out so this scenario isolates the
      # age-65 add-on rather than mixing it with the phase-out.
      - {age: 70, ira_distributions: 45000}

  - name: '36 - QSS, joint rate schedule and joint LTCG breakpoints (FL)'
    year: 2026
    state: FL
    filing_status: qss
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # The other half of the QSS story: everything NOT called out above
      # follows JOINT. Gains here clear Single's 49,450 ceiling but sit under
      # the joint 98,900 one, so a QSS filer built on the Single tables would
      # over-tax them.
      - {age: 62, wages: 45000, long_term_capital_gains: 40000}

  # --- Self-employment income -------------------------------------------
  # Added 2026-08-16. Every scenario above this point uses wages, pensions,
  # IRA distributions or investment income - NOT self-employment - so the
  # "federal matches exactly" result had never once been tested against a
  # self-employed household, even though compare.py has always been able to
  # express the field. The frontend Roth calculator's oracle found the gap
  # first: the Section 199A qualified business income deduction is not
  # modelled, which overstates federal tax for anyone with SE income.
  #
  # These are expected to FAIL on federal until QBI is modelled. That is the
  # point - a measured gap, in the suite, rather than a silence.
  #
  # NOTE: no scenario mixes self-employment with WAGES. The engine's
  # `is_self_employed` is a boolean over the whole of gross_income, so a mixed
  # household would tax the wages as self-employment too - a mismatch that says
  # nothing about the engine. compare.py raises rather than letting one through.
  # Social Security, pensions and investment income mix freely; they are passed
  # separately or are not earned income.

  - name: '37 - Single, self-employment only (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # The clean case: nothing but SE income, so any federal difference is
      # the SE treatment and nothing else. 90k is comfortably inside the
      # 199A threshold, where the deduction is a plain 20% limited by taxable
      # income - no SSTB phase-out, no W-2 wage limit.
      - {age: 55, self_employment: 90000}


  - name: '38 - Single 68, SS + self-employment (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # SE income feeds provisional income through AGI, so a wrong SE
      # adjustment moves the taxable portion of Social Security as well as
      # taxable income. Retirees with consulting income are the commonest
      # real instance of this combination.
      - {age: 68, self_employment: 45000, social_security_gross: 36000}

  - name: '39 - Single, self-employment + large LTCG (QBI capital-gain limit) (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # The QBI deduction is capped at 20% of taxable income LESS net capital
      # gain. With gains this large the cap binds and is far below 20% of the
      # business income, so an implementation that used plain taxable income -
      # the easy mistake - over-allows the deduction and under-taxes. Every
      # other SE scenario here would still pass with that bug.
      - {age: 58, self_employment: 60000, long_term_capital_gains: 120000}

  # ── OBBB §170(p) charitable deduction for non-itemizers (TY2026+) ────────
  #
  # Every scenario keeps donations small and carries no SALT or mortgage
  # interest, so the standard deduction wins and PE stays on the non-itemizing
  # path. That is the ONLY path RF models - we do not model Schedule A at all -
  # so a scenario that tipped PE into itemizing would be comparing two
  # different things and reporting it as an engine bug.
  #
  # TX throughout: the deduction reduces federal taxable income and never AGI,
  # and state tax keys off federal AGI, so a state with income tax would add
  # noise without testing anything extra.

  - name: '40 - Single, charitable giving above the cap (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # $5,000 given, $1,000 deductible. Catches a missing cap, which would
      # deduct the full gift.
      - {age: 60, ira_distributions: 80000, charitable_cash_donations: 5000}

  - name: '41 - MFJ, charitable giving above the cap (TX)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # $2,000 for a joint return, not $1,000 and not two separate $1,000
      # caps. The per-return-vs-per-person error is invisible on a single
      # filer, so MFJ is where it shows up.
      - {age: 62, ira_distributions: 120000, charitable_cash_donations: 5000}
      - {age: 60}

  - name: '42 - MFJ, split gifts across both spouses (TX)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # $1,200 + $1,500 = $2,700 given, still capped at $2,000 for the return.
      # An implementation that capped per PERSON would allow $2,700 in full.
      - {age: 62, ira_distributions: 120000, charitable_cash_donations: 1200}
      - {age: 60, charitable_cash_donations: 1500}

  - name: '43 - Single, giving BELOW the cap (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # $400 given, $400 deductible - min(gift, cap), not the cap itself.
      # An implementation that always granted the cap passes every scenario
      # above and fails only this one.
      - {age: 60, ira_distributions: 80000, charitable_cash_donations: 400}

  - name: '44 - HOH, charitable giving above the cap (TX)'
    year: 2026
    state: TX
    filing_status: hoh
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # HOH takes $1,000, not the joint $2,000, even though it takes the
      # single age-65 additional and its own bracket schedule elsewhere.
      - {age: 60, ira_distributions: 80000, charitable_cash_donations: 5000}

  - name: '45 - MFJ senior, charitable on top of the senior deductions (TX)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Stacked with the OBBB senior bonus and the §63(f) age-65 additional.
      # The charitable deduction is allowed IN ADDITION to the standard
      # deduction, so this catches an implementation that treated it as an
      # alternative to part of it, or that let it be inflation-scaled with the
      # other components.
      - {age: 70, ira_distributions: 100000, social_security_gross: 30000, charitable_cash_donations: 5000}
      - {age: 68}

  # ── Interest income ────────────────────────────────────────────────────
  # Added 2026-08-26. The corpus had NO interest income of any kind across
  # its 147 scenarios, so nothing here was ever checked against PolicyEngine:
  # not taxable interest, not its effect on Social Security provisional
  # income, and not the state treatment. That hole is why the missing
  # Treasury state exemption went unnoticed - the oracle could not have
  # caught it, because no scenario could express the input.

  - name: '46 - Single, taxable interest only, no state tax (TX)'
    year: 2026
    state: TX
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # The simplest possible interest case. Ordinary income, no preferential
      # rate, no state layer - isolates federal treatment of interest.
      - {age: 55, taxable_interest: 40000}

  - name: '47 - Single, interest plus IRA in a high-tax state (CA)'
    year: 2026
    state: CA
    filing_status: single
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Interest stacked on ordinary income where the state rate bites.
      - {age: 58, taxable_interest: 25000, ira_distributions: 60000}

  - name: '48 - MFJ seniors, interest lifts SS into taxability (TX)'
    year: 2026
    state: TX
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # The case that matters most and was never covered: interest counts in
      # PROVISIONAL income, so it drags Social Security into taxability even
      # though the interest itself is ordinary. A plan holding cash at
      # current yields hits this constantly.
      - {age: 70, social_security_gross: 36000, taxable_interest: 30000}
      - {age: 68, social_security_gross: 18000}

  - name: '49 - MFJ seniors, interest with an IRA draw in a high-tax state (NY)'
    year: 2026
    state: NY
    filing_status: mfj
    default_tolerance: 1
    tolerances:
      state_income_tax: 0
    people:
      # Interest + IRA + SS in a state with its own retirement-income rules,
      # so the state subtraction and the interest have to coexist correctly.
      - {age: 72, social_security_gross: 34000, ira_distributions: 50000, taxable_interest: 20000}
      - {age: 71, social_security_gross: 22000}
