Calculation catalog › Required minimum distributions

QCD annual

Gross qualified charitable distribution physically made in a projection year.

Field: Qualified charitable distribution Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain

Appears in: cash-flow-drilldown (QCD lines); csv (qcd); scenarios-page (Annual "QCD" and lifetime "QCDs")

Calculation records

  • QCD income offset and qualified slice Composition record, justified by derivation

    Purpose: Separate the physical charitable gift from the slice of it that is excluded from income.

    Statement: projection/internal/types/result.ts#YearResult.qcd publishes the gross physical gift while its excludable income offset is the qualified from-RMD slice less the remaining section 408(d)(8)(A) second-sentence offset; projection/internal/annualLegacyQcdOwnerCharacterPlan.ts#annualLegacyQcdOwnerCharacterPlan computes that character, with the qualification ceiling being the owner's aggregate includible IRA amount (pre-distribution owned-IRA balance less aggregate basis) rather than the taxable fraction of the RMD. Gift beyond the RMD carries no offset; gift beyond aggregate includible dollars is non-qualified ordinary distribution charged to the from-RMD portion first; rmd stays gross. Units: nominal USD. Rounding: none.

    Formula: qualified = min(gift, max(0, preDistribution - basis)); offsetApplied = min(qualified, max(0, section219 - alreadyTaken)); nonQualified = gift - qualified; nonQualifiedFromRmd = min(fromRmd, nonQualified); qualifiedFromRmd = fromRmd - nonQualifiedFromRmd; incomeOffsetDelta = min(qualifiedFromRmd, qualified - offsetApplied)

    • gift: Gross physical gift for the owner-year (unit: usd/year) (domain: nonnegative)
    • fromRmd: Part of the gift that satisfied the RMD (unit: usd/year) (domain: 0 <= fromRmd <= gift)
    • preDistribution: Pre-distribution aggregate owned-IRA balance (unit: usd) (domain: nonnegative)
    • basis: Aggregate nondeductible IRA basis (unit: usd) (domain: nonnegative)
    • section219: Post-70.5 deductible section 219 contributions, net of reductions already consumed (unit: usd) (domain: nonnegative)

    Timing: one owner-year

    Rounding: none; the cross-year donor-offset ledger is carried in integer cents

    Limits:

    • The qualification ceiling is statutory: section 408(d)(8)(B), last sentence, treats a distribution as a qualified charitable distribution only to the extent it would be includible in gross income, and section 408(d)(8)(D) measures that extent as if every IRA of the owner were one contract distributed in full, which is the aggregate includible amount (pre-distribution owned-IRA balance less aggregate basis) this record uses; the second sentence of section 408(d)(8)(A) supplies the post-70.5 section 219 reduction. The allocation of the non-qualified remainder between the from-RMD and beyond-RMD portions has no statutory text and is an engine convention: the remainder is charged to the from-RMD portion first. Either order gives the same total ordinary inclusion (25,000 on the worksheet inputs: 50,000 - 35,000 + 10,000 under a qualified-first allocation, 50,000 - 30,000 + 5,000 under the engine's), so tax-total-annual and magi-annual do not depend on the order; only the split between the two published character rows does. The worksheet was first derived on the qualified-first order and re-derived on the engine's; the evidence pins the re-derived 30,000 and 5,000 and is green. The gross gift (YearResult.qcd) and the gross RMD are written by the gift planner and the RMD phase, not by the character planner this test exercises, so they are not asserted here.
    • The evidence asserts the qualified slice through the exported plan builder rather than reading a private local: the slice is recovered from the published qualifiedFromRmd and nonQualifiedBeyondRmd rows
    • An unprovable or contradictory offset history falls back to a zero exclusion rather than guessing, and writes nothing to the cross-year ledger; this record is asserted on a provable history

    Provenance: derived by codex, implemented by claude.

    Independent review: cursor

    Gates (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: qcd-income-offset-qualified-slice.md Mutation receipt: qcd-income-offset-qualified-slice.mutation.md

    Evidence tests: annualLegacyQcdOwnerCharacterPlan.evidence.test.ts

    Implemented in: result.ts; annualLegacyQcdOwnerCharacterPlan.ts; annualLegacyQcdGiftPlan.ts

    Is the number of: qcd-annual

    Feeds: tax-total-annual, magi-annual

  • QCD per-donor limit and age-70.5 annual proxy Data record, justified by derivation

    Purpose: Cap each donor at the indexed annual QCD limit and gate eligibility on the annual age proxy.

    Statement: projection/internal/types/result.ts#YearResult.qcd caps each donor at year2026.rmd.qcdAnnualLimit multiplied by the applicable limit-growth factor, and uses an annual proxy for age 70.5: eligible at attained age 71, or at attained age 70 when the birth month is June or earlier. projection/internal/annualLegacyQcdGiftPlan.ts#annualLegacyQcdGiftPlan applies both, with the household scalar capped at the sum of the per-donor caps and unused capacity never pooled into a donor who has already exhausted their own limit. Units: nominal USD per donor per year. Rounding: none; a full drain of a source publishes whole ledger cents.

    Formula: eligible(p) = alive(p) and (age(p) >= 71 or (age(p) = 70 and birthMonth(p) <= 6)); cap = qcdAnnualLimit x limitGrowth; requested = min(qcdAnnual x inflFactor, cap x donorCount)

    • qcdAnnualLimit: year2026.rmd.qcdAnnualLimit (unit: usd/donor/year) (domain: positive)
    • limitGrowth: Limit-growth factor to the projection year: 1 in a year whose QCD limit the IRS has published in its notice of retirement-plan limits, otherwise the inflation path from that limit's latest published year (unit: 1) (domain: positive)
    • age(p): Attained age of person p this year (unit: years) (domain: integer >= 0)
    • birthMonth(p): Birth month of person p (unit: month) (domain: integer 1..12)

    Timing: one projection year, per donor

    Rounding: none

    Limits:

    • The age gate is an annual proxy for 70.5, not a dated test: a July-born donor attaining 70 is refused for the whole year even though the statute would admit the second half of it
    • The cap is per donor, not per household: a two-donor plan carries twice the capacity, and the scalar arm never pools one donor's unused capacity into another
    • The evidence exercises the scalar arm (no named QCD request); a named request returns an empty plan from this helper and is a different path
    • Restated 2026-09-28 under decision D-2027-ROLLOVER: the growth factor and the named arm read the QCD limit's own publication (params/index.ts#componentScale, actions/annualQcdTaxCharacterPostPass.ts#qcdLimitPublishedFor), not the whole set of published figures. The named arm executes only in a tax year whose limit is published, so a gift dated in a year whose November notice has landed executes even while that year's brackets are projected; every year through 2026 reads exactly as before

    Provenance: derived by codex, implemented by claude.

    Independent review: grok

    Gates (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: qcd-limit-and-age-proxy.md Mutation receipt: qcd-limit-and-age-proxy.mutation.md

    Evidence tests: annualLegacyQcdGiftPlan.evidence.test.ts

    Implemented in: result.ts; annualLegacyQcdGiftPlan.ts; index.ts; annualQcdTaxCharacterPostPass.ts

    Is the number of: qcd-annual

    Feeds: tax-total-annual, magi-annual

Engine source: engine/src/projection/internal/types/result.ts (YearResult.qcd / qcdIncomeOffset)

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.