Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
66 commits
Select commit Hold shift + click to select a range
f68832e
Document Social Security benefit lever implementation plan
MaxGhenis Oct 23, 2026
ad80c62
Add audited Social Security COLA and bend-point histories
MaxGhenis Oct 23, 2026
6758e66
Audit stored Social Security scoring sources before applying levers
MaxGhenis Oct 23, 2026
1d17f32
Separate reported Social Security inputs and compose benefit levers
MaxGhenis Oct 23, 2026
2a81c22
Isolate and extend Social Security compatibility coverage
MaxGhenis Oct 23, 2026
ee98c1d
Preserve separate compatibility reports and reported-input uprating c…
MaxGhenis Oct 23, 2026
6101d97
Preserve overlapping Social Security sources and exact multi-year inputs
MaxGhenis Oct 23, 2026
b9c7191
Honor selected bases and zero rows for multi-year benefit sources
MaxGhenis Oct 23, 2026
89edc6c
Share identical COLA policy fixtures across exposure cases
MaxGhenis Oct 23, 2026
8401cd8
Treat Social Security benefit multipliers as dimensionless levels
MaxGhenis Oct 23, 2026
fa7ddfa
Run Social Security lever YAML serially on the existing CI runner
MaxGhenis Oct 23, 2026
561a664
Clarify the Core public retirement override fixture
MaxGhenis Oct 23, 2026
fbca48f
Verify original Social Security dataset columns extend once
MaxGhenis Oct 23, 2026
460f573
Preserve benefit metadata and explicit multi-year test weights
MaxGhenis Oct 23, 2026
4789c54
Check declared Social Security uprating metadata
MaxGhenis Oct 23, 2026
99d7384
Use raw calculation arrays inside multi-year benefit factors
MaxGhenis Oct 23, 2026
849ad59
Preserve legacy Social Security inputs across ingress and extraction
MaxGhenis Oct 23, 2026
1a7b912
Compare legacy benefit ingress against unmodified main
MaxGhenis Oct 23, 2026
43a07be
Retain automatic benefit source provenance across later writes
MaxGhenis Oct 23, 2026
d0187ab
Require an independent main reference and nonzero dataset evidence
MaxGhenis Oct 23, 2026
9bb01d8
Require compatible Core input provenance APIs
MaxGhenis Oct 23, 2026
4372e6b
Isolate extraction comparisons with the review reproducer
MaxGhenis Oct 23, 2026
dfd2e17
Compare every Core builder form with legacy main inputs
MaxGhenis Oct 23, 2026
1ed0b37
Fetch the independent main reference for CI comparisons
MaxGhenis Oct 23, 2026
90c64b5
Preserve every recorded benefit override in household extraction
MaxGhenis Oct 24, 2026
a8b1ec8
Preserve legacy Social Security inputs across Core write routes
MaxGhenis Oct 24, 2026
3db106d
Merge current main into Social Security benefit levers
MaxGhenis Oct 24, 2026
02b7415
Cover branch slots and direct Social Security input helpers
MaxGhenis Oct 24, 2026
5f4b175
Supply exact-period weights in compound Microsimulation fixtures
MaxGhenis Oct 24, 2026
01b60b2
Keep calendar-year factor checks for rolling compound inputs
MaxGhenis Oct 24, 2026
f164c21
Clear compound test caches before rechecking annual factors
MaxGhenis Oct 24, 2026
1f28500
Preserve Social Security sources across Core input routes
MaxGhenis Oct 25, 2026
cd6d298
Keep legacy dump fixtures compatible with removed empty holders
MaxGhenis Oct 25, 2026
6ff667b
Synchronize raw Social Security seeds during initial loading
MaxGhenis Oct 25, 2026
7dce1d6
Refresh automatic benefit sources during composed loading
MaxGhenis Oct 25, 2026
9f8c6c3
Reconcile successful benefit inputs when initial loading fails
MaxGhenis Oct 25, 2026
9f7db36
Search composed Social Security input routes with a state machine
MaxGhenis Oct 25, 2026
8396422
Resolve reported benefit sources at read time
MaxGhenis Oct 25, 2026
8c4640f
Preserve benefit sources through branch and cache cleanup
MaxGhenis Oct 25, 2026
b338d0a
Skip source routing before populations exist
MaxGhenis Oct 25, 2026
607106f
Supply group role metadata to the Core test backend
MaxGhenis Oct 25, 2026
ce2ad34
Refresh derived benefits after source writes
MaxGhenis Oct 25, 2026
ffb62a5
Check benefit readers against surviving source inputs
MaxGhenis Oct 25, 2026
5cff908
Load Core calendar metadata for reader tests
MaxGhenis Oct 26, 2026
25fde8a
Report all values on mismatching benefit routes
MaxGhenis Oct 26, 2026
f0cda5e
Verify benefit state machine under both Core versions
MaxGhenis Oct 26, 2026
70b4949
Remove unapproved Social Security CI job
MaxGhenis Oct 26, 2026
2ecebb1
Compare fresh benefit formulas and assert intended cache differences
MaxGhenis Oct 26, 2026
d91e28f
Exercise inherited sources across branch builder rewrites
MaxGhenis Oct 26, 2026
d52f0c0
Bound composed branch coverage per generated example
MaxGhenis Oct 26, 2026
5826256
Observe compound source refresh across Core versions
MaxGhenis Oct 26, 2026
2a289b5
Preserve Social Security helper precedence and bounded refreshes
MaxGhenis Oct 26, 2026
2e7cccd
Compare compound benefit helpers with pinned current law
MaxGhenis Oct 26, 2026
a439dae
Refresh rebuilt and exact source caches within dependency bounds
MaxGhenis Oct 26, 2026
82d52db
Exercise real Core bookkeeping in benefit input state machine
MaxGhenis Oct 26, 2026
57dc195
Isolate synthetic Social Security checks from microsimulation CI
MaxGhenis Oct 26, 2026
13216f9
Fix refresh of Social Security formula returns without holder storage
MaxGhenis Oct 27, 2026
fb4d238
Generate skipped-storage cache policies in Social Security input machine
MaxGhenis Oct 27, 2026
06092f1
Cover reported-source writes with fast-only benefit caches
MaxGhenis Oct 27, 2026
871ee0e
Date fast returns at memo insertion and preserve cache serialization
MaxGhenis Oct 27, 2026
d55a673
Keep restored memo dates ahead of subsequent Core stores
MaxGhenis Oct 27, 2026
5c0e93e
Preserve formula dates across duplicate memo assignments
MaxGhenis Oct 27, 2026
a7cf9ad
Merge main into ss-benefit-levers
MaxGhenis Oct 27, 2026
6498687
Preserve formula return dates across bulk cache deletion
MaxGhenis Oct 27, 2026
df0ec80
Generate bulk deletion and source-write state transitions
MaxGhenis Oct 27, 2026
a6dcc92
Retain shared benefit parameters in calculator reference
MaxGhenis Oct 27, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .github/workflows/push.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -501,6 +501,8 @@ jobs:
- name: Update system certificate authorities
id: system-ca
uses: ./.github/actions/setup-system-ca
- name: Fetch Social Security differential reference
run: git fetch --no-tags --depth=1 origin 8986e05cab65d56435a23df1910e2d35f2c5f5bf
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/python-tests.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ jobs:
- name: Update system certificate authorities
id: system-ca
uses: ./.github/actions/setup-system-ca
- name: Fetch Social Security differential reference
run: git fetch --no-tags --depth=1 origin 8986e05cab65d56435a23df1910e2d35f2c5f5bf
- name: Set up Python
uses: actions/setup-python@v6
with:
Expand Down
33 changes: 27 additions & 6 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ REST_SPM_TESTS := $(TESTS)/core/test_spm_policy_family.py \
# from its dry run and spreads their files over parallel runners by recent
# CI times, splitting a long group into chunks. A process still runs files
# of one group only, in this order, so none holds more than its group here.
REST_PYTHON_GROUPS := cached-arrays heavy-a heavy-b core-heavy ss-calculator-engine ss-calculator-differential core policy-parameters policy remaining-heavy remaining
REST_PYTHON_GROUPS := cached-arrays heavy-a heavy-b core-heavy ss-calculator-engine ss-calculator-differential social-security-inputs core policy-parameters policy remaining-heavy remaining
REST_CACHED_ARRAY_TESTS := $(TESTS)/test_formulas_do_not_write_into_cached_arrays.py
REST_HEAVY_A_TESTS := $(TESTS)/test_md_poverty_line_credit_invariants.py \
$(TESTS)/core/test_ald_determinism.py \
Expand All @@ -78,6 +78,11 @@ REST_SS_CALCULATOR_ENGINE_TESTS := $(TESTS)/unit/test_ss_calculator_engine.py
# Local macOS/Python 3.13.9, four numeric threads: all eight cases passed
# with 2.45 GiB peak RSS, below the ~10 GB per-process target above.
REST_SS_CALCULATOR_DIFFERENTIAL_TESTS := $(TESTS)/unit/test_ss_calculator_differential.py
# Synthetic household input/branch checks load no national dataset. Keep their
# retained reform systems in a separate process rather than growing core's heap.
# Local 4 GiB-capped run peaked at 2.385 GiB before its 1800s loaded-host cap;
# CI run 40318424511 passed all 126 cases in 312.62s (6:20 process wall).
REST_SOCIAL_SECURITY_INPUT_TESTS := $(TESTS)/core/test_social_security_inputs.py
# These three parameter modules construct full reform trees; one also loads
# a default Microsimulation only to read parameters. Release their heap before
# the remaining policy tests, rather than stacking both in the 13.9 GB group.
Expand All @@ -93,7 +98,7 @@ REST_REMAINING_HEAVY_TESTS := $(TESTS)/test_niit_section_911_magi.py \
$(TESTS)/test_schedule_d_filer_net_capital_gain.py
REST_LISTED_TESTS := $(REST_CACHED_ARRAY_TESTS) $(REST_HEAVY_A_TESTS) $(REST_HEAVY_B_TESTS) \
$(REST_CORE_HEAVY_TESTS) $(REST_SS_CALCULATOR_ENGINE_TESTS) $(REST_SS_CALCULATOR_DIFFERENTIAL_TESTS) \
$(REST_POLICY_PARAMETER_TESTS) $(REST_REMAINING_HEAVY_TESTS)
$(REST_SOCIAL_SECURITY_INPUT_TESTS) $(REST_POLICY_PARAMETER_TESTS) $(REST_REMAINING_HEAVY_TESTS)
REST_IGNORES := $(addprefix --ignore=,$(TESTS)/policy/contrib $(TESTS)/microsimulation $(REST_SPM_TESTS))
# $(call rest_pytest,<group>,<paths>,<more paths to ignore>) is the pytest
# command for one group. With REST_REPORT_DIR set, each group writes its own
Expand Down Expand Up @@ -166,10 +171,12 @@ test-yaml-structural-heavy-shard-4:
test-yaml-structural-other:
# Per-subdir so every remaining contrib folder runs in its own subprocess,
# instead of stacking ~20 light files into one ~13-min catch-all batch that
# risked the 30-min per-batch timeout on slow runners. ssa is excluded (it
# has no YAML tests — only a pytest .py run elsewhere). --workers 1 so a
# single ~1-5 GB peak is resident at a time, leaving headroom for growth.
# risked the 30-min per-batch timeout on slow runners. SSA runs separately
# per-file below because its reform combinations retain large model copies.
$(BATCH) $(TESTS)/policy/contrib --exclude states,ctc,ubi_center,federal,harris,treasury,crfb,congress,refundable_credit_conversion,ssa --mode per-subdir --workers 1
# SSA's COLA file measured about 11 GB locally. Override CI's worker setting
# as well as the CLI default so each file exits before the next one starts.
POLICYENGINE_BATCH_WORKERS=1 $(BATCH) $(TESTS)/policy/contrib/ssa --mode per-file --workers 1
test-yaml-structural-other-shard-2a:
# ctc is microsim-heavy: per-file isolation frees each ~5 GB peak between
# files, and --workers 1 keeps only one peak resident at a time.
Expand Down Expand Up @@ -322,6 +329,8 @@ test-other-python-rest-ss-calculator-engine:
$(call rest_pytest,ss-calculator-engine,$(REST_SS_CALCULATOR_ENGINE_TESTS))
test-other-python-rest-ss-calculator-differential:
$(call rest_pytest,ss-calculator-differential,$(REST_SS_CALCULATOR_DIFFERENTIAL_TESTS))
test-other-python-rest-social-security-inputs:
$(call rest_pytest,social-security-inputs,$(REST_SOCIAL_SECURITY_INPUT_TESTS))
test-other-python-rest-core:
$(call rest_pytest,core,$(TESTS)/core,$(REST_LISTED_TESTS))
test-other-python-rest-policy-parameters:
Expand All @@ -332,8 +341,20 @@ test-other-python-rest-remaining-heavy:
$(call rest_pytest,remaining-heavy,$(REST_REMAINING_HEAVY_TESTS))
test-other-python-rest-remaining:
$(call rest_pytest,remaining,$(TESTS)/,$(REST_LISTED_TESTS) $(TESTS)/core $(TESTS)/policy)
MICROSIMULATION_GROUPS := base social-security-current-law
test-microsimulation:
pytest $(TESTS)/microsimulation --maxfail=0
@failed=""; for group in $(MICROSIMULATION_GROUPS); do \
$(MAKE) --no-print-directory test-microsimulation-$$group || failed="$$failed $$group"; \
done; \
if [ -n "$$failed" ]; then echo "Failed microsimulation groups:$$failed"; exit 1; fi
test-microsimulation-base:
# Compatibility checks copy policy for differential/branch isolation. Keep
# their heaps separate on this existing runner (local differential peak 7 GB).
pytest $(TESTS)/microsimulation --maxfail=0 --ignore=$(TESTS)/microsimulation/test_social_security_current_law.py
test-microsimulation-social-security-current-law:
# Only the two probes need a country system. Avoid importing another through
# the empty parent conftest in the pytest process that compares their JSON.
pytest $(TESTS)/microsimulation/test_social_security_current_law.py --confcutdir=$(TESTS)/microsimulation --maxfail=0 $(if $(findstring --junitxml,$(PYTEST_ADDOPTS)),--junitxml=simulation-social-security-current-law.xml)
# Local convenience: run both halves back to back.
test-other: test-other-python test-microsimulation
test-policy-contrib-python:
Expand Down
1 change: 1 addition & 0 deletions changelog.d/ss-benefit-levers.added.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Add composable Social Security benefit levers, pure reported component inputs, and a stored-data scoring preflight. Legacy public inputs remain independent sources, exposed through a read-time reported view without automatic twin arrays or provenance stamps. Initial loading routes sources so same-year benefit factors apply; later public writes remain final overrides. Explicit reported inputs retain priority, while exact compound totals keep their period semantics. Compound helpers retain initial-input precedence. Source refreshes use Core's dependency sequence cutoff, including formula returns kept only in the fast cache, preserving raw caller values, supplied inputs and earlier formula results. Bulk cache deletion preserves surviving formula dates; a formula return whose date is lost is conservatively recalculated after a dependent source write. Builders, partial loading failures, copies, cache replay, extraction, dump restoration and subsampling preserve successful source inputs. A state machine searches combinations of write and copy routes.
185 changes: 185 additions & 0 deletions docs/social-security-benefit-levers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
# Social Security benefit levers

The benefit lever interface separates reported annual benefits from public
benefit outputs. Stable public formulas combine a selected reported source with
independent across-the-board and supplied payable-share factors, and one joint
payment-year COLA factor. All factors are one by default. A stored-data preflight
checks the scoring artifact's identity and columns before population scoring.

The engine contract does not certify API or application field exposure.
Calculated benefit source modes are reserved for subsequent implementations.
No package-specific values are part of this interface.

## Input and override contract

The public components are `social_security_retirement`,
`social_security_disability`, `social_security_survivors`, and
`social_security_dependents`. Their `_reported` twins are pure annual Person
inputs, including zero, with the original effective per-capita SOI/CBO
uprating. Public formulas have no variable-level uprating. Dataset extension
still uprates original column names before constructor migration.

Initial ingress through either PE-US constructor, dataset loading, or Core's
`SimulationBuilder` (including YAML) migrates a legacy public input to its
reported twin separately for each supplied period. The public formula then
applies the levers in the stored year as well as later years. When both names
are supplied in the same period, the reported amount remains the source and
the public input remains a final override.

A public `set_input` after construction is a final override for its own period.
It also seeds or refreshes the reported twin for that period unless the caller
explicitly supplied that twin. Automatically migrated or seeded twins follow
later public writes, preserving main's uprating and earlier-year lookbacks;
calculated or uprated caches do not block seeding. An explicitly supplied
reported input, including zero, retains precedence. Aggregate `social_security`
inputs remain final overrides.
Clones and branches retain these rules. Later public writes replace their
runtime input-name list privately, preserving overrides in legacy extraction
without changing a parent or sibling clone. Migrated inputs extract as twins;
later overrides extract alongside their twins and retain precedence on reload.
A private provenance envelope preserves the distinction between automatic and
explicit sources through legacy extraction and complete snapshots, including
subsequent public writes after reload. Builders consume this metadata before
validating ordinary situation inputs.

This compatibility layer requires Core 3.33.1 or later for recorded-input,
storage-origin and replay APIs. The lockfile uses Core 3.33.2.

Each component has a `_source` selector with default `reported`. Reserved
options `supplied_aime`, `supplied_pia`, `supplied_ratio_survivor`, and `history`
fail explicitly until their calculation routes are implemented. Selection never
depends on whether an amount is positive. The selected `_base` and the
`_pia_ratio_factor` (currently one) are separate from reported inputs. Future
calculated routes must bypass population PIA and COLA factors, apply the other
factors once, and perform their earnings test on the resulting payable amount.

`policyengine_us.tools.social_security_inputs.export_source_inputs(simulation)`
exports all recorded pure-input periods and entity memberships as `sources`,
with supplied formula-variable values in a separate `overrides` payload. Cached
calculations are excluded. `reload_source_inputs(payload, ...)` restores sources
before final overrides. Core's legacy `extract_person` retains every recorded
input period for the four benefit components and their reported twins, excluding
formula caches. This keeps later overrides final on reload and preserves their
source histories and future uprating. Other variables keep Core's usual first
known period. Neither interface settles the separate API/application metadata
and field-exposure contract.

Annual benefit inputs retain Core's period normalization. For example, supplying
`social_security_retirement: {"2024-07": 500}` through a PE-US constructor stores
500 under the rolling annual period `year:2024-07`, then migrates that exact
stored period to `_reported`. A July monthly output still follows Core's normal
calendar-year amount divided by twelve; it is not a new monthly entitlement
schedule. The complete source snapshot preserves the rolling annual key.

PE-US wrappers also preserve an exact supplied multi-year annual total, such as
`year:2024:2`. They apply a benefit multiplier only when it is identical in every
calendar year covered by that total. Differing annual multipliers require
separately supplied annual source amounts, because the total provides no annual
allocation for nonzero rows. Zero rows remain zero. An exact supplied `_base`
amount takes precedence over `_reported`, and base neutralization or abolition
still applies; base overrides for only part of a multi-year source require
annual source amounts. Explicit public multi-year inputs remain final overrides,
and neutralization still takes precedence. Raw Core `SimulationBuilder` and YAML
should supply canonical annual `_reported` periods for lever calculations; the
additional exact multi-year reported-source adapter belongs to PE-US wrappers.
A weighted `Microsimulation` request also needs an explicit weight for that exact
multi-year period, matching Core.

## Parameters and composition

All lever parameters live under `gov.contrib.ssa.benefit_levers`:

| Parameter | Default | Meaning |
| --- | --- | --- |
| `across_the_board.start_year` | 9999 | First affected payment year |
| `across_the_board.rate` | 0 | Proportional change, finite and at least -1 |
| `payable.share` | 1 | Externally supplied annual share in [0,1] |
| `payable.oasi_only` | false | OASI-only rather than combined OASDI |
| `cola.start_year` | 9999 | First affected January payment year |
| `cola.effective_year` | 0 | Optional first affected COLA effective year |
| `cola.effective_month` | 12 | Effective month when effective year is supplied |
| `cola.end_year` | 0 | Last affected payment year, or no end |
| `cola.pp_change` | 0 | Addition to the annual proportional COLA rate |
| `cola.alternative_rate` | -1 | Supplied pinned alternative annual rate; -1 selects baseline |
| `cola.withholding_enabled` | false | Enable component payment-year withholding terms |

A positive `effective_year` takes precedence over `start_year`: December 2028
means the first affected check is January 2029. Parameter values can vary by
payment year. Example modeling assumptions are CPI-E `pp_change = 0.0015`
(+0.15 percentage points), or chained CPI `pp_change = -0.0030`
(-0.30 percentage points), with an explicitly selected start. These are examples,
not observed index series or package presets. An alternative-rate path must be
pinned and recorded by its supplier; it is not generated from a household.

For each exposed payment event, statutory baseline rate c and configured
addition delta give adjusted rate max(0,c+delta), without a second rounding of
the policy addition. The joint multiplier is `(1+adjusted)/(1+c)` unless that
event is withheld, in which case it is `1/(1+c)`. Each component's
`_cola_withheld` term defaults false; a subsequent means-test implementation
supplies the rules. Switching a policy off later preserves its earlier effect
on the benefit level. Public reported-route benefits multiply this joint factor,
the PIA ratio factor, the across-the-board factor, and the payable factor once.

The payable supplier specifies an annual path beginning in the depletion year
and supplies a blended share for a depletion year beginning partway through the
year. The engine does not predict depletion, share paths, or within-year fund
cash flow. In OASI-only mode, disability is unaffected; a positive dependent
benefit requires an explicit `social_security_dependents_fund` of `oasi` or `di`.
Mixed or unidentified dependent streams require an external allocation/blended
scenario; the engine does not assign the entire stream to OASI silently.

## COLA history and exposure

Historical statutory events are under `gov.ssa.cola_history` through December
2025 (January 2026 payment), including June/July events before 1983, zero-rate
years and the corrected December 1999 2.5% rate. The current main January 2027
SSA schedule already includes the actual Q3 2026 index. Starting with that
schedule entry, rate construction uses `gov.ssa.uprating`, comparing with the Q3
index associated with the last effective nonzero COLA. Decimal half-up rounding
to 0.1 percentage point preserves the high-water base over zero-COLA years.
`cola_events(system.parameters, through_year)` in
`tools.social_security_benefit_levers` exports the exact baseline schedule for a
scoring receipt. Freeze this schedule together with the reform parameters.

Historical PIA COLA rounding is implemented separately by
`tools.ssa_cola_history.apply_cola_to_pia`: round upward to a dime before June
1982 and downward thereafter; unknown rule codes fail explicitly. Reported-route
ratios validate exposed rules but do not reconstruct a rounded individual PIA
from an annual recipient amount. Initial-PIA computations/inverses before the
1983 benchmark remain outside this implementation. Bend-point history is now
available back to 1979 for future services.

Per-component optional inputs are `_first_cola_year`/`_first_cola_month`,
`_worker_eligibility_year`, `_worker_id`, and
`_entitlement_year`/`_entitlement_month`. A first-COLA date takes precedence.
A worker link follows an explicit component basis on the linked worker, falling
back to the shared authoritative `social_security_worker_computation_year`.
A supplied recipient basis is preserved; conflicting positive bases or invalid
links fail. No disability or deceased-worker computation basis is inferred from
a linked worker's retirement age.

Absent identified dates, own retirement uses age-62 eligibility (even if claiming
later); older survivors use an age-60 recipient proxy. Positive disability,
child/caregiver dependents and young survivors use a pre-start exposure proxy.
These proxies are assumptions, not observed histories. Supply current-year
entitlement/first-COLA dates as the lower-exposure sensitivity; compare adjacent
retirement eligibility years for integer-age birthday uncertainty and age-50
survivor or linked-record alternatives where relevant. Report these sensitivity
envelopes separately from identified results. Without worker/share information,
auxiliary PIA inversion remains unidentified.

Each projected cross-section infers eligibility anew from `year - age + 62`.
It does not carry cohorts or income histories across static population rows.
A supplied retirement entitlement/claim date does not erase pre-claim COLAs.
Pre-1983 retirement eligibility can expose a July payment in its eligibility
year; modern retirement eligibility first exposes January of the next year.

Run the stored-data preflight described in
[the scoring-data guide](usage/social-security-scoring-preflight.md) before
population lever scoring. Its receipt records the artifact, bytes hash, stored
year, full stored columns, source mapping and projection convention.

For a supplied alternative-index level path, convert its pinned levels (including
an explicit pre-start anchor) with `projected_cola_rates`, then supply the
resulting payment-year rates under `cola.alternative_rate`. Record the source,
anchor, level path and generated rate path with the scoring receipt.
Loading
Loading