Skip to content

Retirement-plan rule layer (WP2) - #572

Draft
MaxGhenis wants to merge 10 commits into
masterfrom
ir/wp2-20261024
Draft

MaxGhenis wants to merge 10 commits into
masterfrom
ir/wp2-20261024

Conversation

@MaxGhenis

@MaxGhenis MaxGhenis commented Oct 24, 2026 •

Copy link
Copy Markdown
Contributor

WIP. This PR builds milestone WP2 of the income-and-resources design (#559: §3.2 and the WP2 row of the milestone table). Round 3 approved 3a07cc1e. The head, 184d1081, merges master 4ab97297 into that commit, recounts the tiers and corrects one stale citation in two places. CI passed at 13c1428e, one commit earlier, and runs on the head now.

What it adds

A statutory rule layer for retirement plans, in src/populace_dynamics/income_resources/. WP4 (wealth and pension trajectories) and SSI2 (the value of retirement funds as SSI resources) will call it.

Module Rules
retirement_rules.py RMDs:
• applicable age by birth cohort (70½, 72, 73, 75);
• the April 1 required beginning date and the still-working delay;
• Uniform Lifetime and Joint and Last Survivor divisors in both vintages (TD 8987 and TD 9930);
• Roth exemptions;
• the 2009 and 2020 waivers.
early_distribution.py The 26 U.S.C. 72(t) additional tax on the includible amount:
• the 59½ boundary;
• the SIMPLE IRA 25% rate in the first two years;
• the death, disability, separation-from-service and SEPP exceptions.
contribution_limits.py The 402(g), 414(v), 415(c) and 219 ceilings for 2002–2026 from IRS announcements, including the ages 60–63 catch-up. The module projects later years from the statutory bases with an index path the caller supplies.
retirement_resources.py An account's SSI countable value under POMS SI 01120.210:
• the lump-sum value net of withdrawal penalties;
• exclusions for required job separation and for deeming;
• refusal when an election stays unresolved.
pension_cola.py CSRS and FERS cost-of-living adjustments:
• the FERS age-62 restriction and its exception categories;
• proration;
• monthly dollar rounding and caps.

Sources:

  • The builder extracted parameters from pinned captures under data/external/income_resources/retirement_rules_v1/, and each capture manifest records URL, time, size and SHA-256.
  • A folder-level .gitattributes (* -text) stores captures byte for byte, so their SHA-256s hold on every checkout.
  • Any year, age, account type, plan or option that the layer does not cover raises an error. The layer never substitutes a default.

Invariants

Each module's docstring states its invariants. Hypothesis properties and worked examples test them:

  • RMDs lie in [0, prior balance]. Exempt accounts give zero.
  • The applicable age is nondecreasing in birth year under either named 1959 reading.
  • Within a table, the RMD fraction is nondecreasing in owner age. Only the literal CFR joint-table cell breaks this (open question 2).
  • The early-distribution tax is nonnegative, at most 25% of the includible amount, and linear in that amount with other facts fixed. Age 59½ and every covered exception zero it.
  • Published ceilings from 2002 are nondecreasing. Projected ceilings are nondecreasing on any nonnegative index path. Projections round from unrounded statutory bases.
  • SSI countable value lies in [0, balance]. A higher deductible penalty never raises it, and ordinary tax never lowers it.
  • The assessment function and its numeric helper agree (a differential test).
  • Federal COLAs are nonnegative and bounded by the rounded CPI growth rate. Proration never exceeds the full COLA. An annuity adjustment never lowers a payment.

Open questions (named options, no defaults)

  1. The 1959 cohort. Two applicable-age clauses overlap for this cohort, and the final regulation still reserves the question. A call for the cohort must choose IRS_PROPOSED_73 (REG-103529-23) or STATUTORY_75.
  2. One joint-table cell (owner 97, spouse 20). TD 8987 and the 2020 CFR print 60.3; IRS Publication 590 (2013) prints 63.0. A call that reaches the cell must choose CFR_LITERAL_60_3 or IRS_PUBLICATION_63_0. Under the literal CFR reading, monotonicity in owner age fails at that cell only. The tests keep the minimal counterexample.
  3. The 72(t) tax in SSI. SSI2 takes up whether SSA deducts the additional tax as a withdrawal penalty, and every caller names one reading through AdditionalTaxTreatment, with no default.
  4. State, local and private DB COLAs. pension_cola.py names STATE_LOCAL_CPI_CAPPED_AT_THREE_PERCENT and PRIVATE_FIXED_NOMINAL as proposals that need a later model registration, with no default, and encodes no state plan's law.

Tests

On the host at 6ee80a19, with two workers, WP2's seven modules and the birth-evidence module ran 424 tests: 423 passed and 1 failed. test_post_review_exclusions_are_unreachable_from_birth_evidence fails on its track-U assertion, because this sparse clone leaves out a track-U module, and WP2's block runs before that line and passes. CI's four test shards ran the full tree at 13c1428e, and none failed. black -l 79 and ruff check pass on WP2's twelve modules and tests.

Tier Added
unit 170
artifact 241

b19e7572 recounts the tier files on master 4ab97297: master's 36,123 tests plus WP2's 170 unit and 241 artifact make unit 26,824, artifact 7,382, integration_psid 1,471, reproduction_legacy 520 and oracle_policyengine 337, 36,534 in all.

Every test uses invented owners, balances, dates and CPI paths. The IRS, POMS and plan worked examples keep their published figures. The tests read no survey microdata.

Review and status

  • Round 1 (Subfleet 20261024-110416-ir-wp2-review-r1) returned REVISE with one medium finding: the declared section 415(d) high-water invariant had no generated property test. d8f63b4e adds one for all four ceiling kinds, plus a test showing that it rejects round 1's mutation. It also disables Hypothesis deadlines, as the repository does by convention, because they caused six first-call timing flakes.

  • Round 2 (20261024-131057-ir-wp2-review-r2): APPROVE, no findings.

  • 72b6965a merges master. 3a07cc1e lists the package's six files among the birth-evidence check's post-review exclusions, in that test's expected tuple and in its reachability test, which asserts the historical reducer cannot import them.

  • Round 3 (20261024-141134-ir-wp2-review-r3): APPROVE at 3a07cc1e, no findings. All 528 in-memory import mutations trip the new reachability assertion.

  • Merge turn on 4ab97297 (6ee80a19, b19e7572, 13c1428e, 184d1081). 6ee80a19 merges master, whose later milestones add their own lines where WP2 adds its six: the birth-evidence exclusions, their expected tuple and the reachability test. The merge keeps master's blocks and puts WP2's after them, so the exclusions and the expected tuple list the same 229 paths in the same order. Master's D4 block already lists income_resources/__init__.py, with WP2's blob, so WP2's blocks drop their copy of that line. 13c1428e corrects the RMD research note's citation for the Roth IRA exemption from 408A(c)(5) to 408A(c)(4), where the USCODE-2024 capture prints it; no manifest lists the note and none of WP2's test modules reads it. 184d1081 makes the same correction in a docstring of test_retirement_rule_composition.py, a module that reads no __doc__. Pub. L. 116-94 redesignated (c)(5) as (c)(4) in 2019, so rmd/tables2003_pdf.txt, which prints the 2002 rule, keeps (c)(5). A search of WP2's 29 modules, tests, parameter files, notes, manifests, scripts and text extractions finds the old number nowhere else.

    From 3a07cc1e to 184d1081, 994 paths changed: the two tier files, the two birth-evidence files, the research note, the composition test and 988 other paths that master changed since fe5ab5ab. All 988 match master's blobs. Each birth-evidence file holds master's text plus the lines WP2 inserted from fe5ab5ab to 3a07cc1e, less the shared __init__ line, and running the resolution script on a fresh git merge-tree reproduces both byte for byte.

  • No review round covers 6ee80a19, b19e7572, 13c1428e or 184d1081. The dynamics coordinator takes the proof above in place of a delta review of the merge. It read both citation commits and takes a note in the merge request in place of a delta on them.

  • Correct coverage-registry statements, worker prose and the B0.2 sentence #598 landed on master at 7599ea73 after that merge. It changes five paths this PR doesn't change and adds no test, and a git merge-tree of this head onto it exits 0, so this branch needs no further merge for it. If a PR with tests or new exclusions lands first, this branch merges master and recounts the tiers.

  • Still to do: RuleSpec. This layer serves as the statutory reference, following Track B insured status: quarters of coverage; fully, currently and disability insured #562's precedent. The builder has drafted one axiom-corpus ingestion issue and four dispatch-ready rulespec-us issues, and an independent review of the drafts waits for a lane. The builder files them after that review and links them here, and the coordinator merges this PR only after that.

axiom: n/a: microcosm-dynamics statutory reference, not a PolicyEngine country model

🤖 Generated with Claude Code

MaxGhenis and others added 3 commits October 24, 2026 10:48
Captures, manifests and research notes for required minimum
distributions, contribution limits, the early-distribution tax, the SSI
retirement-fund valuation and federal and proposed DB cost-of-living
rules, with the scripts that captured and parsed them. A folder-level
.gitattributes stores the captures byte-exact, so their SHA-256s hold
on checkout.

Built by Subfleet job 20261024-073637-ir-wp2-build (Codex lane);
committed by the lane's main session.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
RMD ages and divisor tables across regimes, Roth exemptions and
waivers; the 72(t) additional tax and its exceptions; contribution
ceilings 2002-2026 with statutory indexing; POMS SI 01120.210 fund
valuation with both 72(t) readings as named options; CSRS and FERS
COLAs, and named-option DB COLA assumptions with no default.

Built by Subfleet job 20261024-073637-ir-wp2-build (Codex lane).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Built by Subfleet job 20261024-073637-ir-wp2-build (Codex lane).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vercel

vercel Bot commented Oct 24, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
social-security-model Ready Ready Preview Nov 6, 2026 3:41pm UTC

Request Review

Review r1's medium finding: the section 415(d) high-water invariant had
only a fixed elective-deferral example, and the projection property
allowed no deflation. A Hypothesis property now generates Q3 paths with a
post-anchor peak, declines and a recovery for elective deferrals, the
employer catch-up, the ages 60-63 catch-up and annual additions. It
checks that no ceiling falls below one already reached and that each
equals an independent statutory computation. The review's minimized
annual-additions path is an explicit example, and a further test shows
that the property rejects a projector that forgets the earlier peak.

Property tests in tests/income_resources also take
@settings(deadline=None), the repository's convention: the review's six
failures were first-call deadline overruns while parameters load.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 2 commits October 24, 2026 14:08
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The birth-evidence identity check seals every source under
src/populace_dynamics except listed opt-in modules, so the new
income_resources package broke it. List its six files as post-review
exclusions, in the test's expected tuple, and in the reachability test,
which asserts the historical reducer cannot import them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
MaxGhenis and others added 3 commits November 6, 2026 09:04
Master's SM1, D1a, D4, SSI0, CE1 and D2 blocks sit in the birth-evidence
exclusions, their expected tuple and the reachability test, where WP2 adds
its own. Keep master's blocks and put WP2's after them in all three places.
Master's D4 block already lists income_resources/__init__.py, with the same
blob as WP2's, so WP2's blocks drop their copy of that line. The merge takes
master's tier files, and the next commit recounts them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Master at 4ab9729 holds unit 26,654, artifact 7,141, integration_psid
1,471, reproduction_legacy 520, oracle_policyengine 337, total 36,123.
WP2 adds 170 unit and 241 artifact tests, so the files now read unit
26,824, artifact 7,382, total 36,534. CI's tier policy test checks the
count on the full collection.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The USCODE-2024 capture in the early-distribution folder prints the
exemption from the lifetime required distribution rules at 26 U.S.C.
408A(c)(4), and the research note cited (c)(5). No manifest lists the note
and no test reads it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Pub. L. 116-94 redesignated 408A(c)(5) as (c)(4) in 2019, as the amendment notes in WP2's USCODE-2024 capture print. 13c1428 corrected the RMD research note, and this commit corrects a test docstring that still used the old paragraph. That module reads no __doc__, and its three tests pass.

rmd/tables2003_pdf.txt keeps (c)(5): it prints the words of the final rule the Federal Register published on April 17, 2002.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>

This branch was successfully deployed

1 active deployment
Preview — 184d1081 Deployed Nov 6, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant