Calculation catalog › Spending and withdrawals
Withdrawals by category annual
Dollars withdrawn this year from cash, taxable, traditional, Roth and HSA accounts.
Field: Withdrawals by account category Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain
Appears in: scenarios-page (Annual "Traditional withdrawals" / "Roth withdrawals" and lifetime withdrawals by category); cash-flow-drilldown (needBasedPortfolioWithdrawal and retirementActionWithdrawal source lines); mcp (run_projection.years[].withdrawals.cash / .taxable / .traditional / .roth / .hsa)
Calculation records
-
Annual withdrawals partitioned by source-account category Composition record, justified by derivation
Purpose: Which bucket each withdrawn dollar is reported in, and which named amounts are subsets rather than extra categories.
Statement: projection/internal/types/yearLedger.ts#YearWithdrawals partitions the year's draws by SOURCE ACCOUNT category in the sequential order cash, taxable, traditional, roth, hsa. projection/internal/annualFundingApplicationAndClosePhase.ts#annualFundingApplicationAndClosePhase assembles the reported vector: traditional carries the planned traditional draw plus the owner RMD total, the SEPP total and the forced dollars executed from inherited traditional accounts; roth carries the planned Roth draw plus forced inherited Roth dollars, taxable earnings included; cash and taxable carry the retirement-action cash, equity-compensation and taxable proceeds. The separately published rmd, sepp and inheritedDistribution are SUBSETS of those categories, not extra categories and not amounts to add again; inheritedTraditionalDistribution is the ordinary income from inherited accounts, whose traditional rows are inside traditional and whose non-qualified Roth earnings, if any, are inside roth (decision D-INHERITED-ROTH-SLICE, 2026-09-25). Units: nominal USD per year. Rounding: none.
Formula:
traditional = plannedTraditional + rmdTotal + seppTotal + inheritedTraditionalForced; roth = plannedRoth + inheritedRothForced; hsa = plannedHsa; cash and taxable add the retirement-action proceeds-
plannedX: The withdrawal planner's draw from category X (unit: usd/year) (domain: nonnegative) -
rmdTotal, seppTotal: Forced owner distributions, reported inside traditional (unit: usd/year) (domain: nonnegative subsets) -
inheritedTraditionalForced, inheritedRothForced: Forced inherited dollars, split by the type of the inherited account they are withdrawn from (unit: usd/year) (domain: nonnegative subsets)
Timing: once per projection year, at the funding-and-close phase
Rounding: none
Limits:
- Asserted on a real simulatePlan run whose five accounts hold exactly the worksheet's five category amounts and whose spending exceeds their sum, so each account drains and its category equals its own opening balance. Plan assumptions beyond the worksheet's inputs: a single 76-year-old filing single in KY with a zero state rate, zero inflation, zero account return, no taxable yield, cost basis equal to balance, sequential withdrawal order, and a base lifestyle far above the portfolio
- The worksheet's stated subset split ($8,000 of RMD, $3,000 of SEPP, $2,000 of forced inherited traditional inside $18,000 of traditional, and $1,000 of forced inherited Roth inside $7,000 of Roth) is NOT constructed. Those four amounts are each determined by the engine from age, balance and beneficiary facts, and no plan sets them to chosen values inside a traditional draw that must also equal $18,000. The run's own RMD is asserted instead to be a nonzero subset of the published traditional category, which is the claim the worksheet's first wrong reading denies
- HSA is a withdrawal category like the other four, not a sixth non-withdrawal bucket
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: withdrawals-by-category-annual.md Mutation receipt: withdrawals-by-category-annual.mutation.md
Evidence tests: annualFundingApplicationAndClosePhase.evidence.test.ts
Implemented in: yearLedger.ts; annualFundingApplicationAndClosePhase.ts
Is the number of: withdrawals-by-category-annual
Feeds: withdrawals-total-annual
-
Records that feed this number
-
Annual forced inherited distribution Composition record, justified by derivation
Purpose: The gross forced cash every inherited account is required to distribute this year.
Statement: projection/internal/types/result.ts#YearResult.inheritedDistribution publishes the sum of every inheritedAccounts[] row's executed required amount (annual, year-of-death and final-sweep alike) across traditional and Roth inherited accounts; voluntary amounts are excluded. #YearResult.inheritedTraditionalDistribution publishes the phase's ordinary-income total from projection/internal/annualInheritedIraDistributions.ts#AnnualInheritedIraDistributionsResult: each traditional row in full plus a non-qualified inherited Roth distribution's characterized taxable earnings, which are ordinary income under IRC 408A(d). It is the ordinary income from inherited accounts, not a withdrawal category: the Roth earnings in it are withdrawn from a Roth account and are counted in withdrawals.roth, not withdrawals.traditional. Units: nominal USD per year. Rounding: none.
Formula:
inheritedDistribution = sum of executed required amounts; inheritedTraditionalDistribution = traditional executed + characterized Roth ordinary income-
executed: Executed required amount on one inherited row (unit: usd) (domain: nonnegative) -
Roth taxable slice: Characterized ordinary income inside a Roth forced row (unit: usd) (domain: 0 <= slice <= that row's gross)
Timing: once per projection year, over every inherited row
Rounding: none
Limits:
- Decision D-INHERITED-ROTH-SLICE (2026-09-25) settled the meaning of inheritedTraditionalDistribution as the ordinary income from inherited accounts, the composition the code publishes: an earlier comment excluded Roth dollars outright, and the field comment now says what the figure is. The 8,600 is asserted, and separately the Roth row's 3,000 gross and 600 slice. The same decision moved the withdrawal categories so the 600 is counted in roth only
- Asserted at the exported phase with the worksheet's three rows realized from plan facts: a traditional annual-RMD row whose 8,000 executed amount is min(required, live balance) at a live balance of exactly 8,000, a Roth final-sweep row executing its whole 3,000 live balance with an injected characterization returning the worksheet's 600 of ordinary income, and a traditional row inside a ten-year window with no annual requirement. Plan assumptions beyond the worksheet's inputs: each account is a sole designated-individual beneficiary IRA with asserted provenance and a 2026 owner death, and the voluntary amounts the worksheet lists belong to a later phase and are not supplied here; the test asserts that this phase writes zero voluntary on every row
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-forced-annual.md Mutation receipt: inherited-distribution-forced-annual.mutation.md
Evidence tests: annualInheritedIraDistributions.evidence.test.ts
Implemented in: result.ts; annualInheritedIraDistributions.ts
Is the number of: inherited-distribution-forced-annual
Feeds: withdrawals-by-category-annual, withdrawals-total-annual
-
-
Executed inherited required distribution Formula record, justified by derivation
Purpose: The cash a required inherited distribution actually debits, once the live balance is consulted.
Statement: projection/internal/annualInheritedIraDistributions.ts#annualInheritedIraDistributions writes #AnnualInheritedIraDistributionOperation.executed as min(required amount, live balance) in an ordinary required year, the full live balance in a final-sweep year, and 0 when the requirement is none or notice-waived. Requirement evidence stays immutable at its legal-year amount; only the executed figure is capped. Units: nominal USD per year. Rounding: none.
Formula:
ordinary: executed = min(R, L); final sweep: executed = L; none or notice-waived: executed = 0-
R: Required amount from the requirement evidence (unit: usd) (domain: nonnegative) -
L: Live account balance at the forced-distribution phase (unit: usd) (domain: nonnegative)
Timing: once per inherited account per projection year, before the funding solve
Rounding: none
Limits:
- All five cases asserted at the exported phase with the worksheet's required amounts, live balances and requirement statuses realized from plan facts. Plan assumptions beyond the worksheet's inputs: an 8,000 requirement is realized as a 320,000 prior-year-end balance over an exact 40.0 fixed Single Life divisor (a beneficiary aged 46 in the first distribution year), so the quotient is exactly the worksheet's whole dollar; the final sweep is a non-eligible designated beneficiary reaching deathYear + 10; the none case is the same beneficiary inside the ten-year window; and the notice-waived case is a post-RBD ten-year-with-annual-RMDs row for a 2022 death in relief year 2024, whose 312,000 prior-year-end balance over the 39.0 continuation divisor is again exactly 8,000
- The requirement evidence is asserted alongside the executed amount in every case, so a cap can never be mistaken for a smaller requirement
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-executed-annual.md Mutation receipt: inherited-distribution-required-executed-annual.mutation.md
Evidence tests: annualInheritedIraDistributions.evidence.test.ts
Implemented in: annualInheritedIraDistributions.ts
Is the number of: inherited-distribution-required-executed-annual
Feeds: inherited-distribution-forced-annual, withdrawals-by-category-annual
-
-
Voluntary inherited-account draw Composition record, justified by derivation
Purpose: The ordinary withdrawal plan's extra draw from an inherited account, kept apart from its forced take.
Statement: projection/internal/annualWithdrawalApplyFlowPlan.ts#annualWithdrawalApplyFlowPlan writes #AnnualWithdrawalEvidenceWrite.voluntaryAmount as the ordinary withdrawal plan's draw from that inherited account, taken from the plan by account id after the forced required take has already been classified. An account for which treat-as-own is effective this year is routed as owner treatment and receives no inherited evidence write at all. Units: nominal USD per year. Rounding: none.
Formula:
voluntaryAmount = withdrawnByAccountId(accountId), or no write when treat-as-own is effective-
withdrawnByAccountId: Accepted ordinary withdrawal plan by account (unit: usd) (domain: nonnegative) -
treat-as-own effective: Annual owner-treatment routing for the account (unit: boolean) (domain: IRA, death year >= 2020, year > death year)
Timing: once per projection year, in the apply-flow phase after forced distributions
Rounding: none
Limits:
- Asserted at the exported phase with the worksheet's remaining balance, ordinary plan draw and treat-as-own flag verbatim: the account enters the phase at 95,000, already net of the 5,000 forced take, and the plan draws 12,000. The 5,000 forced take is not re-supplied to this phase, which is the point of the separation, so the test asserts that the sum of the two is the account's 17,000 total distribution while only 12,000 is voluntary
- The treat-as-own case is asserted with the same inputs and an accepted owner-treatment route for the account, where the phase emits no inherited evidence write; it needs an IRA whose owner died in 2020 or later and a year after the death year, which the test supplies
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-voluntary-annual.md Mutation receipt: inherited-distribution-voluntary-annual.mutation.md
Evidence tests: annualWithdrawalApplyFlowPlan.evidence.test.ts
Implemented in: annualWithdrawalApplyFlowPlan.ts
Is the number of: inherited-distribution-voluntary-annual
Feeds: withdrawals-by-category-annual, withdrawals-total-annual
-
Engine source: engine/src/projection/internal/types/yearLedger.ts
(YearWithdrawals.cash / taxable / traditional / roth / hsa)
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.