Repository navigation
Conversation
# Conflicts: # policyengine_us/tests/code_health/test_rest_python_groups.py
Main's #10401 (ee6c1c5) added the ss-calculator-engine and ss-calculator-differential Rest groups beside this branch's social-security-inputs group. The Makefile keeps all three groups, their file lists and recipes, and lists all three files in REST_LISTED_TESTS so the core and remaining groups don't run them twice. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#10400 was squash-merged as 9969420, so Git's merge base is the pre-#10400 commit d453360 and every file #10400 added and this branch then changed shows as an add/add conflict. Each was resolved against #10400's head a6dcc92: - The four social_security_*_pia_ratio_factor.py variables, Makefile and tests/code_health/test_rest_python_groups.py: main's copy equals #10400's head, so this branch's filled version (built on that head) is kept. - tests/core/_social_security_state_backend.py: this branch's copy equals #10400's head and main changed it afterwards (#10504, Core 3.33.13 names), so main's version is kept. - changelog.d/ss-benefit-levers.added.md: main's release compiled and removed it; removed here too. A merge-tree run with a6dcc92 as the base gives the same tree apart from the version files (CHANGELOG.md, pyproject.toml, uv.lock), which this merge takes from main. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis
marked this pull request as ready for review
October 28, 2026 12:38
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Both prerequisite PRs, #10400 and #10401, have merged into main. Main is merged into this branch.
Round-1 review fixes
Review head:
fea0d26ef98a6771e946f621e8edd2bff11a046c. The only production behavior change in this round is proxy classification for assumed retirement claiming ages.1 - 36/180 - 16/240 = 11/15,1 - 16/180 = 41/45,1, and1 + 44/150 = 97/75. The review example keeps E=2018 and adjustment 41/45, withproxy=trueandunmodeled=false.[true, true], proxy count[2], positive sensitivity AIMEs and exclusion from the documented identified-only filter. There is no separate identified-only AIME output; consumers must filter proxy, unmodeled, missing-claim-age and pre-1983 records and report sensitivities separately.set_input, and YAML through Core'sSimulationBuilderagainst main's constant-one hooks. They carry 2024 public inputs into 2026, checking SSI directly plus the four components, their sum, taxes, household net income and every reached baseline cached output. A fourth case checks multiple household members and weighting counts once. A shared read-only neutral reference avoids repeated cloning.Reformwrappers and preserve PE-US's input carry-forward setting on those wrappers and the neutral reference. Core appliesreform=in place to an explicitly supplied system; the first local run exposed a selector leaking into two following existing cases (8 passed, 2 failed). A private-wrapper retry exposed Core's generic carry-forward default (9 passed, 1 failed). The final complete rerun passes all ten unchanged-assertion cases with private parameters and the country input contract preserved.22bcfb497c4235cd0498adf38a09d873a33919e7. Main remainsc55b806541c4b8a2fe1530a2468bf231befe5f22; no new merge or resolved hunks. Nothing under.github/changed.Round-1 validation
Full CI is green at
fea0d26ef98a6771e946f621e8edd2bff11a046c. The final-head run 40796218956 completed successfully. Verified at 02:00:31Z on October 29: 34 checks passed, one documentation-deployment check skipped, no failures or pending jobs. All 11 Baseline jobs, all nine Contrib jobs, all six Rest Python shards and their aggregate, Quick Feedback, and Microsimulation passed. Monitoring took about 74 minutes after the final push, within the 100-minute limit, with checks polled no faster than every five minutes.The changed files pass 25 cases in CI and locally: retirement resolution 14 (6 added), Core ingress/reporting 10 (5 added), composition 1 (new). Historical results below belong to preceding heads. Added simulation tests share one neutral reference and add one sensitivity reform; both reformed tests now own private parameter copies.
The original seven focused files plus the new composition file pass 184 cases at this head, up from 172 before the round-1 additions. Final JUnit artifacts confirm 47 ratio-service, 5 parameter-path, 14 retirement-record, 33 survivor-record, 12 worker-basis, 62 routing/code-health, 10 Core-ingress and 1 composition passes, with no failures, errors or skips in this focused set.
policyengine_us/tests/unit/test_ss_population_retirement.pycore/test_ss_pia_ratio_inputs.pyunit/test_ss_population_composition.pyThe two unit-file timings include their case setup/teardown and exclude the containing process's shared country import. Per-file process wall/RSS are unavailable for those files. Their containing
remaininggroup passed 3,204 tests with 11 skips, with 1,603.15s process wall and 7,928,704 KiB peak RSS; those resource figures describe the entire group, not these two files.All 33 original PIA-ratio YAML cases passed twice in Quick Feedback at this final head; both executions used coverage commands. First/second file times were: retirement factor 8 passed, 80.82/78.70s; claiming 5 passed, 77.22/77.65s; eligibility 10 passed, 71.83/71.13s; disability 4 passed, 46.74/46.03s; dependents 4 passed, 46.66/45.91s; survivors 2 passed, 32.80/32.54s. This retains the original S.770 case and all pre-existing assertions.
policyengine_us/tests/unit/test_ss_population_retirement.pycore/test_ss_pia_ratio_inputs.pyunit/test_ss_population_composition.pyAt this final head, Rest Python shard 2 passes the complete Core file: 10 passed in 46.98s, 94.34s process wall, 3,132,092 KiB peak RSS (2.99 GiB), exit 0. Before this round, its five cases took 35.915s in CI, with 68.47s process wall and 2,359,476 KiB peak RSS. The measured growth is 11.066s pytest / 25.87s full process, within the approximate one-minute added-time budget; peak RSS rises by 772,616 KiB (0.74 GiB). The five added cases total 16.787s of JUnit time including their setup/teardown; the shared reference setup is charged to the first existing case. The earlier intermediate head's two isolation failures are superseded by this complete final-head pass.
make formatand Ruff pass.env -u UV_FROZEN uv lock --checkpasses without relocking (150 packages, 9ms). Local Python uses the required 4 GiB / 1,800-second cap, thread caps of four, passive OpenMP waiting and a load1 gate at 100. Mutants use 2 GiB / 900 seconds.The evidence patch
claiming-sensitivity-proxy-removed.patchremoves this round's fix. It is caught by all four cases ofunit/test_ss_population_retirement.py::test_imputed_claiming_scenario_is_flagged_with_exact_birth, each failing at the proxy assertion. The normal-import run completed with 4 failed / 10 deselected in 1.57s, full process 56.66s, exit 1, under the required 2 GiB / 900-second cap (starting load1 42.86). Production source was restored and verified unchanged against Git. The named-output integration test is an additional intended detector. The four review mutants remain assigned to the hub; no local detection result is claimed for them.Reported-benefit PIA ratios
Implements design v3 C1–C3, A1/A5.2, P2, ADDENDA item 2, and amendments M2/M4/M8/M9. The four reported-benefit components now use the shared PIA service to apply
new_PIA(inferred_AIME, E; t) / baseline_PIA(inferred_AIME, E). Inference starts from the unreformed annual reported benefit, removes the claiming/composite-share adjustment and unreformed COLAs, and uses the baseline inverse's lower endpoint. The forward baseline and reform calculations read the sameFormulaPathas the household calculator; applicability uses its existing cohort/recompute mode.social_security_retirement_claim_age_months, a pure annual Person input without a formula or uprating. The explicit claiming selector offerssupplied_claim_age(default),entitlement_dates,age_62,age_65,fra, andage_70; missing or unusable supplied ages have no fallback.E = t - age_t + 62within each scoring year's cross-section. Disability uses adjustment one and supplied onset/entitlement information before its labelledpre_reformorcurrent_yearsensitivity.Named outputs and supported coverage
For
Xinretirement,disability,survivorsanddependents, these annual Person variables expose the inference. Monetary values are monthly dollars at the eligibility-year basis; the source benefit is annual.social_security_X_inferred_aime_inferred_aime_lower_bound,_inferred_aime_upper_bound_pia_ratio_eligibility_year_pia_ratio_claiming_adjustmenta * sis supplied in the worker-benefit ratio._pia_ratio_own_record_retired_worker_pia_ratio_missing_claim_age,_pia_ratio_pre_1983,_pia_ratio_unmodeled,_pia_ratio_proxy,_pia_ratio_measurement_uncertaintyZero AIME diagnostics mean no inference and must be read with the flags. For an identified-only own-record AIME distribution, exclude proxy, unmodeled, missing-claim-age and pre-1983 records and inspect measurement uncertainty. Every resolved
age_62,age_65,fraorage_70sensitivity is a proxy even with an exact birth date. Supported proxy AIME outputs remain available for separately reported sensitivities. Each coverage indicator has a Household_countvariable. Weight those household counts once when members share that weight; distinct Person weights require weighting the Person indicator directly. Unmodeled amounts should likewise be reported with their Person weights.Unsupported positive records keep factor one and expose flags: missing/unusable retirement claiming data, mixed retirement components, pre-1983 benchmarks, links without a supported worker basis, and auxiliaries without an identified worker/composite ratio. Recipient age alone never supplies an auxiliary worker PIA. S.770 survivors additionally require the shared ordinary-survivor/death basis; missing identification is neutral and flagged, while contradictory supplied information is rejected. No auxiliary age proxy is enabled. Identified, proxy and unmodeled results need separate population reporting.
Approximations and departures from the idealized design
[age_65], rather than bare scalar strings.[inverse(z), inverse(z + 0.10)). Paid annual/survey amounts only identify this interval conditionally.annual_rounding_boundcan propagate an explicit annual measurement-resolution interval through all baseline divisors. Core stores named AIME values as float32 ceilings, so represented endpoints can differ by one float32 step; the pure service retains exact rational endpoints for boundary work.The claiming scenario changes the inference materially: C2's adjustment 0.70 recovers AIME $5,000 and a $1,165.248 unrounded annual gain; assuming one infers $2,978 and a $1,664.64 gain, about 43% larger.
Historical invariants and validation before round 1
Before round 1, full CI was green at
22bcfb497c4235cd0498adf38a09d873a33919e7. Run 40763309982 completed successfully, verified at 21:48:44Z on October 28: 33 checks passed and one documentation-deployment check was skipped. All 11 Baseline jobs, all nine Contrib jobs, all five Rest Python shards and their aggregate, Quick Feedback, and Microsimulation passed. All 33 original PIA-ratio YAML cases now pass, including the formerly failing disability proxy and the retained S.770 case.First full CI before the corrections
The first full CI run at
27bd7fdb7803513aded985e35faec5d5b5f04d2fran on the ready PR from 12:38–13:56Z on October 28. All 11 Baseline jobs passed, includingssa-usda(2,028 cases), as did Microsimulation, package/compatibility, PR validation, and five of six Rest Python shards. The remaining 103-file process in Rest Python shard 3 also passed 3,163 cases with 11 skips; that shard failed in the separate household-calculator differential process. Eight contributed jobs passed; the remaining contributed job reported the disability failure and stopped during the final retirement case.The seven focused Python files passed 172 tests in CI, including all five H5/differential integration cases:
policyengine_us/tests/unit/test_ss_pia_ratio.pyunit/test_ss_pia_ratio_parameters.pyunit/test_ss_population_retirement.pyunit/test_ss_population_survivor.pyunit/test_ss_worker_basis.pycode_health/test_rest_python_groups.pycore/test_ss_pia_ratio_inputs.pyThe PIA-ratio integration checks cover supplied H5 claiming months, the pure-input contract, float32 endpoint round-trips, and lever-off equality for the reported-situation and public H5 routes. The latter restore the original constant-one population hooks within the merged model and compare the requested benefit/downstream outputs and every reached baseline cached output; they do not claim an exhaustive country-system comparison. Existing
core/test_social_security_inputs.pyalso passed in Rest Python shard 6. Microsimulation completed its three processes with 115 passed, 184 passed / 4 skipped, and 82 passed.TOB is covered by passing CI. In Rest Python shard 5, job 122820893300, both
baseline/gov/ssa/revenue/test_tob_revenue.pycases passed: generated split invariants (75.65s call) and preservation of supplied inputs and cached current-law tax in either calculation order (78.78s call). Their containing policy process finished with 982 passed in 630.95s. This supplies the clean result missing from the earlier local run capped at 1,800 seconds.The first full run passed all four dependent PIA-ratio YAML cases and the first 22 retirement cases. Disability had three passes and the proxy-year failure described below. The final S.770 retirement case did not finish, so that run supplies no full-file retirement pass; the interrupted log supplies no survivor PIA-ratio file result.
Main merge and three CI fixes
ff6d5e35fada97570c3f2f55453a12359d06f6aabrings in upstream main atbf108e931a, including Add S.770 section 8 investment and active income tax options #10402's narrower household-calculator reference exclusions. The merge was clean: no conflicts or resolved hunks.WithoutHouseholdCalculatorremoved parameter nodes that the reported-benefit population route now reads, raisingAttributeErrorbefore legacy comparisons could run. Commitae2d60bbbbretainsgov.contrib.ssa.piaandgov.contrib.congress.s770.section_2in that reference because these formula switches now serve the reported-benefit route too. Calculator-only variables remain removed, and the exclusion list remains a flat tuple of dotted paths. Production parameter access retains its normal schema requirements. The normal differential passed all eight tests. A supplementary reference-only mutant probe adds the numerically neutral0 * person("ss_calculator_supplied_pia", period)dependency toss_piaand uses the real reference Reform and Core builder. Its first attempt deferred before Python started (exit 75 at 20:19:00Z, load1 119.23). The retry started at 20:27:22Z with load1 59.82 under the required 2 GiB / 900-second cap, then timed out after 900.2s (full process 902.06s), exit 137, with no collection output or completed assertions. The runner restored the source and verified it against the saved copy and Git. Under the lane I/O rule this mutant is unrun: no detection proof is claimed, and a default-QoS hub rerun is needed.pre_reformsensitivity place an unidentified onset before the substantive PIA reform: the fixture's PIA change begins in 2026, soE = 2026 - 1 = 2025. Adjustment one and an inapplicable cohort reform give ratio one and an unchanged annual benefit of $12,000. Production instead took the parameter gate's historical YAML start (2000), producingE = 1999. Commitbc32567e49finds the first operative change through the shared formula path, honoring its separate factor, bend-point and indexing gates, and applies the fallback only to positive records with a missing basis. The separatecurrent_yearsensitivity remainsE = t(2026 in this fixture), and supplied onset/entitlement or worker-basis information retains precedence over either proxy. The new disability unit file passed all 12 cases through normal, unmodifiedpython -m pytest; YAML results are recorded below.27bd7fdb78after country loading (about 19 minutes total), and 1/1 in 100.83s on the merged tree with process exit 0. The merged probe's 30-second snapshots show Core'stest_runner.py:447cloning the baseline through recursiveParameterNode.clone/deepcopyduring setup, followed by pytest garbage collection during cleanup. The sharedparameters.pyandpia.pyfiles are byte-identical to upstream main; a native authored-subtree probe computes the fixture's PIA of 2313.6 in 0.000197s. The original 23-case file has 13 distinct parameter-input combinations retained in Core's cache. Each newly cached combination clones the baseline; a combination with an inline parameter reform adds a second full parameter-tree clone. Earlier CI processes reached 9,605 MB with six combinations and 10,878 MB with seven; the memory guard acts between files. Accumulated cache pressure is the inferred explanation for the file-level stall; RSS during the stalled final case was not measured. Commit22bcfb497csplits the 23 unchanged case blocks across three files: the original factor file keeps eight cases / five combinations, claiming keeps five / five, and eligibility keeps ten / four. Every case block matches byte-for-byte, and the S.770 case remains in the original file with its pinned bend points and all assertions. Existing file isolation in both Contrib and Quick Feedback runs each file in a fresh subprocess, releasing its cache afterward. The split changes only YAML fixture organization, with no changes to Core, production PIA calculation, or CI configuration.Local validation after the fixes
Local validation head:
22bcfb497c4235cd0498adf38a09d873a33919e7. These local results are separate from the passing jobs and failed cases in the historical CI run above; CI at the corrected head is recorded below.unit/test_ss_calculator_differential.pypolicy/contrib/ssa/benefit_levers/social_security_disability_pia_ratio_factor.yamlpolicy/contrib/ssa/benefit_levers/social_security_retirement_pia_ratio_factor.yaml(8 cases)policy/contrib/ssa/benefit_levers/social_security_retirement_pia_ratio_factor_claiming.yaml(5 cases)policy/contrib/ssa/benefit_levers/social_security_retirement_pia_ratio_factor_eligibility.yaml(10 cases)unit/test_ss_population_disability.pyThe disability YAML pass markers cover the supplied-onset case and the formerly failing missing-onset
pre_reformcase. Thecurrent_yearand supplied-eligibility precedence cases did not complete, so neither has a result from this run.Both Python files above used normal, unmodified
python -m pytest. The differential process completed at 18:37:33Z with six NumPy warnings from an unchanged unemployment formula and no assertion failures.env -u UV_FROZEN uv lock --checkpassed without relocking (150 packages resolved in 55ms).make formatleft 7,648 files unchanged and Ruff reported all checks passed.Local Python processes use the required 4 GiB / 1,800-second cap (mutants: 2 GiB / 900 seconds), thread caps of four, passive OpenMP waiting, and country imports start only at load1 at most 100. Any process capped during import or collection is reported as unrun rather than credited with a result. No microsimulation runs locally.
CI at the corrected head
The corrected head was pushed as a fast-forward, and its entire full CI run completed successfully. The final 21:48:44Z snapshot has 33 passed, 1 skipped, with no failures or pending jobs. Baseline
ssa-usdapassed 2,028 cases. The Contrib other-shard-1 job passed every required YAML file and all its remaining tests.At this corrected head, the seven focused files listed above again passed all 172 tests. The new disability unit file added 12 passes, and the household-calculator differential passed 8/8 independently. Completed-job logs provide these targeted results:
policyengine_us/tests/unit/test_ss_calculator_differential.pyunit/test_ss_population_disability.pycore/test_ss_pia_ratio_inputs.pypolicy/baseline/gov/ssa/revenue/test_tob_revenue.pypolicy/contrib/ssa/benefit_levers/social_security_disability_pia_ratio_factor.yamlpolicy/contrib/ssa/benefit_levers/social_security_retirement_pia_ratio_factor.yamlpolicy/contrib/ssa/benefit_levers/social_security_retirement_pia_ratio_factor_claiming.yamlpolicy/contrib/ssa/benefit_levers/social_security_retirement_pia_ratio_factor_eligibility.yamlpolicy/contrib/ssa/benefit_levers/social_security_dependents_pia_ratio_factor.yamlpolicy/contrib/ssa/benefit_levers/social_security_survivors_pia_ratio_factor.yamlThe four required YAML files also passed twice in Quick Feedback, with first-run times of 41.93s, 71.47s, 69.18s, and 66.27s respectively. In Contrib, every individual case in the original retirement file took at most 16.49s, bounding the S.770 case at 16.49s or less. The duration rows omit case names, so its individual timing is not separately identified. All nine SSA batches exited 0; the original retirement file's complete process took 131.9s including setup and cleanup.
Interaction with #10486
At the checked heads, the static overlap check found no file changed by both branches, and a trial merge was clean. They interact in the public component formulas and
tools/social_security_benefit_levers.py: #10486 preserves the reported-routejoint_cola_factor * pia_ratio_factorand bypasses these population factors for calculated sources. C imports only the source enum from that shared helper; its inversion uses the unreformed statutory COLA ledger, so E3 withholding never enters the baseline divisor. Their reported-route factors compose multiplicatively. This is a static compatibility check; the combined branch has not been exercised at runtime.impact: none under current law (contributed reform); the hub will measure
axiom: n/a: contributed reform, not enacted law