Tax penalties annual
Early-withdrawal penalties plus the IRC §4974 RMD-shortfall excise for the year; never part of tax, AGI or MAGI.
Field: Penalties and excise tax Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain
Appears in: csv (penalties); scenarios-page (Annual "Penalties" and lifetime "Lifetime penalties"); cash-flow-drilldown (earlyWithdrawalPenalty use line); mcp (run_projection.years[].penalties)
Calculation records
-
Annual penalties: early withdrawal plus the IRC 4974 excise Composition record, justified by derivation
Purpose: The two penalty channels a year composes, and why neither is part of tax.
Statement: projection/internal/types/result.ts#YearResult.penalties is composed by projection/internal/annualFundingCandidateEvaluation.ts#annualFundingCandidateEvaluation as projection/internal/annualFundingWithdrawalEffects.ts#annualFundingWithdrawalEffects reporting penaltyExcludingRmdShortfallExcise plus the IRC 4974 excise from rmd/rmdShortfallExcise.ts#computeRmdShortfallExcise. The early-withdrawal rate is 10 percent on pre-age-59-and-a-half taxable traditional withdrawals, inherited distributions are never subject to it, and the excise prices max(0, required - distributed by deadline) at the stated rate, whose post-SECURE-2 default is 25 percent, once for each obligation. In the calendar year a surviving spouse's election to treat an inherited IRA as her own takes effect, after the year of the owner's death, that IRA owes only its owner RMD (Treas. Reg. 1.408-8(c)(3)) and leaves the beneficiary's same-decedent group ((e)(2)(i)), so projection/internal/annualInheritedIraDistributions.ts#annualInheritedIraDistributions puts it in no beneficiary obligation. Penalties stay outside tax, AGI and MAGI. Units: nominal USD per year. Rounding: none.
Formula:
penalties = penaltyExcludingRmdShortfallExcise + rmdShortfallExciseTax; early = 0.10 x penalizable traditional; excise = rate x max(0, required - distributed)-
penalizable traditional: Taxable pre-59.5 traditional withdrawal (unit: usd) (domain: nonnegative; 0 for inherited accounts) -
required: Required minimum for the obligation (unit: usd) (domain: nonnegative) -
distributed: Distributed by the statutory deadline (unit: usd) (domain: nonnegative) -
rate: IRC 4974 rate (unit: 1) (domain: 0.25 default from 2023)
Timing: once per accepted funding evaluation in the projection year
Rounding: none
Limits:
- Asserted twice. At the two component producers: annualFundingWithdrawalEffects reports 2,000 of penaltyExcludingRmdShortfallExcise on the worksheet's 20,000 pre-59.5 taxable traditional withdrawal, and computeRmdShortfallExcise prices the worksheet's 12,000 required against 4,000 distributed as an 8,000 shortfall and a 2,000 excise at the default 25-percent rate with no relief elected. And as the published penalties of a real simulatePlan 2026 row that carries both channels at once: a 50-year-old whose only portfolio is a traditional IRA and whose 16,000 of required lifestyle plus the 2,000 excise drive a need-based withdrawal of exactly 20,000 under a zero-rate test calculator, alongside an inherited Roth account whose completed five-year deadline observation (opening benefit 12,000, 4,000 distributed by the 2026 deadline) prices the excise without replaying any cash
- Plan assumptions beyond the worksheet's inputs for that ledger year: filing single in KY at a zero state rate with a zero-rate test tax calculator, so tax is 0 and the whole need is spending plus penalties; the test asserts the 10-percent rate relation W = S + excise + 0.10 W closes at exactly 20,000
- The worksheet's fourth wrong reading is asserted as a rule: the same withdrawal-effects call on an inherited traditional account reports a zero penalty
- Restated 2026-09-30 and reviewed by Grok the same day: in an election year the elected IRA enters no beneficiary obligation. Asserted in simulate.spousalElectionYearOwnerRmd.test.ts on a 75-year-old spouse whose mid-2026 election takes effect: the owner RMD of 4,065.04 (100,000 / 24.6) is paid by 1,000 before the election and 3,065.04 after and is the one obligation, and the beneficiary figure of 6,689.19 (99,000 / 14.8) stays on the account's evidence as the trigger only, so the excise is 0 and not the 1,672.30 a second, unpaid obligation would charge
Provenance: derived by
codex, implemented byclaude.Independent review:
grokGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: tax-penalties-annual.md Mutation receipt: tax-penalties-annual.mutation.md
Evidence tests: annualFundingCandidateEvaluation.evidence.test.ts
Implemented in: result.ts; annualFundingCandidateEvaluation.ts; annualFundingWithdrawalEffects.ts; rmdShortfallExcise.ts; annualInheritedIraDistributions.ts
Is the number of: tax-penalties-annual
Feeds: portfolio-need-annual, display-total-spending-annual, display-tax-plus-penalties-annual, scenario-lifetime-penalties
-
Records that feed this number
-
Civil date parse format and month shift Formula record, justified by derivation
Purpose: Parse, format and shift calendar dates without timezone rollover.
Statement: actions/civilDate.ts#parseCivilIsoDate accepts only canonical valid proleptic-Gregorian YYYY-MM-DD dates, formatCivilDate emits that form, and addCalendarMonths shifts by whole calendar months while preserving the day or clamping to the target month's last day.
Formula:
monthIndex = 12 year + month - 1 + shift; dayOut = min(day, daysInTargetMonth)-
date: Canonical Gregorian civil date (unit: YYYY-MM-DD) (domain: valid year 1..9999) -
shift: Calendar month displacement (unit: months) (domain: safe integer)
Timing: civil calendar; no timezone
Rounding: whole months; clamp day
Limits:
- No direct census family: actions/annualHsaPenaltyEvaluation.ts consumes addCalendarMonths for the age-65 date and exact-cent penalty evidence feeding tax-penalties-annual.
Provenance: derived by
codex, implemented bycodex.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: civil-date-parse-format-and-month-shift.md Mutation receipt: civil-date-parse-format-and-month-shift.mutation.md
Evidence tests: civilDate.evidence.test.ts
Implemented in: civilDate.ts
Feeds: tax-penalties-annual
-
-
Exact cent pro rata half up Formula record, justified by derivation
Purpose: Round a rational share of an exact-cent amount with halves upward.
Statement: actions/exactCentProRata.ts#exactCentProRataNearestHalfUp computes amount*numerator/denominator in exact nonnegative integer minor units and rounds the single rational result to the nearest minor unit with exact halves upward.
Formula:
N = A n; q = floor(N/d); r = N mod d; result = q + [2r >= d]-
A: Amount (unit: cents) (domain: nonnegative integer) -
n/d: Allocation ratio (unit: 1) (domain: integer n >= 0, integer d > 0)
Timing: one allocation
Rounding: exact integer half up
Limits:
- No direct census family: actions/annualHsaPenaltyEvaluation.ts consumes the pro-rata amount for finalPenaltyAmount. Callers validate the domain.
Provenance: derived by
codex, implemented bycodex.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: exact-cent-pro-rata-half-up.md Mutation receipt: exact-cent-pro-rata-half-up.mutation.md
Evidence tests: exactCentProRata.evidence.test.ts
Implemented in: exactCentProRata.ts
Feeds: tax-penalties-annual
-
-
Annual inherited-account required amount Formula record, justified by derivation
Purpose: The legal-year requirement on an inherited account, before any comparison to the live balance.
Statement: strategies/inheritedIra.ts#inheritedRequirementForYear publishes the annual inherited-account requirement from the prior-December-31 balance. A beneficiary fixed Single Life Table divisor is read once at the beneficiary's age in the year after death and declines by exactly one per later calendar year; for a traditional account inherited on or after the owner's required beginning date under the eligible-designated-beneficiary life-expectancy regime, the greater of the beneficiary's fixed divisor and the owner's fixed divisor governs, so the longer life expectancy produces the smaller required amount; a no-annual-requirement window publishes 0; and the final deadline year requires the full prior-year-end balance, reconciled to the live balance at execution. The 2026 tax parameters supply 14.8 at age 75, so the next fixed divisor is 13.8, not the age-76 entry 14.1; an owner aged 86 in the death year starts from the entry 7.6 and so stands at 6.6 in the first year after the death year. Units: nominal USD per year. Rounding: none.
Formula:
annual: required = B / d, d read once at the first distribution year and reduced by one per later year; none: 0; final sweep: required = B-
B: Prior-December-31 balance (unit: usd) (domain: nonnegative) -
d: Fixed Single Life divisor, minus elapsed years (unit: years) (domain: positive; <= 1 becomes a sweep)
Timing: once per inherited account per calendar year
Rounding: none
Limits:
- All six of the worksheet's cases asserted at the exported calculator with the stated year, prior-year-end balance and divisors verbatim: the 2027 first-year beneficiary arm, the 2028 fixed-minus-one continuation, both post-RBD rows in the first year after the death year, the pre-RBD ten-year no-annual window, and the 2036 final sweep. Plan assumptions beyond the worksheet's inputs: the annual arm and both post-RBD rows are an eligible designated beneficiary (disabled) born so the stated age falls in the stated year, the post-RBD rows add an owner born in 1940 (age 86 in the 2026 death year) who had started RMDs and whose death-year RMD was satisfied, the no-annual and sweep cases are a non-eligible designated beneficiary whose owner died in 2026 before the required beginning date, and every case is a sole designated-individual beneficiary of a traditional IRA with asserted provenance
- The first derivation stated the post-RBD rule as the greater required amount, max(B / beneficiary divisor, B / owner divisor), and used an owner-fixed divisor of exactly 10.8 that no 2026 Single Life Table entry can produce; that derivation was corrected on 2026-09-18. The worksheet now states the comparison production holds: the greater DIVISOR (`if (ownDiv > divisor) divisor = ownDiv`), which is the longer life expectancy and so the smaller amount, per Treas. Reg. 1.401(a)(9)-5(d)(1)(ii). The worksheet reaches the owner arm from the age-86 death-year entry 7.6, giving 6.6 in the first year after the death year. Both winning arms are now constructed and asserted: a beneficiary aged 75 keeps 14.8 and publishes 10,000, and a beneficiary aged 90 at 5.7 loses to the owner's 6.6 and publishes 22,424.242424. No production-versus-worksheet discrepancy remains
Provenance: derived by
codex, implemented byclaude.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: inherited-distribution-required-annual.md Mutation receipt: inherited-distribution-required-annual.mutation.md
Evidence tests: inheritedIra.evidence.test.ts
Implemented in: inheritedIra.ts
Is the number of: inherited-distribution-required-annual
Feeds: inherited-distribution-required-executed-annual, tax-penalties-annual
-
-
RMD shortfall excise at the default rate Formula record, justified by derivation
Purpose: Price a missed RMD at the post-SECURE-2 default excise rate without touching income or balances.
Statement: rmd/rmdShortfallExcise.ts#computeRmdShortfallExcise prices a post-SECURE-2 annual RMD shortfall as the nonnegative required amount less timely distributions, times the default 25% rate carried by RMD_SHORTFALL_DEFAULT_RATE, without changing income or account balances. The reason is default25Percent when no corrective distribution, discretionary waiver or automatic waiver applies and the tax year is 2023 or later. Units: nominal USD. Rounding: none.
Formula:
S = max(0, R - D); tax = S x q; reason = default25Percent when no relief applies and taxYear >= 2023-
R: Required amount for the obligation (unit: usd) (domain: nonnegative after the floor) -
D: Amount distributed by the statutory deadline (unit: usd) (domain: nonnegative after the floor) -
q: RMD_SHORTFALL_DEFAULT_RATE (unit: fraction) (domain: 0.25)
Timing: one obligation in one tax year
Rounding: none
Limits:
- The excise is a penalty, never tax: it joins the published penalties line and never AGI, MAGI or the tax line
- The corrected 10% rate, the discretionary waiver and the two automatic waivers are separate branches this record does not claim; the asserted case elects none of them
- Pre-2023 tax years take the 50% pre-SECURE-2 rate on the same shortfall arithmetic
Provenance: derived by
codex, implemented byclaude.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: rmd-shortfall-excise-default.md Mutation receipt: rmd-shortfall-excise-default.mutation.md
Evidence tests: rmdShortfallExcise.evidence.test.ts
Implemented in: rmdShortfallExcise.ts
Feeds: tax-penalties-annual
-
-
Usd cent arithmetic Formula record, justified by derivation
Purpose: Add, subtract and sum validated US cents exactly.
Statement: actions/money.ts#addUsdCents, subtractUsdCents, and sumUsdCents perform exact arithmetic on validated nonnegative integer US cents, with subtraction valid only when the result remains in that domain.
Formula:
add=a+b; subtract=a-b; sum=sum_i a_i-
a,b,a_i: Validated currency minor units (unit: US cents) (domain: nonnegative safe integers; a >= b for subtraction)
Timing: one exact money operation
Rounding: none; results validated as safe integer cents
Limits:
- No direct census family: actions/annualHsaPenaltyEvaluation.ts consumes addUsdCents to aggregate exact penalties feeding tax-penalties-annual. Overflow and negative subtraction reject rather than returning an out-of-domain amount.
Provenance: derived by
codex, implemented bycodex.Independent review:
cursorGates (6 of 6 pass): fixture registers a test: pass; pins resolve: pass; families exist: pass; worksheet exists: pass; mutation receipt exists: pass; provenance independent: pass
Worksheet: usd-cent-arithmetic.md Mutation receipt: usd-cent-arithmetic.mutation.md
Evidence tests: money.evidence.test.ts
Implemented in: money.ts
Feeds: tax-penalties-annual
-
Engine source: engine/src/projection/internal/types/result.ts
(YearResult.penalties)
The evidence links on this page (each record's worksheet, mutation receipt, evidence tests
and implementation, and the source links) are pinned to engine commit
2624dbdba9fd, committed 2026-10-11.
The live calculation index and
output-family census links point at the repository's main
branch, so you can check this snapshot against them.