Calculation catalog › Spending and withdrawals
Withdrawals total annual
Total account withdrawals used in a projection year.
Field: Total withdrawals Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain
Appears in: cash-flow-drilldown (uses/withdrawals); csv (withdrawals); results-table (Withdrawals column); report (year-ledger block withdrawals); scenarios-page (Annual "Total withdrawals"); mcp (run_projection.years[].withdrawals.total)
Calculation records
-
Annual withdrawal total Composition record, justified by derivation
Purpose: The single figure for everything drawn from the portfolio this year.
Statement: projection/internal/types/yearLedger.ts#YearWithdrawals.total is cash + taxable + traditional + roth + hsa for the published year row. projection/internal/annualWithdrawalPlanning.ts#annualWithdrawalPlan folds the planned categories into byCategory.total in that operand order, and projection/internal/annualFundingApplicationAndClosePhase.ts#annualFundingApplicationAndClosePhase publishes the reported total as that planned total plus the forced amounts each category also carries: the RMD and SEPP totals, the inherited distributions and the retirement-action proceeds. Each forced inherited dollar is in exactly one category, traditional for a traditional account and roth for a Roth account, the taxable earnings of a non-qualified inherited Roth distribution included (decision D-INHERITED-ROTH-SLICE, 2026-09-25; before it those earnings were also added to traditional, and the categories exceeded the total by them). Units: nominal USD per year. Rounding: none.
Formula:
total = cash + taxable + traditional + roth + hsa-
cash, taxable, traditional, roth, hsa: Published withdrawal totals by source-account category (unit: usd/year) (domain: nonnegative)
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 sequential ordering drains every one of them and the published categories are the worksheet's. 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, so the year also publishes a shortfall
- The year's traditional draw includes an RMD, so the test also asserts that the published total is the five categories and not the five categories with the RMD added again
- Roth dollars are a member like any other; subtracting them as a tax offset is the worksheet's second wrong reading
- The worksheet's plan carries no inherited account. The identity in a year with a non-qualified inherited Roth distribution (two 100 Roth draws with 140 of taxable earnings beside a 100 traditional draw: traditional 100, roth 200, total 300) is held by a projection test that checks the five categories add to the total in that year (projection/simulate.inheritedRegimeExecution.test.ts), added with decision D-INHERITED-ROTH-SLICE on 2026-09-25, when traditional stopped carrying the Roth earnings
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-total-annual.md Mutation receipt: withdrawals-total-annual.mutation.md
Evidence tests: annualFundingApplicationAndClosePhase.evidence.test.ts
Implemented in: yearLedger.ts; annualWithdrawalPlanning.ts; annualFundingApplicationAndClosePhase.ts
Is the number of: 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
-
-
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
-
-
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
-
Engine source: engine/src/projection/internal/types/result.ts
(YearResult.withdrawals.total)
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.