Calculation catalog › Social Security
Social security claiming sweep objective
The signed change in ending after-tax estate of the sweep's winning whole-year claim against the plan as entered (claim months included), and of the month refinement against that winner, the refinement ranked on the same objective; nominal at the plan's last year.
Field: Claim-age sweep: winner versus the plan as entered, and the month refinement versus the winner Unit: US dollars Basis: nominal dollars Computed by: UI-native Status: Complete chain
Appears in: ss-page (In-your-plan verdict "(±$X)" against the current claim ages, in the plan's last-year dollars, and the refinement "(±$Y over the whole-year pick)"); ss-page (Claim-age heatmap and ranked table read engine candidateSummary.endingAfterTaxEstate directly)
Calculation records
-
Claim-age refinement to the month Model record, justified by derivation
Purpose: Refine the sweep's whole-year winner to the month on the same objective, and publish its signed changes against the winner.
Statement: decisions/claimAgeSweep.ts#refineClaimAgeMonthly prices, through #refineClaimMonths, for each open claim in the canonical people order (model/peopleOrder.ts, never list order), every claim from a year below the winner's age to a year above it (62 to 70, none below the age reached in the start year; months 0 to 11, and only 70y0m at 70), with the other claims at the running best, in whole passes repeated until one changes no claim, the windows staying around the starting whole years, and a combination priced once not priced again (decision D-PEOPLE-ORDER, rule R7); there is no pass cap, since a changing pass takes a strictly greater primary metric and a combination's row is fixed once priced, so no combination is the incumbent twice and the search always ends at a fixed point, within one more pass than the windows hold combinations, a bound kept only as a guard that throws, and #refineClaimMonths reports the passes it ran (round-one review of #765, issue 4), each month through decisions/evaluateCandidate.ts#evaluateCandidate against the plan as entered and ranked alone by the sweep's objective policy (decisions/tournament.ts#rankEvaluations, margin 0). A month replaces the incumbent only when its row is eligible under the objective's constraints and its primary metric is strictly greater; a greater but ineligible month is counted and rejected. It publishes estateChangeVsWinner, the refined ending after-tax estate minus the winner's, and primaryChangeVsWinner, the objective's own difference, both signed and unrounded. Units: nominal dollars of the plan's last year, and the objective's own units. Rounding: none.
Formula:
take month m over incumbent i <=> eligible(m) and primary(m) > primary(i); estateChangeVsWinner = estate(refined) - estate(winner); primaryChangeVsWinner = primary(refined) - primary(winner)-
primary(m): The objective policy's metric of month m against the plan as entered (unit: the policy's own) (domain: finite) -
eligible(m): Month m meets every hard constraint of the policy (money-lasts, the estate floor, a structural diagnostic) (unit: boolean) (domain: true or false) -
estate(m): Ending after-tax estate of the plan with month m's claim (unit: nominal USD of the plan's last year) (domain: finite)
Timing: one full ledger run per combination tried: up to 36 per claim per pass (the months from a year below the whole-year pick to a year above it: 36, or 24 for a pick of 62, 25 for 69 and 13 for 70, fewer when the age reached in the start year cuts the window), 13 to 72 in the first pass, and a later pass prices only the combinations not yet priced
Rounding: none
Limits:
- Coordinate passes to a fixed point within a year of the whole-year winner: a month further away, or a combination that needs both claims moved at once, is not tried, and the page says "no month within a year of the whole-year pick ranks higher" rather than "optimal"
- Bridge durability and survivor liquidity score a month that removes the bridge or survivor years on the estate change, as the sweep does, until the objective-policy change ranks them on one fixed window
- Runs on request, 0.2 to 0.6 seconds on the example couples
Provenance: derived by
claude, implemented byclaude.Independent review:
codexGates (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-claim-age-monthly-refinement.md Mutation receipt: social-security-claim-age-monthly-refinement.mutation.md
Evidence tests: claimAgeSweep.refine.evidence.test.ts
Implemented in: claimAgeSweep.ts
Is the number of: social-security-claiming-sweep-objective
-
-
Whole-plan Social Security claim-age sweep Model record, justified by derivation
Purpose: Rank every whole-year claim age of the plan's open claims on the full ledger by a chosen objective, and publish the winner's signed estate change against the plan as entered.
Statement: decisions/claimAgeSweep.ts#sweepClaimAges takes the plan's open claims (socialSecurity/openClaims.ts#openClaims: of the first two Social Security streams with a positive entered PIA or an earnings history, those whose claim year, the birth year plus the claim age's whole years, is not before the start year; the others are already made and keep their own claim age in every row, #isClaimAlreadyMade, social-security-claim-already-made), holds an open claim that is paid as a disability benefit from its onset as the ledger pays it (decisions/generators.ts#claimAgeGridClaims), prices every combination of whole-year ages of the other open claims from #gridClaimAges (62 to 70, none below the age reached in the start year; decisions/generators.ts#socialSecurityClaimGridGenerator) with decisions/evaluateCandidate.ts#evaluateCandidate against the plan as entered, and ranks the rows with decisions/tournament.ts#rankEvaluations under the chosen objective policy with a margin of 0. The verdict (#claimAgeSweepVerdict) is empty with no claim, already-claimed when every claim is already made, disability when every open claim is paid as a disability benefit from its onset (those claims are published as disabilityPersonIds, and held in a ranked sweep), winner when an eligible row strictly improves the objective, otherwise aca-unpriced when no row is eligible and the plan as entered has a year whose premium tax credit cannot be priced (#unpricedAcaYears: each such year with its blocking support codes), ineligible when no row is eligible, flat when two or more eligible rows lie within 0.5 of the first and current-best otherwise. It publishes winnerEstateChangeVsCurrent, the winner's ending after-tax estate minus the plan as entered's (claim months included), signed and unrounded, each row's change the same way, a row as current only when every open claim is a whole year equal to it, and per row whether the objective ranked it on its own metric (objective) or on the estate fallback, because the plan as entered has no survivor or bridge years (estate-fallback-plan, then every row) or because this row has none (estate-fallback-row) (decisions/objectives.ts#rankedMetricBasis). Units: nominal dollars of the plan's last year, published as estateYear. Rounding: none.
Formula:
change(row) = estate(row) - estate(plan as entered); winner = the first eligible ranked row whose primary metric is above 0; verdict = claimAgeSweepVerdict(ranked, winner exists, unpriced credit years)-
estate(row): Ending after-tax estate of the plan with the row's whole-year claim ages (unit: nominal USD of the plan's last year) (domain: finite) -
estate(plan as entered): Ending after-tax estate of the plan with its own claims, months included (the decision context's baseline run) (unit: nominal USD of the plan's last year) (domain: finite) -
primary: The objective policy's metric against the plan as entered, higher is better (unit: the policy's own (dollars or years)) (domain: finite) -
claimYear: Birth year plus the claim age's whole years; before the start year means already made (unit: calendar year) (domain: integer)
Timing: one full ledger run per grid row, from the start year
Rounding: none; the page prints the sign of the printed magnitude, so a change that prints as $0 has neither
Limits:
- The plan's own conversion strategy is held fixed: a bracket fill resizes to each claim age, a fixed schedule does not. The Optimize page's co-optimization re-optimizes conversions but tries only 62, full retirement age and 70, one stream at a time, on the estate with a 1,000-dollar margin (claim-age-co-optimization), so the two searches can pick different ages, and a gain found here is measured with the plan's own conversions
- Whole years only, and at most two streams
- A claim counts as already made when its claim year is before the start year, the ledger's annual convention: the ledger ignores the birth month, so a claim in the start year whose month has passed is still treated as open; and a claim at or after full retirement age that was never filed can be paid up to six months back (20 CFR 404.621(a)(2)), which the test treats as made, the conservative side
- Withdrawing an application within 12 months of the first month of entitlement, repaying every benefit (20 CFR 404.640(b)(3), (b)(4)), and voluntary suspension from full retirement age (42 U.S.C. 402(z)) are not modeled
- Where the premium tax credit cannot be priced in a year of the plan as entered, no row is ranked: a claim age moves the income the credit depends on (26 U.S.C. 36B(d)(2)(B)(iii)); ranking only the rows that leave that income unchanged was measured and is not safe in either direction (an equal stand-in credit changed three of eleven such winners on the example plans)
- Bridge durability and survivor liquidity rank a row on the estate change when that row or the plan has no bridge or survivor years, within one ranking; each row says which of the two lacked them, and ranking both on one fixed window in today's dollars is a separate objective-policy change
- A disability benefit from its onset has no claim age to sweep: the sweep holds it as the ledger pays it and ranks the other claim, and refuses only when every open claim is one. The benefits-only ranking prices a couple's claims as pairs of claim ages and cannot place a disability benefit on that grid, so it ranks no claim age for either person
Provenance: derived by
claude, implemented byclaude.Independent review:
codexGates (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-claim-age-sweep.md Mutation receipt: social-security-claim-age-sweep.mutation.md
Evidence tests: claimAgeSweep.evidence.test.ts
Implemented in: claimAgeSweep.ts; openClaims.ts; generators.ts
Is the number of: social-security-claiming-sweep-objective
-
Records that feed this number
-
Social Security claim already made Model record, justified by derivation
Purpose: Decide, for every claim-age search, whether a claim was already made before the plan starts, so it is held rather than re-made.
Statement: socialSecurity/openClaims.ts#isClaimAlreadyMade says a claim at a claim age is already made when its claim year (#claimYearOf: the calendar year of the date of birth plus the claim age's whole years, the months ignored) is before the plan's start year, and open otherwise, the ledger's annual convention. It is the one test every claim-age search applies: #openClaims for the Social Security page's sweep and its month refinement, socialSecurity/analysis/expectedValue.ts#benefitsOnlyRanking, decisions/generators.ts#socialSecurityClaimGenerator for a stream and for each canonical age, projection/optimizePlan.ts#optimizePlanCoOptimizingClaimAge, and the Scenarios page's claim-age lever. A claim already made is held at its own claim age; a claim age whose claim year is before the start year is not offered. Units: calendar years. Rounding: none.
Formula:
claimYear = birthYear + claimAge.years; alreadyMade = claimYear < startYear-
birthYear: The calendar year of the person's date of birth as entered (no birth-month or January-1 adjustment) (unit: calendar year) (domain: integer) -
claimAge.years: The whole years of the stream's claim age; its months are ignored (unit: years) (domain: integer 62 to 70) -
startYear: The plan's first projection year (unit: calendar year) (domain: integer)
Timing: once per claim, against the start year
Rounding: none
Limits:
- The birth-month convention: the test reads years only, so a claim whose month has already passed in the start year is still open, and a claim age's months never move the claim year (born 1964-06-15, a claim at 62y6m falls in 2026, open in 2026 and made from 2027)
- A claim at or after full retirement age that was never filed can still be paid up to six months back (20 CFR 404.621(a)(2)); a claim year before the start year cannot be known to fall within those six months, so the test treats it as made, the conservative side
- Withdrawing an application within 12 months of the first month of entitlement, repaying every benefit (20 CFR 404.640(b)(3), (b)(4); a withdrawn application is treated as never filed, 404.640(d)), is not modeled: a claim made stays made
- Voluntary suspension from full retirement age to 70 (42 U.S.C. 402(z)) is not modeled, so a claim made cannot earn delayed credits here
Provenance: derived by
claude, implemented byclaude.Independent review:
codexGates (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-claim-already-made.md Mutation receipt: social-security-claim-already-made.mutation.md
Evidence tests: openClaims.evidence.test.ts
Implemented in: openClaims.ts
Feeds: social-security-claiming-sweep-objective, claim-age-co-optimization-combinations-evaluated
-
Transformations
- decisions/claimAgeSweep.ts#sweepClaimAges: winnerEstateChangeVsCurrent = the winner's ending after-tax estate − the plan as entered's (the decision context's baseline run, claim months included), signed and unrounded; the page prints the sign of the printed magnitude (a change that prints as $0 has no sign and no colour).
- decisions/claimAgeSweep.ts#refineClaimAgeMonthly: from the winner, every claim month within a year of it for each open claim in household order (62 to 70, none below the age reached, 70y0m only at 70), keeping a month only when its row is eligible under the objective and its primary metric is strictly greater; estateChangeVsWinner = refined − winner estate, and primaryChangeVsWinner the objective's own difference.
- Claims made before the plan's start year are held fixed and not swept (socialSecurity/openClaims.ts#openClaims: claim year = birth year + claim years); a plan with a Marketplace year whose premium tax credit cannot be priced is refused (verdict 'aca-unpriced', each year with its reasons), as is a disability benefit.
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.