Skip to content

Count railroad retirement annuities as SSI unearned income - #10534

Open
hua7450 wants to merge 3 commits into
mainfrom
ssi-railroad-retirement-income
Open

hua7450 wants to merge 3 commits into
mainfrom
ssi-railroad-retirement-income

Conversation

@hua7450

@hua7450 hua7450 commented Oct 29, 2026 •

Copy link
Copy Markdown
Collaborator

Fixes #10533

What and why

The federal Supplemental Security Income (SSI) unearned income list, gov.ssa.ssi.income.sources.unearned, omitted railroad_benefits. Neither social_security nor pension_income includes it, so the model paid SSI as if a railroad annuitant had no income. A 70-year-old whose only income was a $37,000 Railroad Retirement annuity received the full 2026 federal benefit of $11,928 (994 x 12). Missouri LIHEAP PR 10438 exposed the gap.

This PR adds railroad_benefits to paragraph (a) of the list ("annuities, pensions, and other periodic payments"):

  • 42 U.S.C. 1382a(a)(2)(B) defines unearned income to include "railroad retirement annuities and pensions".
  • 20 CFR 416.1121(a) lists "railroad retirement annuities" among periodic payments.
  • POMS SI 00830.225 B.1: "Payments made by RRB are unearned income." B.3 charges the annuity "before the collection of any obligations of the annuitant", which matches the gross railroad_benefits input.

The parameter now cites the statute and the POMS section next to the existing 20 CFR 416.1121 reference. The railroad_benefits input gets a corrected label ("Railroad Retirement Board benefits"), a USD unit, and documentation stating that it holds gross annual payments before Medicare premiums or other withholding, with Railroad Unemployment Insurance Act unemployment and sickness benefits reported in unemployment_compensation and disability_benefits. ssi_unearned_income gets its correct label ("SSI unearned income").

Who this affects

Every variable that reads ssi_unearned_income or the source list now counts railroad annuities:

  • Federal SSI: ssi_countable_income, uncapped_ssi, ssi, and spousal and parental deeming.
  • The Medicare Savings Program (msp_countable_income), which uses SSI income methodology, and through it medicare_part_b_premium.
  • Medicaid optional senior or disabled deeming from an ineligible spouse or parent.
  • State supplements and related programs that read the SSI income definition: CA WDP, CO OAP deeming of a dependent child's income, CO state supplement, CT SSP, MN MSA, NC SA, NH SSP, OH RSS, OK SSP, OR SSP, SD SSP, VA AG, VT AABD, WI CTS and WY SSP eligibility.
  • Colorado's Old Age Pension (OAP) list of the client's and spouse's own unearned income now includes railroad_benefits. 9 CCR 2503-5 3.520.785(B)(5) counts "Railroad retirement payments, such as sick pay, annuities, pensions, and unemployment insurance benefits, which are paid by the Railroad Retirement Board". Without this, the lower SSI would raise the OAP grant, for example from $2,268 to $6,216 a year for a 70-year-old with $6,000 of railroad income.
  • Programs gated on SSI receipt (ssi > 0) and programs that count ssi as income see lower SSI for aged, blind or disabled railroad annuitants.

Household net income, SNAP, HUD annual income and other program income lists (34 lists in all) count ssi and social_security but not railroad_benefits. For an SSI-eligible railroad annuitant, the higher SSI used to stand in for the missing annuity. With SSI now correct, the annuity drops out of those totals: a 70-year-old Texan with a $37,000 annuity shows $4,251 of household net income and the maximum SNAP benefit. The omission predates this PR for annuitants who are not SSI-eligible. A NOTE in the SSI list records it, and #10551 tracks adding railroad_benefits to those lists.

California's 250% Working Disabled Program exempts disability income until it converts to retirement income. It now counts every railroad payment, including Railroad Retirement disability annuities, because the input does not separate them from age, spouse or survivor annuities. Before this PR, the program counted no railroad payment at all. A NOTE in the program's exempt-income list records the limitation, and #10540 tracks the fix.

No microdata pipeline in policyengine-us-data or Microcosm populates railroad_benefits, so the input stays 0 in microsimulation and the change affects household calculations only. I did not run a microsimulation.

Test plan

New cases, with values hand-derived from the 2026 federal benefit rate of $994 a month and the $20 general income exclusion:

File Case Railroad annuity Countable income SSI
ssa/ssi/income/ssi_unearned_income.yaml Railroad annuities count as unearned income $10,800 (unearned income 10,800) n/a
ssa/ssi/ssi.yaml Aged divorced spouse, SSI reduced $900/month, near the RRB's September 2025 average divorced spouse annuity of $893.35 (900 - 20) x 12 = 10,560 11,928 - 10,560 = 1,368
ssa/ssi/ssi.yaml Aged retiree with the average employee annuity, SSI zero $3,636/month (RRB News Release 25-8, January 2026 average) (3,636 - 20) x 12 = 43,392 11,928 - 43,392 < 0, so 0
states/co/ssa/oap/co_oap.yaml Case 36, Colorado OAP counts a railroad annuity $900/month OAP: 900 - 20 = 880 a month (10,560 a year) SSI 1,368; OAP grant 1,032 - 114 - 880 = 38, raised to the $189 floor: 2,268

Existing tests: 37 YAML files set railroad_benefits. For each case, I checked whether a person with railroad income is aged, blind or disabled and whether the asserted outputs depend on SSI or the Medicare Savings Program. No expected value changes:

  • Every aged railroad recipient whose outputs read SSI already pins ssi as an input: AL, AR, AZ, GA, MS and SD LIHEAP, NH Fuel Assistance, and the PA property tax or rent rebate.
  • AZ and AR LIHEAP net the Part B premium, and they pin medicare_part_b_premium as an input.
  • SD and GA LIHEAP net the premium only from social_security, which those cases leave at 0.
  • NH Fuel Assistance Cases 28 and 32 compute the premium. With railroad counted, the $18,000 annuity gives MSP countable income of 1,500 - 20 = 1,480 a month, within the qualifying individual limit of about 1,848 a month. The $20,000 of assets still exceeds the $9,950 resource limit, so the enrollee still pays the $2,434.80 premium. This PR updates the Case 28 comment, which said railroad income was not an SSI unearned source, and corrects the 2027 limit in four comments in that file from about 1,838 to about 1,848 a month (16,426.79 x 1.35 / 12).
  • The other cases involve people under 65 with no disability input, or outputs that do not depend on SSI (Indiana civil service annuity deduction).
  • No partner contract test under policyengine_us/tests/policy/baseline/partners/ sets railroad_benefits.

Merge order: #10382 adds a 2004-07-01 version of the SSI unearned list without railroad_benefits. Whichever of the two PRs merges second must add railroad_benefits to that version; the 2026 SSI tests here fail if it is missed.

  • For policy changes: YAML tests whose expected values come from an external source (42 U.S.C. 1382a(a)(2)(B), 20 CFR 416.1121(a), the 2026 SSI federal benefit rate, RRB average annuities)
  • Source conflict: not applicable. The statute, regulation and POMS agree.
  • make format
  • Changelog fragment in changelog.d/
  • CI passes

axiom: TheAxiomFoundation/rulespec-us#1967 queued

🤖 Generated with Claude Code

42 U.S.C. 1382a(a)(2)(B) and 20 CFR 416.1121(a) count railroad
retirement annuities as unearned income, but the SSI unearned income
list omitted railroad_benefits, so an aged railroad annuitant received
the full federal benefit. Add it to paragraph (a) with statute and
POMS SI 00830.225 references, add SSI tests for a reduced and a zero
benefit, and update an NH fuel assistance comment that described the
gap.

Fixes #10533

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Relabel railroad_benefits, give it a USD unit and document that it holds
gross Railroad Retirement Board payments before Medicare premiums or other
withholding. Note in the California 250% Working Disabled Program exempt
list that railroad disability annuities cannot be separated from other
railroad payments, so they now count (follow-up issue 10540). Correct the
2027 Medicare Savings Program limit in the NH fuel assistance test comments
to about 1,848 a month.

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

@PavelMakarchuk PavelMakarchuk left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Program Review

PR #10534, "Count railroad retirement annuities as SSI unearned income" (hua7450). Round 1.

IDs: C = critical, SA = should address, G = suggestion.

Source Documents

  • Reviewed head SHA: 8e96a5b
  • Mode: full (round 1)
  • 42 U.S.C. 1382a(a)(2)(B): unearned income includes "railroad retirement annuities and pensions". The #a_2_B anchor resolves.
  • 20 CFR 416.1121(a): periodic payments include "railroad retirement annuities".
  • 20 CFR 416.1123(b)(2): amounts withheld "to make any other payment such as payment of your Medicare premiums" still count. So SSA counts the gross amount, as for Social Security.
  • POMS SI 00830.225 (TN 237, effective 09/10/2024): B.1 "Payments made by RRB are unearned income." B.2 counts the SMI premium deducted. B.3 counts the annuity "before the collection of any obligations". The section makes no split between Tier I and Tier II.
  • 20 CFR 416.1124 and the Subpart K appendix: I found no exclusion for any RRB payment. That includes the supplemental annuity and the vested dual benefit.
  • LA County DPSS, 250% WDP: "Federal, State and private disability income" is exempt. Only SSDI stays exempt after it converts to retirement. This matches the PR's WDP NOTE.
  • 9 CCR 2503-5 3.520.785(B)(5), file page 69: Colorado OAP counts "Railroad retirement payments, such as sick pay, annuities, pensions, and unemployment insurance benefits, which are paid by the Railroad Retirement Board".
  • 7 CFR 273.9(b)(2)(ii): SNAP counts "Annuities; pensions; retirement ... benefits". 24 CFR 5.609(a)(1): HUD counts "All amounts, not specifically excluded".
  • RRB News Release 25-8: the average regular employee annuity rises "to $3,636" in January 2026. This confirms the test comment. I could not open the RRB Quarterly Benefit Statistics page (404), so I did not check the $893.35 divorced spouse figure. The test value does not depend on it.
  • policyengine-us-data datasets/cps/cps.py and datasets/puf/puf.py: neither has a "railroad" term. CPS pensions (PNSN_VAL + ANN_VAL) go to the pension variables. So railroad_benefits is 0 in microdata and does not overlap pension_income. In the model, social_security and pension_income do not add railroad_benefits, so the SSI list does not count the same dollars twice.

Branch Status

  • The PR is 2 commits ahead and 2 behind main (73f6376). git merge-tree against main is clean.
  • CI passed: 33 of 33 checks. The last check finished at 12:08pm EDT on October 29. There are no reviews or comments yet.
  • Main gained Iowa State Supplementary Assistance (#8017) after the PR base. It reads ssi_unearned_income and ssi_countable_income. On a scratch tree of main plus this PR, the Iowa hhs folder, the SSI folder and every railroad test file pass (1,340 passed).
  • #10532 (farm and partnership SSI earnings): a text conflict in tests/.../gov/ssa/ssi/ssi.yaml, because both PRs append cases after the last case. Keep both blocks. The code does not interact (#10532 changes the earned side).
  • #10382 (SSI interest and dividend exclusion): adds a second dated list, 2004-07-01, to unearned.yaml, and that list has no railroad_benefits. In my three-way merge of unearned.yaml, the value lists merge cleanly and only the reference block conflicts. The risk is that whoever merges second resolves only the reference conflict. Then the 2005+ list silently drops railroad_benefits. This PR's 2026 tests would catch that in CI. The fix is to add railroad_benefits to the 2004-07-01 list as part of the second merge.
  • #10332: it has no text conflict with this PR's files. Its conflicts with main are in its own files.

Findings

Critical

None.

Should address

SA1. Colorado OAP now pays more to a railroad annuitant, because its own income list omits railroad_benefits.

  • Where: policyengine_us/parameters/gov/states/co/ssa/oap/income/sources/unearned.yaml (not in the diff). It is read by co_oap_unearned_income and then co_oap_own_countable_income.
  • Evidence: co_oap pays the grant standard less SSI and co_oap_countable_income ($1,032 a month in 2026). The OAP list has social_security and pension_income but not railroad_benefits, against 3.520.785(B)(5). Before this PR, the overstated SSI ($994 a month) stood in for the annuity. Now SSI correctly falls, and OAP adds nothing for the annuity. Take a 70-year-old in Colorado with $6,000 a year of railroad income in 2026. co_oap goes from $2,268 to $6,216. The same $6,000 as Social Security gives $2,268. The PR body lists "CO OAP deeming" as a program that now counts railroad annuities. That is true for a dependent child's income in co_oap_deemed_income, which reads the SSI list, but it is not true for the client's or the spouse's own income.
  • Fix: add railroad_benefits to the OAP unearned list. Cite 3.520.785(B)(5) at #page=69. Add a co_oap case: age 70, railroad_benefits: 6_000, expected co_oap: 2_268.

Suggestions

G1. Say where railroad unemployment and sickness benefits go.

  • Where: policyengine_us/variables/household/income/person/retirement/railroad_benefits.py.
  • Evidence: the new label is "Railroad Retirement Board benefits". POMS SI 00830.225 A.4 says RRB also pays unemployment, sickness and strike benefits. The documentation lists only annuities, and NH FAP and AR LIHEAP net the Part B premium from this input, which only fits annuities.
  • Fix: add a sentence that says Railroad Unemployment Insurance Act benefits go in unemployment_compensation (or in whichever input you intend), not here.

G2. Fix the label of the variable that the new test covers.

  • Where: policyengine_us/variables/gov/ssa/ssi/eligibility/income/ssi_unearned_income.py, label = "SSI earned income". This is older than the PR.
  • Fix: change it to "SSI unearned income".

G3. For an aged, blind or disabled railroad annuitant, the annuity now drops out of household net income, SNAP income and HUD income. The PR body does not say so.

  • Where: gov/household/household_benefits.yaml and spm_unit_benefits (neither has railroad_benefits, and neither does market_income_sources.yaml), gov/usda/snap/income/sources/unearned.yaml and gov/hud/annual_income/sources/unearned.yaml. My scan found 34 parameter lists that count ssi and social_security but not railroad_benefits.

  • Evidence: these omissions are older than this PR. But the inflated SSI used to stand in for the missing annuity, and this PR removes that. I probed Texas, age 70, rent $9,600, 2026:

    Case SSI SNAP hud_annual_income household_net_income
    $6,000 railroad, before 11,928 2,244 11,928 14,823
    $6,000 railroad, after 6,168 3,600 6,168 10,419
    $6,000 Social Security 6,168 2,136 12,168 14,955
    $37,000 railroad, before 11,928 2,244 n/a 14,823
    $37,000 railroad, after 0 3,600 n/a 4,251
    $37,000 Social Security 0 0 n/a 37,000

    The PR body's own example, a $37,000 annuitant, now shows $4,251 of net income and the maximum SNAP benefit. 7 CFR 273.9(b)(2)(ii) and 24 CFR 5.609(a)(1) count the annuity.

  • Why a suggestion: the lists are older than this PR and are not in its files, and no dataset fills railroad_benefits, so the microsimulation does not change. A tracked follow-up is enough for this PR.

  • Fix: open a follow-up issue that lists the 34 lists. Link it from a NOTE in unearned.yaml, and state the net income and SNAP effect in "Who this affects". If you prefer to fix it here, add railroad_benefits next to social_security in household_benefits and spm_unit_benefits, and add it to the SNAP and HUD unearned lists, with one test each.

Downstream effects

These are probe values for 2026, age 70, $6,000 a year of railroad income, before and after the PR. In every output except co_oap, I got the same result when I entered the same dollars as social_security_retirement. So the railroad annuity now follows the model's existing Social Security path.

  • Federal SSI: ssi goes from 11,928 to 6,168 ((500 − 20) × 12 = 5,760 of countable income). This is correct.
  • Spousal deeming: take a 70-year-old with a 60-year-old spouse who has $30,000 of railroad income. ssi_unearned_income_deemed_from_ineligible_spouse goes from 0 to 30,000, and ssi goes from 11,928 to 0. This is correct (20 CFR 416.1161).
  • MSP: msp_countable_income equals SSI countable income. At $37,000, msp_eligible changes from true to false and medicare_part_b_premium from 0 to 2,434.80. This is correct.
  • State supplements: CT SSP countable income +240, NH SSP 324 → 84, MN MSA 972 → 0, SD SSP 180 → 0, WY SSP 240 → 0, OR SSP countable income +240. CA SSP ($4,425.72), OK SSP ($480), VT AABD ($668.16) and WI SSP ($1,072.40) do not change. Each result equals the Social Security result. I did not re-audit those programs' own rules.
  • CO OAP: 2,268 → 6,216. This is wrong, see SA1.
  • CA WDP: for a disabled worker aged 40 with $20,000 of earnings and $12,000 of railroad income, ca_wdp_countable_income goes from 9,490 to 21,370 and the person stays eligible. The NOTE and #10540 document the over-count of disability annuities. LA County supports the NOTE. A documented limitation with a tracked issue is acceptable.
  • SNAP, HUD and household net income: these go the wrong way for SSI-eligible annuitants, see G3.
  • NH FAP: Cases 28 and 32 keep their expected values. The model's 2027 first-person FPG is 16,426.79, so the QI limit is 16,426.79 × 1.35 / 12 = 1,848.01 a month. MSP countable income is 1,480, and $20,000 of assets is above $9,950, so the premium of 2,434.80 stays. The old "about 1,838" was wrong, and the PR's 1,848 is right.
  • Partner contract tests: no file under policyengine_us/tests/policy/baseline/partners/ sets railroad_benefits. The input defaults to 0, so no partner output changes. I did not run them.
  • Microsimulation: none, because no dataset fills railroad_benefits.

Tests run

  • policyengine-core test policyengine_us/tests/policy/baseline/gov/ssa/ssi at head: 246 passed.
  • All 39 YAML files that set railroad_benefits (37 existing and 2 changed) at head: 652 passed.
  • Dependent folders at head: Medicare, Medicaid income, CA WDP, CO SSA, CT SSP, MN MSA, NC SA, NH SSP, OH RSS, OK SSP, OR SSP, SD SSP, VA AG, VT AABD, WI SSP, WY SSP and NH FAP. Result: 1,533 passed.
  • Scratch tree of main 73f6376 plus the PR patch: the IA hhs folder, the SSI folder and the railroad test files. Result: 1,340 passed.
  • Non-functional test check: I ran the head's ssi.yaml and ssi_unearned_income.yaml against the pre-PR package. The 3 new cases fail and the 7 old cases pass.
  • Hand checks: FBR $994 × 12 = 11,928, and the general exclusion is $20 (both read from the model).
    • Case 1: (900 − 20) × 12 = 10,560, and 11,928 − 10,560 = 1,368.
    • Case 2: 43,632 / 12 = 3,636; (3,636 − 20) × 12 = 43,392; 11,928 − 43,392 = −31,464, so SSI is 0.
    • Unearned case: 10,800 → 10,800.
    • All three match.
  • References: the three hrefs return 200 and support the rule. The USC anchor #a_2_B exists. The parameter's 1975-01-01 start is right, because the statute has always listed railroad annuities.

Verdict

REQUEST_CHANGES: the SSI change, the references and the tests are correct. But Colorado OAP now overpays a railroad annuitant, because its own income list omits the annuity. Open item: SA1. G3 (net income, SNAP and HUD) needs only a tracked follow-up.

Add railroad_benefits to the Colorado Old Age Pension unearned income
list (9 CCR 2503-5 3.520.785(B)(5)) with a co_oap test, so the lower SSI
no longer raises the OAP grant. Document where Railroad Unemployment
Insurance Act benefits are reported, fix the ssi_unearned_income label,
and note in the SSI list that household net income, SNAP, HUD and other
program lists do not yet count railroad_benefits (issue 10551).

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

hua7450 commented Oct 30, 2026

Copy link
Copy Markdown
Collaborator Author

Thanks, Pavel. Round 1 is addressed in c8a1d69:

  • SA1: fixed. Colorado OAP's unearned income list now includes railroad_benefits (9 CCR 2503-5 3.520.785(B)(5), physical page 69, inside the list's existing #page 68-72 reference). New co_oap.yaml Case 36 uses a 70-year-old with $900 a month, near the RRB's September 2025 average divorced spouse annuity of $893.35, rather than $6,000 a year, so the amount is sourced. SSI is 994 - 880 = 114 a month, OAP countable income 900 - 20 = 880, and the grant 1,032 - 114 - 880 = 38 is raised to the $189 floor: co_oap 2,268, the same as the Social Security case. Without the list change it would be 11,016.
  • G1: applied. The railroad_benefits documentation now says Railroad Unemployment Insurance Act unemployment benefits go in unemployment_compensation and sickness benefits in disability_benefits.
  • G2: applied. ssi_unearned_income is labelled "SSI unearned income".
  • G3: tracked in Count railroad_benefits where Social Security is counted: household net income, SNAP, HUD and program income lists #10551, which lists the 34 lists that count ssi and social_security but not railroad_benefits, plus 48 that count only social_security. A NOTE in the SSI unearned list links it, and "Who this affects" now states the net income and SNAP effect with your $37,000 example. I kept the fix out of this PR because each program list needs its own income-rule check.
  • 10382 merge order: noted in the PR body. Whichever of 10382 and this PR merges second must add railroad_benefits to the 2004-07-01 list; the 2026 SSI tests here catch it if missed.

This branch has not been deployed

No deployments
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.

SSI does not count railroad retirement annuities as unearned income

2 participants