Calculation catalog › Social Security

Social security benefit annual

Social Security benefit income paid in a projection year.

Field: Social Security benefit Unit: US dollars Basis: nominal dollars Computed by: Engine Status: Complete chain

Appears in: chart (socialSecurity); csv (socialSecurity); survivor-page (Household Social Security column: incomes.socialSecurity selected from the death year and the first survivor year); results-table (Income by source chart (socialSecurity series)); scenarios-page (Lifetime income "Social Security"); cash-flow-drilldown (socialSecurity source line)

Calculation records

  • Annual household Social Security benefit Composition record, justified by derivation

    Purpose: The Social Security cash the household is paid in one projection year, after the earnings test.

    Statement: YearResult.incomes.socialSecurity is the sum over living people of that person's benefit for the year, priced by socialSecurity/householdYear.ts#socialSecurityYear, which projection/internal/annualSocialSecurity.ts#annualSocialSecurity calls and the Social Security analysis models share. The year is composed month by month where a month can differ from the one before: a stream's first paying month (12 - claim months in the claim year, all 12 after), the months both spouses are paid, and each full-retirement-age and survivor-full-retirement-age month. In each month each own retirement stream is PIA x the claim-age factor; a divorced-spouse, current-spouse spousal (the claimant's own monthly PLUS the reduced excess of half the higher PIA over the claimant's own PIA, the dual-entitlement-composition record; for a current spouse half the higher PIA is first held to the family maximum less that PIA) or survivor candidate REPLACES the running amount when it is larger, rather than adding to it; the month's amounts are times the COLA factor and the haircut. A year with no such boundary is priced as one product per stream, monthly x payable months x COLA x haircut. The earnings test then charges each working person's excess earnings month by month from January, only to months the person is paid and that fall before the person's full-retirement-age month: half the wages above the lower exempt amount before the calendar year of that month, and in it a third of the wages of the months before that month above the higher amount, reduced to the next lower dollar. A worker's excess is charged first against the family benefit on his record, his benefits and the spouse benefit on his record, the rest of a partial month shared two to one; each other person's excess is then charged against what is left of that person's benefits. Each month of a benefit's reduction period with a deduction is a crediting month, and it raises that benefit from its full-retirement-age month (the survivor full-retirement-age month for a widow(er) benefit). Units: nominal dollars per year. Rounding: the excess earnings are reduced to the dollar; benefits and the partial-month shares are not rounded.

    Formula: benefit = sum over living people and months of max(own, marital candidate) x cola x haircut, less the months' deductions; excess = floor((wages - lower) / 2) before the FRA year, floor((wages x b / 12 - higher) / 3) in it, charged month by month from January to months before the FRA month

    • PIA: Primary insurance amount at FRA (unit: usd/month) (domain: nonnegative)
    • claim factor: Retirement factor for the claim age (early reduction or delayed credits), from the FRA month with the crediting months added to the claim age (unit: 1) (domain: 0.70 to 1.32)
    • payable months: 0, 12 minus the claim months, or 12: the last months of the year (unit: months) (domain: 0..12)
    • cola, haircut: COLA factor from the start year and 1 - cutPct/100 from ssHaircut.fromYear (unit: 1) (domain: positive)
    • lower, higher: Earnings-test exempt amounts from the published parameters, below-FRA and FRA-year, scaled for the year (unit: usd/year) (domain: positive)
    • b: Months of the FRA year before the FRA month (unit: months) (domain: 0..11)

    Timing: monthly within the year where a month can differ; the earnings test after the marital comparison

    Rounding: the excess earnings are reduced to the next lower dollar; benefits are not rounded

    Limits:

    • The own-claim-year case is constructible exactly as the worksheet now states it: a 1960 birth has FRA 67y0m, so a 64y3m claim is 33 months early and the factor is 1 - 33 x (5/9 of 1%) = 49/60, while the claim-age month of 3 pays 12 - 3 = 9 months in the claim year. The first derivation instead paired a 0.8 factor with those nine months (0.8 is exactly 36 months before FRA, whose claim-age month is 0 and which therefore pays all 12), and that was corrected on 2026-09-18; the incompatible pair is now the worksheet's first wrong reading, asserted as not matching
    • Beyond the worksheet's inputs the evidence plans fix, for the claim-year and later-year cases: a 1960-born single filer claiming at 64y3m on one plan whose projection starts in 2024, the year that birth attains 64, so the claim year is the first projection year and its COLA factor is exactly 1; zero inflation and zero return; a fixed 6%/year ssCola so the second projection year's factor is exactly 1.06; ssHaircut cutPct 5 from the start year; and a cash account to fund the year
    • The two-person case asserts the REPLACEMENT rule, so both people claim at their own FRA (claim and spousal factors 1) in the projection's first year, where the COLA factor is 1 and no haircut applies; the family maximum is left non-binding by the PIAs chosen
    • The earnings-test case is a separate 1962-born plan claiming at 64y0m, where the 0.8 factor and 12 payable months ARE the compatible pair, and is asserted in 2026 so the published parameters’ $24,480 below-FRA limit is unindexed; the wage row that drives it is ordinary income and is taxed, which does not enter this family
    • The family-charge cases (E5 and E7b) are a couple born in January 1964, both claiming at 62 in 2026 with no inflation, so every month of 2026 is in both reduction periods and the family maximum does not bind
    • The published field is the PAID benefit; the amount withheld is a separate field (ssEarningsTestWithheld), and the months withheld raise the benefit from its full-retirement-age month rather than being lost
    • Rounding: the excess earnings are reduced to the next lower dollar (42 U.S.C. 403(f)(3)); benefits and the partial-month shares are not rounded, where 20 CFR 404.304(f) reduces each monthly benefit, after all deductions, to the next lower multiple of $1, so a month can be up to a dollar above what SSA pays
    • Wages are the plan's wage rows, whole calendar years spread evenly over their months: there is no grace year and no non-service month (cfr-20-404-435-grace-year-monthly-earnings-test), only wage streams are earnings (usc-42-403-f-5-earnings-counted), and after the latest published year the exempt amounts grow at the plan's inflation (usc-42-403-f-8-earnings-test-exempt-amounts)
    • A claim at a whole age pays the whole calendar year it is attained (social-security-payable-months); the months before the claim month are paid and can be charged, but are not in the reduction period and are not credited
    • A worker's excess is charged against a household member's spouse benefit on his record; a former spouse's record carries no plan earnings, and a disability beneficiary is tested against substantial gainful activity instead of the earnings test
    • Restated 2026-09-27 (decision D-SS-LAW-2): the current-spouse candidate adds to the claimant’s own monthly benefit, held at the own PIA, the excess of half the higher PIA over the claimant’s own PIA reduced by the spousal factor (dual-entitlement-composition). Restated 2026-09-29 (decision D-SS-ANALYSIS-EARNINGS-TEST): the year is composed month by month where a month can differ, and the earnings test is charged month by month with the family charge, the FRA-year months, the dollar floor, reduction-period crediting and the adjustment from the FRA month, where it was one annual amount with rounded months and the adjustment from January. Both restatements were reviewed by Grok on 2026-09-29 (DOCS/calculations/reviews/REVIEW-2026-09-29-grok-3-after-769.md)
    • No discrepancy remains in the two-person case: the year function builds the current-spouse candidate as the claimant's own monthly benefit PLUS the capped spousal excess ($300 + $700 = $1,000) and replaces the running amount with that total, which is what a dually-entitled spouse is actually paid, so the household is $36,000 and the worksheet expects $36,000. The first derivation read the ledger comment's "replaces the running amount" as replacing the own benefit with the excess alone and expected $32,400; it was corrected on 2026-09-18, and $32,400 is now the worksheet's second wrong reading, asserted as not matching

    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: social-security-benefit-annual.md Mutation receipt: social-security-benefit-annual.mutation.md

    Evidence tests: annualSocialSecurity.benefitAnnual.evidence.test.ts

    Implemented in: annualSocialSecurity.ts; householdYear.ts; earningsTest.ts

    Is the number of: social-security-benefit-annual

Records that feed this number

  • Covered earnings counted up to each year's contribution and benefit base, from 1951 Composition record, justified by derivation

    Purpose: Count each year's earnings in the average only up to that year's taxable maximum, back to 1951, before indexing them.

    Statement: socialSecurity/piaFromEarnings.ts#computePiaFromEarnings caps each year's covered earnings at that year's contribution and benefit base, socialSecurity/ssaWageData.ts#WAGE_BASE_BY_YEAR (SSA's bases for every year from 1937 through 2026, read through #wageBaseForYearOrLatest, which uses the latest published base only for a later year SSA has not set), before wage-indexing them (42 U.S.C. 415(e)(1): "there shall not be counted the excess over $3,600 in the case of any calendar year after 1950 and before 1955", and so on through the section 430 base from 1975). Its window of years starts at 1951 (#FIRST_COMPUTATION_BASE_YEAR, the first of "the calendar years after 1950", 415(b)(2)(B)(ii)) or the year the worker turns 22, whichever is later, and ends with the year before 62; the five lowest indexed years are dropped and the rest averaged over their months, floored to a dollar. Units: nominal USD per year for earnings, per month for the average. Rounding: each indexed year floored to a dollar and the average floored to a dollar.

    Formula: counted(y) = min(earnings(y), base(y)); indexed(y) = floor(counted(y) x AWI(e - 2) / AWI(y)) for y <= e - 2, else counted(y); years y from max(1951, b + 22) to e - 1; AIME = floor(sum of all but the five lowest indexed / (12 x (count - 5)))

    • earnings(y): Covered earnings reported or projected for year y (unit: usd/year) (domain: nonnegative)
    • base(y): Contribution and benefit base for year y: SSA's from 1937, the latest published for a later year (unit: usd/year) (domain: positive from 1937)
    • AWI(y): National average wage index for year y, the latest published for a later year (unit: usd/year) (domain: positive, from 1951)
    • e: Eligibility year, the year the worker turns 62 (effective birth year plus 62) (unit: year) (domain: 1979 or later)
    • b: Effective birth year (a January 1 birth counts in the prior year) (unit: year) (domain: integer)

    Timing: once per earnings history, before the projection

    Rounding: each indexed year and the average floored to a whole dollar

    Limits:

    • Each indexed year is floored to a dollar where 20 CFR 404.211(d)(3) rounds to the penny (cfr-20-404-211-d-3-indexed-earnings-nearer-penny), and the window leaves out the age-21 year and years from 62 to first entitlement (usc-42-415-b-2-b-ii-iii-initial-computation-base-window)
    • Eligibility before 1979 is refused, so the earliest window year is 1951 for a worker born from 1917 to 1929; the transitional guarantee for workers born 1917 to 1921 is not modeled
    • The old-start computation (20 CFR 404.240 through 404.243), which can count earnings from 1937 through 1950 for a worker born in 1929 or earlier, is not modeled: earnings before 1951 count for nothing here. SSA pays the highest PIA among the methods a worker qualifies for, so leaving it out can only understate the PIA, and only for a worker 97 or older in 2026
    • A year later than the last published base or wage index uses the latest published figure, and the result says so (usesStandInForFutureTables)
    • The PIA it yields is in eligibility-year dollars; the projection raises it by the cost-of-living increases since (pia-cost-of-living-since-eligibility)

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: aime-covered-earnings-cap.md Mutation receipt: aime-covered-earnings-cap.mutation.md

    Evidence tests: piaFromEarnings.wageBase.evidence.test.ts

    Implemented in: piaFromEarnings.ts; ssaWageData.ts

    Feeds: social-security-benefit-annual

  • Delayed retirement credit factor Formula record, justified by derivation

    Purpose: Credit a percentage of PIA per month after full retirement age, stopping at age 70, at the rate for the date of birth.

    Statement: socialSecurity/benefitFactor.ts#delayedRetirementFactor increases the retirement benefit by r percent of PIA per month after the normal retirement age, capped at the months available through age 70, so there is no credit past 70. Claiming at or before NRA returns a factor of 1. The rate r is the 20 CFR 404.313(b)(2) credit for the effective birth year, from #delayedCreditMonthlyPct: 2/3 of 1 percent for births after January 1, 1943, and from 5/8 down to 1/12 of 1 percent for earlier births; claimFactor and the survivor base pass it, and the function defaults to 2/3. Units: a dimensionless factor on PIA. Rounding: none.

    Formula: factor = 1 + min(m, max(0, c)) x r / 100, and 1 when m <= 0

    • m: Months claimed after the normal retirement age (unit: months) (domain: integer)
    • c: Maximum credited months from NRA through age 70 (unit: months) (domain: integer >= 0)
    • r: Credit per month for the effective birth year (2/3 for births after January 1, 1943) (unit: percent of PIA per month) (domain: a rate from the 20 CFR 404.313(b)(2) table)

    Timing: once per claim

    Rounding: none

    Limits:

    • No direct census family: the factor multiplies PIA on the way to the published annual benefit
    • Delayed credits apply to the worker's own retirement benefit only; spousal benefits earn none
    • The cap is supplied by the caller as the months from that person's NRA to age 70, so the record claims the credit rate and the cap behaviour, not the NRA schedule

    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: delayed-retirement-credit-factor.md Mutation receipt: delayed-retirement-credit-factor.mutation.md

    Evidence tests: benefitFactor.evidence.test.ts

    Implemented in: benefitFactor.ts

    Feeds: social-security-benefit-annual

  • Own benefit plus the separately reduced spouse excess Composition record, justified by derivation

    Purpose: Pay a person entitled to both an own benefit and a spouse or divorced-spouse benefit the own benefit plus the reduced excess of half the worker's PIA over the own PIA.

    Statement: socialSecurity/dualEntitlement.ts#spouseDualEntitlementMonthly returns max(own, min(own, ownPia) + max(0, spouseBase - ownPia) x spouseFactor), where own is the claimant's own benefit as paid, ownPia the claimant's PIA and spouseBase one half of the worker's PIA: the own benefit plus the excess reduced by the spouse factor (42 U.S.C. 402(q)(3)(B), 402(k)(3)(A); POMS RS 00615.250), and for an own benefit with delayed credits the larger of that benefit and the combined amount computed without them (POMS RS 00615.694). The spouse factor is the spouse reduction, 25/36 of 1% for each of the first 36 months before full retirement age and 5/12 of 1% beyond, at the claimant's age in the first month of the spouse benefit (402(q)(6)(A)(ii)): #spouseEntitlementAgeMonths takes the later of the claimant's own configured claim and the claimant's age in the month the worker's benefit starts, the worker's configured claim month for a current spouse (the month the claim age is attained, the plan's claim-age convention) or, for a divorced spouse, the first month the ex is 62 throughout (POMS RS 00202.005 B.2.a: "the NH must be 62 throughout the first month of entitlement but need not have filed a claim for benefits"), since with deemed filing (402(r)) the claimant is taken to apply for the spouse benefit as soon as it is available. projection/internal/annualSocialSecurity.ts#annualSocialSecurity uses it for the lower earner of a couple, with half the worker's PIA first held to the room his family maximum leaves above his PIA (familyMaximum.ts#currentSpouseMonthlyUnderFamilyMaximum), socialSecurity/maritalBenefits.ts#maritalBenefitFor for a divorced spouse, and the claim-milestone insight for its prior-year reconstruction. Units: nominal USD per month, in today's dollars before COLA and haircut. Rounding: none.

    Formula: total = max(own, min(own, ownPia) + max(0, 0.5 x workerPia - ownPia) x f(s)); s = max(ownClaimMonths, workerStartMonth - claimantAgeZeroMonth); f(s) = 1 at s >= FRA, else 1 - (min(36, FRA - s) x 25/36 + max(0, FRA - s - 36) x 5/12) / 100

    • own: Claimant's own old-age benefit as paid, after the claim factor (unit: usd/month) (domain: nonnegative)
    • ownPia: Claimant's own primary insurance amount; 0 with no own benefit (unit: usd/month) (domain: nonnegative)
    • workerPia: Worker's (or the ex's) primary insurance amount (unit: usd/month) (domain: positive)
    • s: Claimant's age in total months in the first month of the spouse benefit, after any earnings-test credit for months the spouse benefit was withheld (unit: months) (domain: integer at or above 744)
    • workerStartMonth: Calendar month the worker's record first supports the spouse benefit: the worker's configured claim month, or the first month the ex is 62 throughout (unit: month index) (domain: integer)
    • FRA: Claimant's full retirement age in total months (unit: months) (domain: integer 780 to 804)

    Timing: once per claimant per projection year

    Rounding: none

    Limits:

    • One claim age serves the own and the spouse benefit, and a spouse benefit is paid only from the year both the claimant and the worker have reached their configured claim ages; the months within a year follow the payable-month rule of social-security-payable-months
    • A divorced spouse's benefit is paid from the calendar year of the first month the ex is 62 throughout, and the ledger pays that whole year, its annual convention for a first year (maritalBenefits.ts#isDivorcedSpouseEligible); an ex born in December after the 2nd starts it the next January, so the year the ex turns 62 pays only the own benefit (worksheet case I)
    • For people born before January 2, 1954, who could restrict an application to the spouse benefit, the one claim age cannot express that choice; they are priced as if they applied for both
    • A worker on disability benefits is taken to start at the claim age configured for him, not at the disability onset, and a claimant on disability benefits is priced with the same composition; 42 U.S.C. 402(q)(3)(C) and (D) govern a disabled claimant and are not modeled separately
    • The family maximum caps a current spouse's excess only; a divorced spouse's benefit is outside the family maximum, and no child benefits are modeled
    • The spouse reduction is credited with the months of the spouse benefit's own reduction period in which it was subject to an earnings-test deduction, the worker's excess charged to the family or the claimant's own, counted for that record (the current spouse, or that former-spouse entry) from the benefit's first month (poms-rs-00615-482-arf-crediting-months), and the credit takes effect in the claimant's full-retirement-age month (cfr-20-404-412-b-arf-effective-fra-month); the claim-milestone insight reconstructs the prior year from configured claim ages without that credit
    • A couple member's divorced-spouse benefit on a living ex's record starts no earlier than the January after the current spouse's death, and is reduced for the claimant's age in its first month (usc-42-402-b-1-C-divorced-spouse-after-widowhood)

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: dual-entitlement-composition.md Mutation receipt: dual-entitlement-composition.mutation.md

    Evidence tests: dualEntitlement.evidence.test.ts

    Implemented in: dualEntitlement.ts; annualSocialSecurity.ts; householdYear.ts; maritalBenefits.ts

    Feeds: social-security-benefit-annual, social-security-expected-present-value

  • Early claim reduction factor Formula record, justified by derivation

    Purpose: Reduce PIA 5/9 of 1% for the first 36 early months and 5/12 of 1% beyond.

    Statement: socialSecurity/benefitFactor.ts#earlyRetirementFactor computes the retirement-benefit factor for months before the normal retirement age by reducing PIA 5/9 of 1% per month for the first 36 months and 5/12 of 1% for each additional month. Claiming at or after NRA returns a factor of 1. Units: a dimensionless factor on PIA. Rounding: none.

    Formula: factor = 1 - (min(m, 36) x (5/9) + max(0, m - 36) x (5/12)) / 100, and 1 when m <= 0

    • m: Months claimed before the normal retirement age (unit: months) (domain: integer)

    Timing: once per claim

    Rounding: none

    Limits:

    • No direct census family: the factor multiplies PIA on the way to the published annual benefit
    • The two-band schedule is the worker retirement schedule; the spousal reduction (25/36 of 1% for the first 36 months) and the widow(er) reduction are different rules

    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: early-claim-factor.md Mutation receipt: early-claim-factor.mutation.md

    Evidence tests: benefitFactor.evidence.test.ts

    Implemented in: benefitFactor.ts

    Feeds: social-security-benefit-annual

  • Retirement/survivor family maximum from PIA Formula record, justified by derivation

    Purpose: The monthly ceiling every auxiliary benefit on a worker record shares, from that worker's PIA.

    Statement: socialSecurity/familyMaximum.ts#familyMaximumMonthlyFromPia takes a monthly PIA P and an eligibility year, reads that year's three family-maximum bend points b1 < b2 < b3 from FAMILY_MAXIMUM_BEND_POINTS (2026: 1,643, 2,371 and 3,093 dollars per month; a year with no published triplet falls back to the latest one on file), and returns floorToDime(1.50 min(P, b1) + 2.72 min(max(P - b1, 0), b2 - b1) + 1.34 min(max(P - b2, 0), b3 - b2) + 1.75 max(P - b3, 0)). A PIA at or below zero returns 0. Units: nominal USD per month, before COLA and any trust-fund haircut. Rounding: the sum is floored to the next lower dime, Math.floor(x * 10 + 1e-9) / 10, so the published figure is quantized and the unrounded sum is an intermediate only.

    Formula: MFB = floorToDime(1.50 min(P, b1) + 2.72 min(max(P - b1, 0), b2 - b1) + 1.34 min(max(P - b2, 0), b3 - b2) + 1.75 max(P - b3, 0))

    • P: Worker primary insurance amount (unit: usd/month) (domain: P > 0; P <= 0 returns 0)
    • b1, b2, b3: Family-maximum bend points for the eligibility year (unit: usd/month) (domain: 0 < b1 < b2 < b3, from FAMILY_MAXIMUM_BEND_POINTS)
    • MFB: Monthly retirement/survivor family maximum (unit: usd/month) (domain: nonnegative multiple of 0.10)

    Timing: once per worker record, before COLA and haircut

    Rounding: floored to the next lower dime

    Limits:

    • The four band rates 150/272/134/175 percent are the retirement and survivor family maximum, not the disability family maximum, which SSA computes differently
    • The bend points are eligibility-year values (the year the worker attains 62 under familyMaximumEligibilityYearFromDobParts), not the benefit year; an eligibility year the table does not price falls back to the latest published triplet
    • RetireGolden models no child dependents, so capAuxiliaryForFamilyMaximum gives the whole room above the worker's PIA (not above the benefit the worker is paid; cfr-20-404-404-family-maximum-counts-the-worker-pia) to the single current-spouse auxiliary. Under this retirement and survivor maximum that room holds a spouse's whole original benefit less the dime rounding of the maximum (under 10 cents a month); the disability maximum of a worker on SSDI can leave no room and is not modeled (usc-42-403-a-6-ssdi-family-maximum). The worker's own benefit is never reduced by this ceiling
    • The dime floor is part of the published contract: the test compares the floored figure exactly and treats the unrounded sum as an intermediate

    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: family-maximum-bend-points.md Mutation receipt: family-maximum-bend-points.mutation.md

    Evidence tests: familyMaximum.evidence.test.ts

    Implemented in: familyMaximum.ts; ssaWageData.ts

    Feeds: social-security-benefit-annual

  • Annual wage income for one stream Formula record, justified by derivation

    Purpose: What a wage stream pays in a projection year, after real raises and inflation.

    Statement: projection/internal/wageIncomeStreams.ts#wageIncomeStreams emits one row per wage stream that pays this year, in plan.incomes order, with amount = annualGross x (1 + realGrowthPct/100)^(year - startYear) x inflFactor, computed in that operand order; annualIncomeSetup folds those rows into the published projection/internal/types/result.ts#YearResult.incomes.wages. A stream is skipped when its owner is not alive, or when the owner has attained the stream's stop age: endAge when the stream names one, else the person's retirementAge, and neither when both are null. Wages run before Social Security because pass 3's earnings test reads the per-person wage totals. Units: nominal USD per year. Rounding: none.

    Formula: wages = annualGross * (1 + realGrowthPct/100)^(year - startYear) * inflFactor

    • annualGross: Gross wage at the projection start year (unit: usd/year) (domain: nonnegative)
    • realGrowthPct: Annual real raise, in percent (unit: percent/year) (domain: absent reads as 0)
    • year - startYear: Elapsed projection years, computed operand for operand at the phase (unit: years) (domain: integer at or above 0)
    • inflFactor: Cumulative general-inflation factor from the projection start year (unit: 1) (domain: positive)
    • stopAge: endAge when present, else the person's retirementAge (unit: attained age) (domain: either may be null; attaining it stops the row)

    Timing: income pass 1, once per projection year, before Social Security

    Rounding: none

    Limits:

    • Asserted on a real simulatePlan run over 2028-2030. Plan assumptions beyond the worksheet's inputs: a single person filing single in KY with a zero state rate, no accounts, and the worksheet's 1.08 cumulative inflation factor supplied as a per-year inflation path of 8 percent in the first projection year and 0 afterwards, because a single constant rate cannot give a two-year cumulative factor of exactly 1.08
    • The stop age is attained-age based: a stream with endAge 65 pays at attained age 64 and stops at 65, so the worksheet's paying year is the last one before the stop
    • The engine preserves plan order across wage rows rather than pre-summing them, so the last binary digit of a multi-row wage total depends on that order; the test asserts a single row

    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: income-wages-annual.md Mutation receipt: income-wages-annual.mutation.md

    Evidence tests: wageIncomeStreams.evidence.test.ts

    Implemented in: wageIncomeStreams.ts; result.ts

    Is the number of: income-wages-annual

    Feeds: income-total-annual, social-security-benefit-annual

  • Normal retirement age endpoint Data record, justified by derivation

    Purpose: Assign births after 1955-1959 ramp the current-law 67-year full retirement age.

    Statement: socialSecurity/nra.ts#fraForBirthYear assigns effective birth years 1960 and later a normal retirement age of 67 years and 0 months under current law, the endpoint of the ramp whose last step is 66 years and 10 months at 1959, and #fraTotalMonths converts that to 804 total month slots as twelve times the completed years plus the extra months. Units: years, months and total month slots. Rounding: none; integers.

    Formula: fra(y >= 1960) = { years: 67, extraMonths: 0 }; totalMonths = years x 12 + extraMonths

    • y: Effective birth year (a January 1 birth uses the prior calendar year) (unit: year) (domain: integer)

    Timing: once per person

    Rounding: none

    Limits:

    • Simplified to the birth year: the month-of-year refinements of the SSA schedule are not modeled, and a January 1 birth is handled by the separate effective-birth-year rule
    • The survivor (widow(er)) full retirement age is a different schedule, keyed to the year the survivor turns 60, so it runs two birth years behind this one and reaches 67 for survivors born 1962 and later; registered as usc-42-416-l-survivor-fra-age-60-attainment-cohorts
    • Current law may change; the record pins what the module states today

    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: normal-retirement-age.md Mutation receipt: normal-retirement-age.mutation.md

    Evidence tests: nra.evidence.test.ts

    Implemented in: nra.ts

    Feeds: social-security-benefit-annual

  • An earnings-history PIA raised by the cost-of-living increases since eligibility Formula record, justified by derivation

    Purpose: Bring a PIA computed from an earnings history from its eligibility year to the projection's first year, one published increase at a time.

    Statement: socialSecurity/piaFromEarnings.ts#piaWithCostOfLivingIncreases multiplies the eligibility-year PIA, year by year from the eligibility year e through the year t before the projection's first year, by one plus that year's cost-of-living increase from socialSecurity/ssaWageData.ts#COLA_PCT_BY_YEAR (SSA's series, 1975 through 2025), flooring to the dime after each step as 42 U.S.C. 415(i)(2)(A)(ii) requires ("any amount so increased that is not a multiple of $0.10 shall be decreased to the next lower multiple of $0.10"); 415(i)(2)(A)(iii) raises the PIA of a person eligible in a year with an increase "by the amount of that increase and subsequent applicable increases". A year SSA has not announced uses the plan's COLA assumption (#socialSecurityColaAssumptionPct: the fixed rate, or the inflation rate under matchInflation) and is reported. projection/simulate.ts#simulatePlan applies it to every PIA derived from earnings before the ledger's own COLA factor, which is 1 in the first year. Units: nominal USD per month. Rounding: floor to the dime after each year.

    Formula: P(e - 1) = eligibility-year PIA; P(y) = floorToDime(P(y - 1) x (1 + c(y) / 100)) for y = e to t; result P(t), with t = startYear - 1

    • e: Eligibility year, the year the worker turns 62 (unit: year) (domain: 1979 or later)
    • t: The year before the projection's first year (unit: year) (domain: integer; no step when t < e)
    • c(y): Cost-of-living increase effective in year y: SSA's published figure, or the plan's COLA assumption for a year not yet announced (unit: percent) (domain: nonnegative in the published series)

    Timing: once per earnings-history stream, before the projection

    Rounding: floor to the next lower ten cents after each year

    Limits:

    • An entered PIA is taken to be in the first year's dollars already, as a PIA shown on a current SSA statement is
    • A year between the last announced increase and the projection's first year uses the plan's COLA assumption, a stand-in the projection warns about; the ledger's COLA from the first year on is social-security-cola-factor
    • The projection, the claim-milestone insight and the planner's Social Security pages read the start-year PIA through socialSecurity/piaFromEarnings.ts#resolveStreamPiaMonthly (social-security-pia-resolution); the planner's AIME explainer shows the eligibility-year computation
    • COLA_PCT_BY_YEAR ends with the increase effective December 2025. From 2027-01-01 the planner's first year is 2027, so every earnings-history PIA past eligibility takes the plan's COLA assumption for the December 2026 increase, with the projection's warning, until SSA's 2026 increase, announced in October 2026, is added to the table with the yearly parameter refresh
    • The PIA is not recomputed for later earnings (usc-42-415-f-2-post-entitlement-pia-recomputation)

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: pia-cost-of-living-since-eligibility.md Mutation receipt: pia-cost-of-living-since-eligibility.mutation.md

    Evidence tests: piaFromEarnings.costOfLiving.evidence.test.ts

    Implemented in: piaFromEarnings.ts; ssaWageData.ts; simulate.ts

    Feeds: social-security-benefit-annual

  • PIA from AIME across the bend points Formula record, justified by derivation

    Purpose: Apply 90/32/15 percent across the eligibility-year bend points and floor to a dime.

    Statement: socialSecurity/piaFromEarnings.ts#piaMonthlyFromAime computes monthly PIA from AIME by applying 90% through the first eligibility-year bend point, 32% between the two bend points and 15% above the second, then rounding the result DOWN to the next lower ten cents. The 2026 bend points come from socialSecurity/ssaWageData.ts#PIA_BEND_POINTS, not from the tax parameters. Units: nominal USD per month. Rounding: floor to a dime.

    Formula: pia = floorToDime(0.90 x min(A, b1) + 0.32 x max(0, min(A, b2) - b1) + 0.15 x max(0, A - b2))

    • A: Average indexed monthly earnings (unit: usd/month) (domain: nonnegative)
    • b1: First bend point for the eligibility year (unit: usd/month) (domain: positive)
    • b2: Second bend point for the eligibility year (unit: usd/month) (domain: positive and above b1)

    Timing: once per eligibility year

    Rounding: floor to the next lower ten cents

    Limits:

    • The rounding is a floor, not a round-to-nearest; the worksheet's cross-both case happens not to distinguish them, so the evidence carries a separate discriminator that does
    • Bend points live in the SSA wage data, not in year2026: reading them from the tax parameters finds nothing
    • An eligibility year past the last published table falls back to the latest published bend points

    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: pia-from-aime-bend-points.md Mutation receipt: pia-from-aime-bend-points.mutation.md

    Evidence tests: piaFromEarnings.evidence.test.ts

    Implemented in: piaFromEarnings.ts; ssaWageData.ts

    Feeds: social-security-benefit-annual

  • Social Security COLA factor Model record, justified by derivation

    Purpose: Compound the benefit escalator from the projection start, with no escalation in the first year.

    Statement: projection/internal/annualSocialSecurity.ts#annualSocialSecurity receives an ssColaFactor that compounds from the projection start, with factor 1 in the first year, using either the plan's general inflation rate or its fixed ssCola rate, and an ssHaircutFactor. projection/simulate.ts#simulatePlan builds both with socialSecurity/colaFactor.ts#socialSecurityColaFactor, to which it passes its own inflation factor (a Monte Carlo path's when one is supplied), and #socialSecurityHaircutFactor (1 - cutPct / 100 from the haircut year, else 1); the break-even chart and the benefits-only expected value call the same two functions on the plan's deterministic inflation. For a fixed rate the factor at year offset n is (1 + rate)^n, so the published benefit of a stream already in force scales by exactly that factor against its start-year amount. Units: a dimensionless factor. Rounding: none.

    Formula: ssColaFactor(y) = (1 + ssCola.annualPct / 100)^(y - startYear) for the fixed mode, or the plan inflation factor from startYear to y for matchInflation

    • y: Projection year (unit: year) (domain: integer at or after startYear)
    • startYear: First projection year (unit: year) (domain: integer)
    • ssCola.annualPct: Plan fixed COLA rate (unit: percent/year) (domain: greater than -100)

    Timing: once per projection year

    Rounding: none

    Limits:

    • Asserted at the plan level the comments state rather than by reading a private local: the evidence runs a real projection of a single stream already in force, with twelve payable months and no haircut and no earnings test, so the year's published benefit over the start year's IS the factor the phase received
    • The two modes are alternatives, never combined: a plan on matchInflation does not additionally apply the fixed rate
    • The worksheet's 2.8% is year2026.socialSecurity.colaPct used as the plan's fixed rate; the parameter figure is not itself the plan default

    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: social-security-cola-factor.md Mutation receipt: social-security-cola-factor.mutation.md

    Evidence tests: annualSocialSecurity.evidence.test.ts

    Implemented in: colaFactor.ts; annualSocialSecurity.ts; simulate.ts

    Feeds: social-security-benefit-annual, social-security-break-even, social-security-expected-present-value

  • Social Security payable months Formula record, justified by derivation

    Purpose: Count the modeled months a claim pays in the year it starts.

    Statement: socialSecurity/householdYear.ts#annualSocialSecurityPayableMonths (re-exported by projection/internal/annualSocialSecurity.ts) returns 0 before the claim year, 12 after it, and max(0, 12 - claimAge.months) when the attained age equals the claim-age years. The claim-age month itself is excluded and there is no calendar payment lag. Units: modeled months. Rounding: none; integers.

    Formula: months = 0 when ageAttained < claimAge.years; 12 when ageAttained > claimAge.years; max(0, 12 - claimAge.months) otherwise

    • ageAttained: Age attained in the projection year (unit: years) (domain: integer)
    • claimAge.years: Completed years of the configured claim age (unit: years) (domain: integer)
    • claimAge.months: Extra months of the configured claim age (unit: months) (domain: integer 0..11)

    Timing: once per person-year

    Rounding: none

    Limits:

    • An annual-ledger approximation in age-year coordinates: the helper takes no birth month and cannot express an anniversary-year fraction
    • No administrative payment lag is modeled, which the comment states explicitly

    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: social-security-payable-months.md Mutation receipt: social-security-payable-months.mutation.md

    Evidence tests: annualSocialSecurity.evidence.test.ts

    Implemented in: householdYear.ts

    Feeds: social-security-benefit-annual

  • The PIA a Social Security stream is paid from Composition record, justified by derivation

    Purpose: Resolve a stream's monthly PIA once, for the projection, its insights and the Social Security pages.

    Statement: socialSecurity/piaFromEarnings.ts#resolveStreamPiaMonthly returns the entered PIA as entered; otherwise, for a stream with an earnings history, the PIA #computePiaFromEarnings gives on the history and its projection (#streamPiaFromEarningsInput, the projection defaulting to the person's retirement age), raised by #piaWithCostOfLivingIncreases from the eligibility year through the year before the projection starts, with the years whose increase SSA has not announced; otherwise no PIA, with the reason: no PIA and no earnings history, or the computation's error. projection/simulate.ts#simulatePlan, the claim-milestone insight and the planner's Social Security pages all call it. The couple primer prints the resolved PIA x 12. Units: dollars a month. Rounding: the PIA's own, to the dime.

    Limits:

    • An entered PIA is taken to be in the first year's dollars already, as a PIA shown on a current SSA statement is
    • The yearly figure the couple primer prints is PIA x 12, the benefit at full retirement age, not the benefit at the claim age

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: social-security-pia-resolution.md Mutation receipt: social-security-pia-resolution.mutation.md

    Evidence tests: piaFromEarnings.resolution.evidence.test.ts

    Implemented in: piaFromEarnings.ts; simulate.ts

    Is the number of: social-security-pia-annualized

    Feeds: social-security-benefit-annual

  • SSDI months paid in a year, from the onset month Formula record, justified by derivation

    Purpose: Count the disability months and converted retirement months an SSDI stream pays in a calendar year, starting after the five-month waiting period.

    Statement: socialSecurity/disability.ts#ssdiFirstPayableMonthIndex places the first payable month E in the onset year, the birth year plus disability.onsetAge: a given disability.onsetMonth M is read as an onset after the 1st, so the next five months are the waiting period and E is the sixth month after M (42 U.S.C. 423(a)(1), (c)(2); POMS DI 10105.070), and a blank month is read as January 1, so E is June. #ssdiSchedule takes F, the month the worker attains full retirement age by the day-before-birthday rule, and returns no schedule when E is at or after F, when the stream is priced as a retirement claim at its claim age and the projection warns. #ssdiMonthsInYear counts, in year Y, the disability months from E to the month before F and the converted retirement months from F on (42 U.S.C. 402(a)(3)); projection/internal/annualSocialSecurity.ts#annualSocialSecurity pays the PIA for both, times the COLA and haircut factors, and publishes the disability months alone as ssdiPaid. Units: months per year; the benefit in nominal USD. Rounding: none; whole months.

    Formula: E = 12(b + a) + (m - 1) + 6 for a given month m, or 12(b + a) + 5 for a blank month; no schedule when E >= F; disability(Y) = max(0, min(F, 12Y + 12) - max(E, 12Y)); retirement(Y) = max(0, 12Y + 12 - max(F, 12Y)); SSDI paid = PIA x disability(Y) x cola x haircut

    • b: Calendar year of birth (unit: year) (domain: integer)
    • a: disability.onsetAge, the age attained in the onset year (unit: years) (domain: integer 40 to 75)
    • m: disability.onsetMonth, read as an onset after the 1st (unit: month) (domain: integer 1 to 12, or blank)
    • E: First payable month, counted as 12 x year + month index from January of year 0 (unit: month index) (domain: integer)
    • F: Month the worker attains full retirement age (unit: month index) (domain: integer)
    • Y: Projection year (unit: year) (domain: integer)

    Timing: per stream per projection year

    Rounding: none; whole months

    Limits:

    • A month without a day is read as an onset after the 1st: a disability that began on the 1st counts that month and is paid one month sooner, and at full retirement age that month can decide whether there is a disability benefit at all (usc-42-423-c-2-ssdi-five-month-waiting-period)
    • A blank month is read as January 1, the earliest start and so the largest amount the statute allows for the onset year
    • The application is taken as timely, so the 12 months of retroactivity in 42 U.S.C. 423(b) never limit it, and the waiting period is always applied, with no exception for re-entitlement within five years or for amyotrophic lateral sclerosis
    • The published source for the year that holds the FRA month reads own retirement, the benefit in force at the end of the year; the disability months in that year are in ssdiPaid
    • The worksheet's claim at 70 pays 12 months in 2040 (29,760) under the engine's claim-year convention (social-security-payable-months); by statute that benefit starts with June 2040 and pays 17,360 that year
    • The annual SGA test applies only in a year that pays disability months and no converted month, and suspends the whole year
    • The disability PIA is the PIA the plan gives or derives from earnings, not a PIA computed as if the worker were 62 at the start of the waiting period (20 CFR 404.317)

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: ssdi-payable-months.md Mutation receipt: ssdi-payable-months.mutation.md

    Evidence tests: disability.payableMonths.evidence.test.ts

    Implemented in: disability.ts; annualSocialSecurity.ts

    Feeds: social-security-benefit-annual

  • Survivor benefit under RIB-LIM Composition record, justified by derivation

    Purpose: Reduce the survivor base for the survivor's age, then cut it to the widow's limit when the deceased was paid a reduced benefit.

    Statement: socialSecurity/survivorBenefit.ts#survivorBenefitMonthly takes the survivor base as the larger of the deceased's PIA and the deceased's actual benefit (42 U.S.C. 402(e)(2)(A) and (C)) and multiplies it by the widow(er) factor #survivorReductionFactor returns: a linear ramp from 71.5% at age 60 to 100% at the SURVIVOR full retirement age, clamped to 0.715 at or before 60 and to 1 at or after that FRA. When the deceased was ever entitled to an old-age benefit reduced for an early claim, the result is then cut, if it is greater than both, to the larger of the deceased's actual benefit and 82.5% of the deceased's PIA (402(e)(2)(D), "after application of subsection (q)"; POMS RS 00615.320 A.3). A deceased with no PIA returns zero. Units: nominal USD per month, in today's dollars before COLA and haircut. Rounding: none.

    Formula: B = max(deceasedPia, deceasedActual) x factor; benefit = min(B, max(deceasedActual, 0.825 x deceasedPia)) when everReduced, else B; factor = 1 - 0.285 x (1 - (m - 720) / (F - 720)) for 720 < m < F, 0.715 at m <= 720, 1 at m >= F

    • deceasedPia: Deceased worker's primary insurance amount (unit: usd/month) (domain: positive; zero or less returns 0)
    • deceasedActual: Deceased worker's actual benefit after their own claim factor (unit: usd/month) (domain: nonnegative)
    • everReduced: Whether the deceased was ever entitled to an old-age benefit reduced for claiming before full retirement age (unit: true or false) (domain: false for a worker who never claimed, claimed at or after FRA, or was paid disability benefits; a caller may omit it, and it is then taken as deceasedActual < PIA, which gives the same amount)
    • m: Survivor age in total months at which the benefit is reduced: in the projection, the age at the first month of widow(er) entitlement (survivor-reduction-entitlement-month) (unit: months) (domain: integer)
    • F: Survivor full retirement age in total months (unit: months) (domain: integer above 720)

    Timing: once per survivor claim

    Rounding: none

    Limits:

    • The survivor FRA is a separate schedule from the worker FRA, and using the worker FRA would change every reduction between 60 and FRA; the survivor schedule is registered as usc-42-416-l-survivor-fra-age-60-attainment-cohorts
    • The deceased's actual benefit is taken as the caller supplies it; for a worker who died before claiming, the projection supplies the benefit the worker would have received for the month before the death, with the credits earned by then, registered as usc-42-402-e-survivor-of-worker-who-died-before-claiming
    • For a worker who dies before 62, the projection uses the PIA as entered; the law computes that PIA with the death as the eligibility event (20 CFR 404.211 and 404.212) and, for a survivor, can raise it by indexing the worker's earnings to a later year (42 U.S.C. 402(e)(2)(B)), which is not modeled
    • For a former spouse who died, the survivor base is priced at the claim age entered for that person, or at full retirement age when none is entered; an entered claim age before full retirement age counts as a reduced benefit for the limit; for a former spouse who died after full retirement age without claiming, entering the age in years and months in the month of death counts the credits earned by then, while a whole-year age drops the months since the last birthday
    • The two forms agree whenever the deceased's actual benefit is at or above the PIA, which is every case the projection can produce for a deceased who was never paid a reduced benefit; the limit also applies to a deceased paid a reduced disability benefit, which the projection does not produce
    • Inputs are monthly, in today's dollars, before COLA and any benefit haircut; callers scale to their own annual frame
    • The family maximum is not applied to a survivor benefit here; POMS applies the limit after that adjustment, and with no other auxiliary on the record the two orders agree

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: survivor-benefit-rib-lim.md Mutation receipt: survivor-benefit-rib-lim.mutation.md

    Evidence tests: survivorBenefit.evidence.test.ts

    Implemented in: survivorBenefit.ts

    Feeds: social-security-benefit-annual, social-security-expected-present-value, social-security-survivor-switch-pv

  • Widow(er) reduction from the first month of widow(er) entitlement Composition record, justified by derivation

    Purpose: Reduce a survivor benefit for the months from the first month of widow(er) entitlement, not from the survivor's earlier own claim.

    Statement: socialSecurity/householdYear.ts#socialSecurityYear, which projection/internal/annualSocialSecurity.ts#annualSocialSecurity calls, prices a current spouse's widow(er) benefit, from the first year after the death, at the survivor's age in the first month of widow(er) entitlement: socialSecurity/survivorBenefit.ts#widowEntitlementAgeMonths returns the later of the survivor's own configured claim age in months and the survivor's age in January of the year after the worker died, the first month the projection pays the survivor (socialSecurity/nra.ts#attainedAgeMonthsInMonth, which counts an age as attained on the day before the birthday). That age feeds the widow(er) factor of survivor-benefit-rib-lim. From the month the survivor reaches the survivor full retirement age, the months of the widow(er) reduction period in which the widow(er) benefit itself was withheld under the earnings test are added back, up to that age; months withheld from the survivor's own benefit before the death are not, and neither are months withheld after the survivor full-retirement-age month. 42 U.S.C. 402(q)(6)(A)(iii) starts the widow(er) reduction period "with the first day of the first month for which such individual is entitled to such benefit or the first day of the month in which such individual attains age 60, whichever is the later", and 402(q)(7)(A) excludes "any month in which such benefit was subject to deductions". Units: months of age. Rounding: none; integers.

    Formula: e = max(ownClaimMonths, (deathYear + 1) x 12 - (birthYear x 12 + birthMonth - 1 - [birthDay = 1])); m = min(F, e + w) from the month the survivor reaches F when e < F, else e

    • ownClaimMonths: Survivor's own configured claim age in total months, before any earnings-test credit (unit: months) (domain: integer 744 to 840)
    • deathYear: The last calendar year the plan keeps the worker alive: birth year plus life age (unit: year) (domain: integer)
    • birthYear, birthMonth, birthDay: The survivor's date of birth (unit: date) (domain: a valid date)
    • w: Months of the widow(er) reduction period in which the widow(er) benefit was withheld under the earnings test (unit: months) (domain: integer >= 0)
    • F: Survivor full retirement age in total months (unit: months) (domain: integer above 720)
    • m: Survivor age in total months at which the widow(er) benefit is reduced (unit: months) (domain: integer)

    Timing: once per survivor per projection year

    Rounding: none

    Limits:

    • The plan states a life age, not a date of death, so December of the last year alive stands for the month of death and the survivor is first paid the next January; a real death earlier in that year would start entitlement, and the reduction, earlier
    • Entitlement from the month of death, which 20 CFR 404.621(a)(4)(ii) lets a widow(er) choose, is not modeled for the months of the death year: the projection pays no survivor benefit for them, so the reduction starts with the January it first pays
    • A caller whose person-year state carries no life age cannot place the death year; the reduction then starts at the survivor's own claim
    • The survivor is paid no survivor benefit before the survivor's own configured claim age: one claim age serves both benefits, so a survivor who would take the survivor benefit first and the own benefit later is not modeled (usc-42-402-r-survivor-deemed-filing-exemption), and the entitlement month is never earlier than that claim
    • For a former spouse who died, the plan holds no date of death; that widow(er) benefit is reduced from the claimant's own claim, as if the death came first
    • Earnings-test months are counted month by month, each month of the widow(er) reduction period in which the widow(er) benefit was paid and subject to a deduction (poms-rs-00615-482-arf-crediting-months); a month after the survivor full-retirement-age month, while the survivor is still under the old-age full retirement age that governs the test, is not credited to it
    • A disabled widow(er) entitled from age 50 is not modeled

    Provenance: derived by claude, implemented by claude.

    Independent review: codex

    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: survivor-reduction-entitlement-month.md Mutation receipt: survivor-reduction-entitlement-month.mutation.md

    Evidence tests: annualSocialSecurity.survivorEntitlement.evidence.test.ts

    Implemented in: annualSocialSecurity.ts; householdYear.ts; nra.ts; survivorBenefit.ts

    Feeds: social-security-benefit-annual

Engine source: engine/src/projection/internal/types/result.ts (YearResult.incomes.socialSecurity / socialSecurityStreams)

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.