diff --git a/changelog.d/state-benefit-railroad.fixed.md b/changelog.d/state-benefit-railroad.fixed.md new file mode 100644 index 00000000000..0e908abdc95 --- /dev/null +++ b/changelog.d/state-benefit-railroad.fixed.md @@ -0,0 +1 @@ +Count Railroad Retirement Act benefits as income in the state and county benefit programs whose rules name them, count pensions, retirement benefits or federal benefits, or count all income they do not exclude. diff --git a/policyengine_us/parameters/gov/local/ca/ala/general_assistance/countable_income/sources.yaml b/policyengine_us/parameters/gov/local/ca/ala/general_assistance/countable_income/sources.yaml index 9cb1d31db44..a6672e3f574 100644 --- a/policyengine_us/parameters/gov/local/ca/ala/general_assistance/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/local/ca/ala/general_assistance/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - sstb_self_employment_income - social_security + - railroad_benefits - total_unemployment_compensation - veterans_benefits diff --git a/policyengine_us/parameters/gov/local/ca/la/dwp/ez_save/income_sources.yaml b/policyengine_us/parameters/gov/local/ca/la/dwp/ez_save/income_sources.yaml index c17fc01a862..0df5a6859e5 100644 --- a/policyengine_us/parameters/gov/local/ca/la/dwp/ez_save/income_sources.yaml +++ b/policyengine_us/parameters/gov/local/ca/la/dwp/ez_save/income_sources.yaml @@ -15,6 +15,7 @@ values: - child_support_received - alimony_income - social_security + - railroad_benefits - ssi - tanf - snap diff --git a/policyengine_us/parameters/gov/local/ca/la/general_relief/income_sources.yaml b/policyengine_us/parameters/gov/local/ca/la/general_relief/income_sources.yaml index 9e7d684cd95..a2ed9242991 100644 --- a/policyengine_us/parameters/gov/local/ca/la/general_relief/income_sources.yaml +++ b/policyengine_us/parameters/gov/local/ca/la/general_relief/income_sources.yaml @@ -12,6 +12,11 @@ values: - sstb_self_employment_income - ssi - social_security + # GR policy 44-101 deducts "all net income to the General Relief (GR) + # household" from the grant, and its current income takes in "the cash + # (including loans), food, housing, and other items that are received by + # and available to the GR household", so railroad benefits count. + - railroad_benefits - tanf metadata: unit: list @@ -20,3 +25,5 @@ metadata: reference: - title: ABP 392 General Relief Information Sheet Form rev 07-2021 href: https://drive.google.com/file/d/1Oc7UuRFxJj-eDwTeox92PtmRVGnG9RjW/view?usp=sharing + - title: DPSS ePolicy, General Relief 44-101, Income + href: https://my.dpss.lacounty.gov/public/en/home/epolicy/program/gr/income/income.html diff --git a/policyengine_us/parameters/gov/local/ca/oc/general_relief/income/sources/unearned.yaml b/policyengine_us/parameters/gov/local/ca/oc/general_relief/income/sources/unearned.yaml index e5f2a7cf212..cf630f83495 100644 --- a/policyengine_us/parameters/gov/local/ca/oc/general_relief/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/local/ca/oc/general_relief/income/sources/unearned.yaml @@ -5,6 +5,7 @@ values: - interest_income - dividend_income - social_security + - railroad_benefits - total_unemployment_compensation - ca_state_disability_insurance - veterans_benefits diff --git a/policyengine_us/parameters/gov/local/ca/riv/cap/liheap/countable_income/sources.yaml b/policyengine_us/parameters/gov/local/ca/riv/cap/liheap/countable_income/sources.yaml index 2d5bd2128bc..2f7fc98d12d 100644 --- a/policyengine_us/parameters/gov/local/ca/riv/cap/liheap/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/local/ca/riv/cap/liheap/countable_income/sources.yaml @@ -9,6 +9,7 @@ values: - pension_income - total_unemployment_compensation - social_security + - railroad_benefits - ssi - ca_tanf - ca_state_supplement diff --git a/policyengine_us/parameters/gov/local/ca/riv/cap/share/countable_income/sources.yaml b/policyengine_us/parameters/gov/local/ca/riv/cap/share/countable_income/sources.yaml index f08e2895b34..29fee9e6996 100644 --- a/policyengine_us/parameters/gov/local/ca/riv/cap/share/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/local/ca/riv/cap/share/countable_income/sources.yaml @@ -9,6 +9,7 @@ values: - pension_income - total_unemployment_compensation - social_security + - railroad_benefits - ssi - ca_tanf diff --git a/policyengine_us/parameters/gov/local/ca/smc/general_assistance/income/sources.yaml b/policyengine_us/parameters/gov/local/ca/smc/general_assistance/income/sources.yaml index 19555039689..756d23e4166 100644 --- a/policyengine_us/parameters/gov/local/ca/smc/general_assistance/income/sources.yaml +++ b/policyengine_us/parameters/gov/local/ca/smc/general_assistance/income/sources.yaml @@ -9,6 +9,7 @@ values: - disability_benefits - ca_state_disability_insurance - social_security + - railroad_benefits - veterans_benefits - pension_income - retirement_distributions diff --git a/policyengine_us/parameters/gov/states/ak/dpa/ccap/income/countable_income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/ak/dpa/ccap/income/countable_income/unearned_sources.yaml index 61649b1c299..d4143ee3c5f 100644 --- a/policyengine_us/parameters/gov/states/ak/dpa/ccap/income/countable_income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/ak/dpa/ccap/income/countable_income/unearned_sources.yaml @@ -10,6 +10,7 @@ values: - child_support_received - alimony_income - social_security + - railroad_benefits - ssi - total_unemployment_compensation - veterans_benefits diff --git a/policyengine_us/parameters/gov/states/al/dhr/ccsp/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/al/dhr/ccsp/income/countable_income/sources.yaml index 93102a0af62..b029d4aa5e4 100644 --- a/policyengine_us/parameters/gov/states/al/dhr/ccsp/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/al/dhr/ccsp/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - farm_operations_income - social_security + - railroad_benefits - pension_income - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/ar/ade/oec/sra/income/sources.yaml b/policyengine_us/parameters/gov/states/ar/ade/oec/sra/income/sources.yaml index c2180e44853..603210f150b 100644 --- a/policyengine_us/parameters/gov/states/ar/ade/oec/sra/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ar/ade/oec/sra/income/sources.yaml @@ -4,6 +4,7 @@ values: - employment_income - self_employment_income - social_security_retirement + - railroad_benefits - social_security_disability - social_security_survivors - ssi diff --git a/policyengine_us/parameters/gov/states/ca/cdss/child_care/income/sources.yaml b/policyengine_us/parameters/gov/states/ca/cdss/child_care/income/sources.yaml index 9810ac7b91f..9980f106625 100644 --- a/policyengine_us/parameters/gov/states/ca/cdss/child_care/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ca/cdss/child_care/income/sources.yaml @@ -14,6 +14,7 @@ values: - ca_state_disability_insurance # Social Security (excluding SSI) - social_security + - railroad_benefits # Support payments - child_support_received - alimony_income diff --git a/policyengine_us/parameters/gov/states/ca/cdss/tanf/cash/income/sources/other_unearned.yaml b/policyengine_us/parameters/gov/states/ca/cdss/tanf/cash/income/sources/other_unearned.yaml index 9d743b0eaca..a841d8eba9c 100644 --- a/policyengine_us/parameters/gov/states/ca/cdss/tanf/cash/income/sources/other_unearned.yaml +++ b/policyengine_us/parameters/gov/states/ca/cdss/tanf/cash/income/sources/other_unearned.yaml @@ -11,6 +11,7 @@ metadata: values: 2017-12-20: - social_security_retirement + - railroad_benefits - social_security_survivors - social_security_dependents - interest_income diff --git a/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml b/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml index ba8834c02ad..80fad20f407 100644 --- a/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/ca/cpuc/income_sources.yaml @@ -76,6 +76,7 @@ values: - wic # Social Security - social_security + - railroad_benefits # Pensions - pension_income - retirement_distributions @@ -118,6 +119,7 @@ values: - wic # Social Security - social_security + - railroad_benefits # Pensions - pension_income - retirement_distributions diff --git a/policyengine_us/parameters/gov/states/co/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/co/ccap/income/countable_income/sources.yaml index a8fa26f227f..1cb6ca8e8d7 100644 --- a/policyengine_us/parameters/gov/states/co/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/co/ccap/income/countable_income/sources.yaml @@ -8,6 +8,7 @@ values: - farm_operations_income # Unearned income - social_security + - railroad_benefits - pension_income - retirement_distributions - military_retirement_income diff --git a/policyengine_us/parameters/gov/states/co/cdhs/tanf/income/unearned.yaml b/policyengine_us/parameters/gov/states/co/cdhs/tanf/income/unearned.yaml index b027d8b0036..3dc845d35ed 100644 --- a/policyengine_us/parameters/gov/states/co/cdhs/tanf/income/unearned.yaml +++ b/policyengine_us/parameters/gov/states/co/cdhs/tanf/income/unearned.yaml @@ -11,6 +11,7 @@ values: - total_unemployment_compensation - gi_cash_assistance - social_security + - railroad_benefits - workers_compensation - disability_benefits # general_assistance is exempt under 9 CCR 2503-6, Section 3.605.4(A)(7) diff --git a/policyengine_us/parameters/gov/states/ct/oec/c4k/income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/ct/oec/c4k/income/unearned_sources.yaml index d23fc3299bc..9750d827536 100644 --- a/policyengine_us/parameters/gov/states/ct/oec/c4k/income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/ct/oec/c4k/income/unearned_sources.yaml @@ -2,6 +2,7 @@ description: Connecticut counts these unearned income sources as countable incom values: 2020-01-01: - social_security + - railroad_benefits - pension_income - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/dc/dhs/ccsp/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/dc/dhs/ccsp/income/countable_income/sources.yaml index a8b09a66f28..3b46915cf46 100644 --- a/policyengine_us/parameters/gov/states/dc/dhs/ccsp/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/dc/dhs/ccsp/income/countable_income/sources.yaml @@ -8,6 +8,10 @@ values: - interest_income - rental_income - pension_income + # Manual 2.5.2 counts railroad retirement. 2.5.3 exempts permanent disability + # payments, but railroad_benefits doesn't separate disability annuities, so + # the whole amount counts. + - railroad_benefits - veterans_benefits - military_retirement_income - alimony_income diff --git a/policyengine_us/parameters/gov/states/dc/dhs/tanf/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/dc/dhs/tanf/income/sources/unearned.yaml index 97c6a8abfc0..e005badfbb0 100644 --- a/policyengine_us/parameters/gov/states/dc/dhs/tanf/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/dc/dhs/tanf/income/sources/unearned.yaml @@ -14,6 +14,7 @@ values: # D.C. Code 4-205.05(4) counts District and federal public assistance payments - general_assistance - social_security + - railroad_benefits metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/de/dss/poc/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/de/dss/poc/income/countable_income/sources.yaml index 5bbedf4a4e6..efd73cae29d 100644 --- a/policyengine_us/parameters/gov/states/de/dss/poc/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/de/dss/poc/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - sstb_self_employment_income - social_security_retirement + - railroad_benefits - social_security_disability - social_security_survivors - ssi diff --git a/policyengine_us/parameters/gov/states/fl/doe/sr/income/sources.yaml b/policyengine_us/parameters/gov/states/fl/doe/sr/income/sources.yaml index 4e98aaf153a..d3eccb3057e 100644 --- a/policyengine_us/parameters/gov/states/fl/doe/sr/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/fl/doe/sr/income/sources.yaml @@ -12,6 +12,7 @@ values: - farm_income - tip_income - social_security_retirement + - railroad_benefits - social_security_disability - social_security_survivors - ssi diff --git a/policyengine_us/parameters/gov/states/ga/decal/caps/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/ga/decal/caps/income/countable_income/sources.yaml index 8eaf3acc5c7..d0f7c263b8a 100644 --- a/policyengine_us/parameters/gov/states/ga/decal/caps/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ga/decal/caps/income/countable_income/sources.yaml @@ -13,6 +13,10 @@ values: - dividend_income - pension_income - social_security_retirement + # Section 8.4.1 counts retirement and pension income. Section 8.4.2 excludes + # only disability and survivors benefits from the Social Security + # Administration, and the Railroad Retirement Board pays these benefits. + - railroad_benefits - military_basic_pay # The following are counted per CAPS Policy Manual §8.4 but have # no matching variables: diff --git a/policyengine_us/parameters/gov/states/hi/bessd/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/hi/bessd/ccap/income/countable_income/sources.yaml index 946715f50ce..b2b2a15c862 100644 --- a/policyengine_us/parameters/gov/states/hi/bessd/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/hi/bessd/ccap/income/countable_income/sources.yaml @@ -7,6 +7,9 @@ values: - social_security_retirement - social_security_disability - social_security_survivors + # HAR 17-798.3-10(b)(2)(A) counts railroad retirement insurance checks + # from the U.S. government, prior to deductions for medical insurance. + - railroad_benefits # Recurring SSI is countable; HAR 17-798.2-11(5)(B) excludes only # retroactive lump-sum SSI, not recurring monthly SSI payments. - ssi @@ -32,7 +35,6 @@ values: - hi_ccap_tanf_income # The following are counted per HAR 17-798.2-10(b) but have no # matching PolicyEngine variables: - # - railroad retirement (item (2)(A); captured via social_security_*) # - temporary disability insurance (item (4)(B)) # - income from estates or trust funds (item (15)) # - royalties (item (17)) diff --git a/policyengine_us/parameters/gov/states/id/dhw/iccp/income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/id/dhw/iccp/income/unearned_sources.yaml index 7880071102a..09d4c644681 100644 --- a/policyengine_us/parameters/gov/states/id/dhw/iccp/income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/id/dhw/iccp/income/unearned_sources.yaml @@ -3,6 +3,7 @@ values: 2022-03-17: # social_security already includes retirement, disability, survivors, and dependents components. - social_security + - railroad_benefits - ssi - pension_income - military_retirement_income diff --git a/policyengine_us/parameters/gov/states/il/dhs/aabd/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/il/dhs/aabd/income/sources/unearned.yaml index c6b56325250..83b5caa1c0a 100644 --- a/policyengine_us/parameters/gov/states/il/dhs/aabd/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/il/dhs/aabd/income/sources/unearned.yaml @@ -3,6 +3,7 @@ values: 2021-01-01: #- pension_income # exempt - social_security + - railroad_benefits - ssi - disability_benefits #- veterans_benefits # exempt diff --git a/policyengine_us/parameters/gov/states/il/dhs/tanf/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/il/dhs/tanf/income/sources/unearned.yaml index 6001fca05d5..73a97ca9b95 100644 --- a/policyengine_us/parameters/gov/states/il/dhs/tanf/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/il/dhs/tanf/income/sources/unearned.yaml @@ -2,6 +2,12 @@ description: Illinois counts these income sources as unearned income under the T values: 1997-07-01: - social_security + # 89 Ill. Adm. Code 112.100 counts unearned income the section doesn't exempt. + # 112.110(aa) disregards Railroad Retirement disability payments up to the SSI + # payment level, as it does Social Security disability. The model doesn't apply + # that disregard to either, and railroad_benefits doesn't separate disability + # annuities, so the whole amount counts. + - railroad_benefits - disability_benefits - workers_compensation - total_unemployment_compensation diff --git a/policyengine_us/parameters/gov/states/il/hfs/hbwd/eligibility/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/il/hfs/hbwd/eligibility/income/sources/unearned.yaml index c284e580cd6..4ab9f97f306 100644 --- a/policyengine_us/parameters/gov/states/il/hfs/hbwd/eligibility/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/il/hfs/hbwd/eligibility/income/sources/unearned.yaml @@ -2,6 +2,7 @@ description: Illinois counts these income sources as unearned income under the H values: 2022-04-01: - social_security + - railroad_benefits - disability_benefits - workers_compensation - total_unemployment_compensation diff --git a/policyengine_us/parameters/gov/states/il/isbe/income/countable_sources.yaml b/policyengine_us/parameters/gov/states/il/isbe/income/countable_sources.yaml index 1396bad9071..25161d98b13 100644 --- a/policyengine_us/parameters/gov/states/il/isbe/income/countable_sources.yaml +++ b/policyengine_us/parameters/gov/states/il/isbe/income/countable_sources.yaml @@ -11,6 +11,7 @@ values: - pension_income - retirement_distributions - social_security + - railroad_benefits - ssi - veterans_benefits - disability_benefits diff --git a/policyengine_us/parameters/gov/states/ks/dcf/ccap/income/countable_income/unearned.yaml b/policyengine_us/parameters/gov/states/ks/dcf/ccap/income/countable_income/unearned.yaml index b1ff628638e..6af0d2557fd 100644 --- a/policyengine_us/parameters/gov/states/ks/dcf/ccap/income/countable_income/unearned.yaml +++ b/policyengine_us/parameters/gov/states/ks/dcf/ccap/income/countable_income/unearned.yaml @@ -3,6 +3,7 @@ values: 2021-07-01: # Unearned income (KEESM 6200): benefits, pensions, settlements. - social_security + - railroad_benefits - total_unemployment_compensation - workers_compensation - pension_income diff --git a/policyengine_us/parameters/gov/states/ks/dcf/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/ks/dcf/liheap/income/sources/unearned.yaml index 743f086f04f..f50e17a6df7 100644 --- a/policyengine_us/parameters/gov/states/ks/dcf/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/ks/dcf/liheap/income/sources/unearned.yaml @@ -5,6 +5,7 @@ values: - strike_benefits # SSA benefits are counted gross, including the Medicare deduction. - social_security + - railroad_benefits - ssi # Military retirement income (retiree pay plus Survivor Benefit Plan # annuities) is a separate input from the pension inputs that diff --git a/policyengine_us/parameters/gov/states/ky/dcbs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/ky/dcbs/ccap/income/countable_income/sources.yaml index f1783127c9e..37f9b73c754 100644 --- a/policyengine_us/parameters/gov/states/ky/dcbs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ky/dcbs/ccap/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - farm_operations_income - social_security + - railroad_benefits - total_unemployment_compensation - workers_compensation - pension_income diff --git a/policyengine_us/parameters/gov/states/la/ldoe/ccap/income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/la/ldoe/ccap/income/unearned_sources.yaml index 668df3bbe46..c40dd237f57 100644 --- a/policyengine_us/parameters/gov/states/la/ldoe/ccap/income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/la/ldoe/ccap/income/unearned_sources.yaml @@ -5,6 +5,9 @@ values: - ssi - veterans_benefits - pension_income + # LAC 28:CLXV.509.A.3.b counts recurring "retirement benefits" (iv) and + # "disability benefits" (v), which railroad retirement annuities are. + - railroad_benefits - disability_benefits - total_unemployment_compensation - workers_compensation @@ -18,6 +21,9 @@ values: - ssi - veterans_benefits - pension_income + # LAC 28:CLXV.509.A.3.b counts recurring "retirement benefits" (iv) and + # "disability benefits" (v), which railroad retirement annuities are. + - railroad_benefits - disability_benefits - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/ma/dta/tcap/gross_income/unearned.yaml b/policyengine_us/parameters/gov/states/ma/dta/tcap/gross_income/unearned.yaml index 9ece76780b1..4954e9b3241 100644 --- a/policyengine_us/parameters/gov/states/ma/dta/tcap/gross_income/unearned.yaml +++ b/policyengine_us/parameters/gov/states/ma/dta/tcap/gross_income/unearned.yaml @@ -4,6 +4,7 @@ values: - dividend_income - interest_income - social_security + - railroad_benefits - total_unemployment_compensation - veterans_benefits - pension_income diff --git a/policyengine_us/parameters/gov/states/ma/eec/ccfa/income/countable_income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/ma/eec/ccfa/income/countable_income/sources/unearned.yaml index 1e8256e24b9..d282a883ba0 100644 --- a/policyengine_us/parameters/gov/states/ma/eec/ccfa/income/countable_income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/ma/eec/ccfa/income/countable_income/sources/unearned.yaml @@ -7,6 +7,7 @@ values: - interest_income - rental_income - pension_income + - railroad_benefits - veterans_benefits - military_retirement_income - alimony_income @@ -19,12 +20,18 @@ values: # (all types), SSI, child support, and veterans disability benefits, and # the countable list becomes exclusive; interest income is not on it. # CCFA mapping assumption: veterans_benefits contains disability payments. + # railroad_benefits counts as "Retirement income": the Railroad Retirement + # Board pays it under the Railroad Retirement Act, and the exclusion of + # "Social Security Income" names Social Security benefits. The input doesn't + # separate railroad disability and survivor annuities, which the list may + # not reach, so the model counts every railroad payment. 2023-10-01: - farm_rent_income - estate_income - dividend_income - rental_income - pension_income + - railroad_benefits - military_retirement_income - alimony_income - total_unemployment_compensation @@ -35,6 +42,7 @@ values: - dividend_income - rental_income - pension_income + - railroad_benefits - military_retirement_income - alimony_income - total_unemployment_compensation diff --git a/policyengine_us/parameters/gov/states/md/msde/ccs/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/md/msde/ccs/income/countable_income/sources.yaml index f6da5365f41..737d2579b0d 100644 --- a/policyengine_us/parameters/gov/states/md/msde/ccs/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/md/msde/ccs/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: # Note: self_employment_income is handled separately in the formula # with a 30% deduction per COMAR 13A.14.06.03F(8)(a)(i) - social_security + - railroad_benefits - pension_income - interest_income - dividend_income diff --git a/policyengine_us/parameters/gov/states/me/dhhs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/me/dhhs/ccap/income/countable_income/sources.yaml index 29dcb229d3f..a8556d1bd11 100644 --- a/policyengine_us/parameters/gov/states/me/dhhs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/me/dhhs/ccap/income/countable_income/sources.yaml @@ -9,6 +9,7 @@ values: - rental_income # Unearned income (Rule §2.C.4) - social_security + - railroad_benefits - pension_income - retirement_distributions - disability_benefits diff --git a/policyengine_us/parameters/gov/states/me/dhhs/tanf/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/me/dhhs/tanf/income/sources/unearned.yaml index 26af4399f38..722caefb0c2 100644 --- a/policyengine_us/parameters/gov/states/me/dhhs/tanf/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/me/dhhs/tanf/income/sources/unearned.yaml @@ -18,6 +18,7 @@ values: - total_unemployment_compensation - gi_cash_assistance - social_security + - railroad_benefits # Chapter III, Section B(2)(d) excludes General Assistance, so # general_assistance is not counted diff --git a/policyengine_us/parameters/gov/states/mi/mdhhs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/mi/mdhhs/ccap/income/countable_income/sources.yaml index 6a75be94507..7cd0944fc89 100644 --- a/policyengine_us/parameters/gov/states/mi/mdhhs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/mi/mdhhs/ccap/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - sstb_self_employment_income - social_security + - railroad_benefits # SSI (public assistance) is not counted: State Plan FFY 2025-2027 §2.2.4.c # leaves "public assistance" unchecked. SSI recipients are income-waived # anyway (see mi_ccap_income_waived), so this never affects the income test. diff --git a/policyengine_us/parameters/gov/states/mn/dcyf/ccap/income/countable_income/unearned.yaml b/policyengine_us/parameters/gov/states/mn/dcyf/ccap/income/countable_income/unearned.yaml index 11978348baa..466d85d6ffe 100644 --- a/policyengine_us/parameters/gov/states/mn/dcyf/ccap/income/countable_income/unearned.yaml +++ b/policyengine_us/parameters/gov/states/mn/dcyf/ccap/income/countable_income/unearned.yaml @@ -8,6 +8,11 @@ values: - dividend_income - interest_income - pension_income + # 256P.06, subd. 3(2)(ix) counts retirement benefits. Item (xvi), which the + # model applies to Social Security through rsdi_sources, doesn't name them. + # railroad_benefits doesn't separate disability annuities, so the whole + # amount counts. + - railroad_benefits - veterans_benefits # Military retirement pay and Survivor Benefit Plan annuities count as # retirement benefits under 256P.06, subd. 3(2)(ix), which has no income diff --git a/policyengine_us/parameters/gov/states/mn/dcyf/mfip/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/mn/dcyf/mfip/income/sources/unearned.yaml index 084172e982f..33118dd5864 100644 --- a/policyengine_us/parameters/gov/states/mn/dcyf/mfip/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/mn/dcyf/mfip/income/sources/unearned.yaml @@ -16,6 +16,9 @@ values: - workers_compensation - gi_cash_assistance - social_security + # 256P.06, subd. 3(2)(ix) counts retirement benefits. railroad_benefits doesn't + # separate disability annuities, so the whole amount counts. + - railroad_benefits # MFIP counts GA, RCA and cash benefits already issued (0017.12.03). # Receipt-of-other-assistance rules in 0011.21 are separate eligibility # rules; this input represents cash received by an assistance-unit member. diff --git a/policyengine_us/parameters/gov/states/mo/dese/ccs/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/mo/dese/ccs/income/countable_income/sources.yaml index aa8be293026..6a24f42b200 100644 --- a/policyengine_us/parameters/gov/states/mo/dese/ccs/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/mo/dese/ccs/income/countable_income/sources.yaml @@ -7,6 +7,7 @@ values: # Regular monthly survivor / RSDI benefits are counted; only death and # burial lump sums are excluded (Manual sec. 5.6.1). - social_security + - railroad_benefits - pension_income # PolicyEngine records military retirement separately from pension_income. - military_retirement_income diff --git a/policyengine_us/parameters/gov/states/ms/dhs/ccpp/income/countable_income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/ms/dhs/ccpp/income/countable_income/unearned_sources.yaml index b6fb36f70df..3e00711066f 100644 --- a/policyengine_us/parameters/gov/states/ms/dhs/ccpp/income/countable_income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/ms/dhs/ccpp/income/countable_income/unearned_sources.yaml @@ -2,6 +2,7 @@ description: Mississippi counts these gross unearned income sources for eligibil values: 2021-11-01: - social_security # SSA benefits for parent and child (umbrella; do not also add social_security_disability) + - railroad_benefits - ssi # SSI for parent and child (separate from the social_security umbrella) - total_unemployment_compensation # counted at redetermination only; simplified to always countable - workers_compensation diff --git a/policyengine_us/parameters/gov/states/mt/dhs/tanf/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/mt/dhs/tanf/income/sources/unearned.yaml index bb23422452f..15bbfec3e0f 100644 --- a/policyengine_us/parameters/gov/states/mt/dhs/tanf/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/mt/dhs/tanf/income/sources/unearned.yaml @@ -10,6 +10,7 @@ values: - rental_income - total_unemployment_compensation - social_security + - railroad_benefits - veterans_benefits - workers_compensation # TANF 501-1 (Assistance Programs) and Mont. Admin. r. 37.78.402 count diff --git a/policyengine_us/parameters/gov/states/mt/dphhs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/mt/dphhs/ccap/income/countable_income/sources.yaml index 614538ff662..a1c51fe3f37 100644 --- a/policyengine_us/parameters/gov/states/mt/dphhs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/mt/dphhs/ccap/income/countable_income/sources.yaml @@ -13,7 +13,8 @@ values: - dividend_income - rental_income # Rental/lease income, contract for deed - social_security # SS and SSDI counted; SSI excluded (social_security omits SSI) - - pension_income # Government pension, annuity, railroad retirement, veterans' pensions + - railroad_benefits # Railroad Retirement counts as pension or retirement income + - pension_income # Government pension, annuity, veterans' pensions - retirement_distributions - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml index 2be2907868d..558d329b52b 100644 --- a/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/lieap/unearned_income_sources.yaml @@ -10,6 +10,7 @@ values: # "Social Security Benefits" (page 20). The plan checks "Including # MediCare deduction", so the benefit is counted gross. - social_security + - railroad_benefits # "Special Assistance (SA)" (page 20): facility SA, SA In-Home and SA for # the Certain Disabled. SA-5400 II also counts the SA In-Home payment for # Energy Programs. diff --git a/policyengine_us/parameters/gov/states/nc/ncdhhs/tanf/income/unearned.yaml b/policyengine_us/parameters/gov/states/nc/ncdhhs/tanf/income/unearned.yaml index 46e17577708..363beb2b3a2 100644 --- a/policyengine_us/parameters/gov/states/nc/ncdhhs/tanf/income/unearned.yaml +++ b/policyengine_us/parameters/gov/states/nc/ncdhhs/tanf/income/unearned.yaml @@ -12,6 +12,7 @@ values: - total_unemployment_compensation - gi_cash_assistance - social_security + - railroad_benefits # general_assistance is not counted per the WF-114 income chart, which # starts on the referenced page 6; the General Assistance row is on page 11 metadata: diff --git a/policyengine_us/parameters/gov/states/nd/dhs/ccap/income/sources.yaml b/policyengine_us/parameters/gov/states/nd/dhs/ccap/income/sources.yaml index f3d11f8a4eb..51c0f852d0c 100644 --- a/policyengine_us/parameters/gov/states/nd/dhs/ccap/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/nd/dhs/ccap/income/sources.yaml @@ -16,6 +16,10 @@ values: # Regular periodic withdrawals from annuities, pensions, and other # retirement plans are countable income (exclusion #8 exception). - pension_income + # NDAC 75-02-01.3-08 disregards twenty types of income, and recurring + # railroad retirement benefits are not one of them; exclusion #1 below + # reaches only retroactive lump sums. + - railroad_benefits - alimony_income - child_support_received @@ -93,3 +97,5 @@ metadata: href: https://www.nd.gov/dhs/policymanuals/40028/40028.htm - title: NDAC 75-02-01.3-07 href: https://ndlegis.gov/prod/acdata/pdf/75-02-01.3.pdf + - title: NDAC 75-02-01.3-08, disregarded income + href: https://ndlegis.gov/prod/acdata/pdf/75-02-01.3.pdf#page=5 diff --git a/policyengine_us/parameters/gov/states/nd/dhs/tanf/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/nd/dhs/tanf/income/sources/unearned.yaml index e817b97e75b..363dad8af2f 100644 --- a/policyengine_us/parameters/gov/states/nd/dhs/tanf/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/nd/dhs/tanf/income/sources/unearned.yaml @@ -16,6 +16,7 @@ values: - workers_compensation - gi_cash_assistance - social_security + - railroad_benefits # Direct GA cash is countable. Section 400-19-55-25 separately excludes # GA reimbursements, third-party payments and complementary assistance # for needs not covered by TANF; those are not represented by this input. diff --git a/policyengine_us/parameters/gov/states/nh/dhhs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/nh/dhhs/ccap/income/countable_income/sources.yaml index 0aa0187bb20..5a13442f076 100644 --- a/policyengine_us/parameters/gov/states/nh/dhhs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/nh/dhhs/ccap/income/countable_income/sources.yaml @@ -8,6 +8,7 @@ values: - farm_operations_income # Unearned income - social_security + - railroad_benefits - pension_income - retirement_distributions - total_unemployment_compensation diff --git a/policyengine_us/parameters/gov/states/nj/dca/liheap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/nj/dca/liheap/income/sources/unearned.yaml index 368a146692a..fd7f9a7d495 100644 --- a/policyengine_us/parameters/gov/states/nj/dca/liheap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/nj/dca/liheap/income/sources/unearned.yaml @@ -9,6 +9,10 @@ values: - strike_benefits - ssi - pension_income + # Handbook 2.3.D counts benefits, pensions and annuities. The Medicare Part B + # deduction in 2.3.F.7 and the exclusion for children in 2.3.E.9 name Social + # Security only, so railroad benefits count gross and at every age. + - railroad_benefits # Military retirement income (retiree pay plus Survivor Benefit Plan # annuities) is a separate input from the pension inputs that pension_income # adds; a household reports the amount in one or the other. Handbook 2.3.D diff --git a/policyengine_us/parameters/gov/states/nj/njdhs/ccap/income/countable_income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/nj/njdhs/ccap/income/countable_income/unearned_sources.yaml index e9288765265..6929eff68ca 100644 --- a/policyengine_us/parameters/gov/states/nj/njdhs/ccap/income/countable_income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/nj/njdhs/ccap/income/countable_income/unearned_sources.yaml @@ -2,6 +2,7 @@ description: New Jersey counts these unearned income sources under the Child Car values: 2024-01-01: - social_security_retirement + - railroad_benefits - social_security_disability - social_security_survivors - ssi diff --git a/policyengine_us/parameters/gov/states/nv/dwss/ccdp/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/nv/dwss/ccdp/income/countable_income/sources.yaml index 94dbafb5cae..d67b8ea8898 100644 --- a/policyengine_us/parameters/gov/states/nv/dwss/ccdp/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/nv/dwss/ccdp/income/countable_income/sources.yaml @@ -10,6 +10,7 @@ values: - farm_operations_income # Unearned income (countable per MS 306). - social_security # MS 306.34 RSDI umbrella (Retirement, Survivors, Disability) + - railroad_benefits - disability_benefits # MS 306.9 Disability Insurance Benefits (employer/private disability; distinct from RSDI 306.34 and workers' comp 306.49) - total_unemployment_compensation # MS 306.45 - workers_compensation # MS 306.49 diff --git a/policyengine_us/parameters/gov/states/ok/dhs/ccs/income/sources.yaml b/policyengine_us/parameters/gov/states/ok/dhs/ccs/income/sources.yaml index 6cb829c7804..d67fec6338f 100644 --- a/policyengine_us/parameters/gov/states/ok/dhs/ccs/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ok/dhs/ccs/income/sources.yaml @@ -14,6 +14,7 @@ values: # social_security already includes the disability, retirement, survivors, # and dependents components. - social_security + - railroad_benefits - total_unemployment_compensation - workers_compensation - child_support_received diff --git a/policyengine_us/parameters/gov/states/or/delc/erdc/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/or/delc/erdc/income/countable_income/sources.yaml index 5406d40ab97..346ab7f9b9e 100644 --- a/policyengine_us/parameters/gov/states/or/delc/erdc/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/or/delc/erdc/income/countable_income/sources.yaml @@ -8,6 +8,11 @@ values: - social_security - ssi - pension_income + # OAR 414-175-0035(52)(a)(A) and (c)(A) count monthly benefits employees + # receive only when they retire as unearned income. OAR 414-175-0030(1)(c) + # counts income no rule excludes, and 414-175-0035 excludes no railroad + # benefit. + - railroad_benefits - retirement_distributions - military_retirement_income - total_unemployment_compensation diff --git a/policyengine_us/parameters/gov/states/pa/dhs/ccw/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/pa/dhs/ccw/income/countable_income/sources.yaml index ebcaf698363..1d15b6b3225 100644 --- a/policyengine_us/parameters/gov/states/pa/dhs/ccw/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/pa/dhs/ccw/income/countable_income/sources.yaml @@ -7,6 +7,9 @@ values: # Part I.E; social_security adds the retirement, disability, # survivors and dependents benefits. - social_security + # Part I.F counts railroad retirement, disability and survivors' benefits + # before deductions of health insurance premiums. + - railroad_benefits - ssi - pension_income - military_retirement_income @@ -18,9 +21,9 @@ values: - dividend_income - interest_income - rental_income - # Not modeled: railroad retirement (F), state blind pension (G), - # public assistance/retirement (H), royalties (Q), lump sums >$100 (R), - # lottery >$100 (S), room/board net income (U) + # Not modeled: state blind pension (G), public assistance/retirement (H), + # royalties (Q), lump sums >$100 (R), lottery >$100 (S), room/board net + # income (U) metadata: unit: list diff --git a/policyengine_us/parameters/gov/states/ri/dhs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/ri/dhs/ccap/income/countable_income/sources.yaml index c35e4f9c44d..bb6eaf1ae1a 100644 --- a/policyengine_us/parameters/gov/states/ri/dhs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/ri/dhs/ccap/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - sstb_self_employment_income - social_security_retirement + - railroad_benefits - social_security_disability - social_security_survivors - ssi diff --git a/policyengine_us/parameters/gov/states/sc/dss/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/sc/dss/ccap/income/countable_income/sources.yaml index b6fce673bbd..0907ff8a940 100644 --- a/policyengine_us/parameters/gov/states/sc/dss/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/sc/dss/ccap/income/countable_income/sources.yaml @@ -6,6 +6,7 @@ values: - sstb_self_employment_income - farm_operations_income - social_security + - railroad_benefits - pension_income - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/sd/dss/cca/income/sources.yaml b/policyengine_us/parameters/gov/states/sd/dss/cca/income/sources.yaml index f80335bff2b..e8958aabeff 100644 --- a/policyengine_us/parameters/gov/states/sd/dss/cca/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/sd/dss/cca/income/sources.yaml @@ -14,6 +14,8 @@ values: - workers_compensation - veterans_benefits # "VA benefits"; VA disability is non-countable - pension_income # "pensions" and "retirement" + # Railroad retirement annuities are pensions and retirement benefits. + - railroad_benefits - alimony_income - child_support_received # "child support" and "back child support" - interest_income # "interest income" diff --git a/policyengine_us/parameters/gov/states/tx/fpp/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/tx/fpp/income/sources/unearned.yaml index 3bb96c30c8d..7ad6df61961 100644 --- a/policyengine_us/parameters/gov/states/tx/fpp/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/tx/fpp/income/sources/unearned.yaml @@ -3,6 +3,7 @@ values: 2016-07-01: # Retirement, survivors and disability insurance (RSDI and SSDI) - social_security + - railroad_benefits - pension_income # Employment-based disability insurance benefits - disability_benefits diff --git a/policyengine_us/parameters/gov/states/tx/tdhca/ceap/income/sources.yaml b/policyengine_us/parameters/gov/states/tx/tdhca/ceap/income/sources.yaml index 62c2170277f..6f645609194 100644 --- a/policyengine_us/parameters/gov/states/tx/tdhca/ceap/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/tx/tdhca/ceap/income/sources.yaml @@ -15,6 +15,7 @@ values: # Counted excluding the Medicare deduction; we don't track # Medicare premium withholding at the moment - social_security + - railroad_benefits # Calculated SSI; API partners can override by sending ssi as an input - ssi - pension_income diff --git a/policyengine_us/parameters/gov/states/ut/dwf/ccap/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/ut/dwf/ccap/income/sources/unearned.yaml index a323a8baab2..0da434dee5a 100644 --- a/policyengine_us/parameters/gov/states/ut/dwf/ccap/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/ut/dwf/ccap/income/sources/unearned.yaml @@ -31,6 +31,7 @@ description: Utah counts these unearned income sources as countable income under values: 2020-01-01: - social_security + - railroad_benefits - pension_income - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/va/dss/ccsp/income/countable_income/unearned_sources.yaml b/policyengine_us/parameters/gov/states/va/dss/ccsp/income/countable_income/unearned_sources.yaml index 4a7db44d921..f9b2425753a 100644 --- a/policyengine_us/parameters/gov/states/va/dss/ccsp/income/countable_income/unearned_sources.yaml +++ b/policyengine_us/parameters/gov/states/va/dss/ccsp/income/countable_income/unearned_sources.yaml @@ -3,6 +3,7 @@ values: 2023-01-01: - child_support_received - social_security + - railroad_benefits - pension_income - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/vt/dcf/ccfap/income/sources.yaml b/policyengine_us/parameters/gov/states/vt/dcf/ccfap/income/sources.yaml index cb90545f111..59cf685d60f 100644 --- a/policyengine_us/parameters/gov/states/vt/dcf/ccfap/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/vt/dcf/ccfap/income/sources.yaml @@ -6,6 +6,7 @@ values: - sstb_self_employment_income - pension_income - social_security + - railroad_benefits - alimony_income - child_support_received - dividend_income diff --git a/policyengine_us/parameters/gov/states/wa/dcyf/eceap/income_sources.yaml b/policyengine_us/parameters/gov/states/wa/dcyf/eceap/income_sources.yaml index f1493945d32..5d7e6633b5b 100644 --- a/policyengine_us/parameters/gov/states/wa/dcyf/eceap/income_sources.yaml +++ b/policyengine_us/parameters/gov/states/wa/dcyf/eceap/income_sources.yaml @@ -6,6 +6,9 @@ values: - tanf - ssi - social_security + # DCYF 05-006 Section 9 lists "Social Security or other retirement + # benefits" and "Retirement or pension plans". + - railroad_benefits - child_support_received - total_unemployment_compensation - veterans_benefits @@ -16,7 +19,7 @@ metadata: period: year label: Washington ECEAP family income sources reference: - - title: DCYF 05-006 ECEAP Prescreen and Application (Combined Form), Section 9 - Income Received by Child's Parent(s) or Guardian(s) - href: https://olycap.org/hubfs/PDF%20Attachments/ECEAP%202025-2026%20(EN).pdf#page=6 + - title: DCYF 05-006 ECEAP Prescreen and Application (Combined Form, revised 04/2025), Section 9 - Income Received by Child's Parent(s) or Guardian(s) + href: https://www.dcyf.wa.gov/sites/default/files/forms/05-006.pdf#page=7 - title: RCW 43.216.505 Eligible children href: https://app.leg.wa.gov/RCW/default.aspx?cite=43.216.505 diff --git a/policyengine_us/parameters/gov/states/wa/dcyf/wccc/sources.yaml b/policyengine_us/parameters/gov/states/wa/dcyf/wccc/sources.yaml index ef72d41196e..b4871f59b53 100644 --- a/policyengine_us/parameters/gov/states/wa/dcyf/wccc/sources.yaml +++ b/policyengine_us/parameters/gov/states/wa/dcyf/wccc/sources.yaml @@ -8,6 +8,7 @@ values: - rental_income # Unearned income (WAC 110-15-0060) - social_security + - railroad_benefits - pension_income - retirement_distributions - disability_benefits diff --git a/policyengine_us/parameters/gov/states/wi/dcf/shares/income/sources.yaml b/policyengine_us/parameters/gov/states/wi/dcf/shares/income/sources.yaml index a720047408c..b434402b6e2 100644 --- a/policyengine_us/parameters/gov/states/wi/dcf/shares/income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wi/dcf/shares/income/sources.yaml @@ -31,6 +31,7 @@ values: # social_security is an umbrella that already includes the old age, # survivorship, disability, and dependents components. - social_security + - railroad_benefits # Pensions and annuities, including veteran pensions. - pension_income - total_unemployment_compensation diff --git a/policyengine_us/parameters/gov/states/wv/dhhr/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/wv/dhhr/ccap/income/countable_income/sources.yaml index 9ad7ebeaa7f..d11602ee5b2 100644 --- a/policyengine_us/parameters/gov/states/wv/dhhr/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wv/dhhr/ccap/income/countable_income/sources.yaml @@ -5,6 +5,7 @@ values: - self_employment_income - farm_operations_income - social_security + - railroad_benefits - ssi - total_unemployment_compensation - workers_compensation diff --git a/policyengine_us/parameters/gov/states/wy/dfs/ccap/income/countable_income/sources.yaml b/policyengine_us/parameters/gov/states/wy/dfs/ccap/income/countable_income/sources.yaml index e2bcfca99cd..a05e3e24501 100644 --- a/policyengine_us/parameters/gov/states/wy/dfs/ccap/income/countable_income/sources.yaml +++ b/policyengine_us/parameters/gov/states/wy/dfs/ccap/income/countable_income/sources.yaml @@ -17,7 +17,8 @@ values: - interest_income - dividend_income - rental_income # The manual's mortgage-payment deduction is not modeled - - pension_income # Pensions, annuities, retirement, railroad retirement + - pension_income # Pensions, annuities, retirement + - railroad_benefits # Rules Appendix B: RR retirement is unearned and nonexempt - retirement_distributions - alimony_income - child_support_received # From a non-assistance-unit member diff --git a/policyengine_us/parameters/gov/states/wy/dfs/power/income/sources/unearned.yaml b/policyengine_us/parameters/gov/states/wy/dfs/power/income/sources/unearned.yaml index 90a9dfc133a..0ba9f415cf3 100644 --- a/policyengine_us/parameters/gov/states/wy/dfs/power/income/sources/unearned.yaml +++ b/policyengine_us/parameters/gov/states/wy/dfs/power/income/sources/unearned.yaml @@ -16,6 +16,7 @@ values: - workers_compensation - gi_cash_assistance - social_security + - railroad_benefits # Section 901(J) treats state, local and tribal GA as nonexempt income. # Vendor payments for purposes not covered by POWER are separately exempt. - general_assistance diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/ala/ca_ala_general_assistance_countable_income_person.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/ala/ca_ala_general_assistance_countable_income_person.yaml new file mode 100644 index 00000000000..373081ec543 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/ala/ca_ala_general_assistance_countable_income_person.yaml @@ -0,0 +1,10 @@ +- name: Case 1, railroad retirement benefits count as income. + # GA regulations define unearned income as "all other income received" and + # name Railroad Retirement Benefits among potentially available income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: CA + railroad_benefits: 12_000 + output: + ca_ala_general_assistance_countable_income_person: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/la/dwp/ez_save/ca_la_ez_save_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/la/dwp/ez_save/ca_la_ez_save_countable_income.yaml new file mode 100644 index 00000000000..e21cb54811f --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/la/dwp/ez_save/ca_la_ez_save_countable_income.yaml @@ -0,0 +1,13 @@ +- name: Case 1, railroad retirement benefits count as income. + # EZ-SAVE defines income as all the money and benefits a household receives and + # names retirement. The income list also counts SNAP, which this case holds at + # zero to isolate the railroad benefit. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: CA + in_la: true + snap: 0 + railroad_benefits: 12_000 + output: + ca_la_ez_save_countable_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/la/general_relief/la_general_relief_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/la/general_relief/la_general_relief_gross_income.yaml index b93868f4c99..4b1485e8233 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ca/la/general_relief/la_general_relief_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/la/general_relief/la_general_relief_gross_income.yaml @@ -61,3 +61,14 @@ output: # 1,200 + 600 + 1,800 la_general_relief_gross_income: 3_600 + +- name: Case 3, railroad retirement benefits count as income. + # ABP 392 defines net income as any income after deductions. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: CA + in_la: true + railroad_benefits: 12_000 + output: + la_general_relief_gross_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/oc/general_relief/income/ca_oc_general_relief_gross_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/oc/general_relief/income/ca_oc_general_relief_gross_unearned_income.yaml new file mode 100644 index 00000000000..02454859c9e --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/oc/general_relief/income/ca_oc_general_relief_gross_unearned_income.yaml @@ -0,0 +1,10 @@ +- name: Case 1, railroad retirement benefits count as income. + # GR manual section 70.1 considers all income, including pensions and retirement. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: CA + in_oc: true + railroad_benefits: 12_000 + output: + ca_oc_general_relief_gross_unearned_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/riv/cap/liheap/ca_riv_liheap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/riv/cap/liheap/ca_riv_liheap_countable_income.yaml new file mode 100644 index 00000000000..4adf22b4f86 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/riv/cap/liheap/ca_riv_liheap_countable_income.yaml @@ -0,0 +1,10 @@ +- name: Case 1, railroad retirement benefits count as income. + # The LIHEAP application lists pension and annuities as countable income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: CA + in_riv: true + railroad_benefits: 12_000 + output: + ca_riv_liheap_countable_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/riv/cap/share/ca_riv_share_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/riv/cap/share/ca_riv_share_countable_income.yaml new file mode 100644 index 00000000000..c423f8f75e7 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/riv/cap/share/ca_riv_share_countable_income.yaml @@ -0,0 +1,10 @@ +- name: Case 1, railroad retirement benefits count as income. + # SHARE requires all household income and names pension among income proofs. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: CA + in_riv: true + railroad_benefits: 12_000 + output: + ca_riv_share_countable_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/local/ca/smc/general_assistance/income/ca_smc_general_assistance_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/local/ca/smc/general_assistance/income/ca_smc_general_assistance_countable_income.yaml index 94db088ae07..24a1b4d68d1 100644 --- a/policyengine_us/tests/policy/baseline/gov/local/ca/smc/general_assistance/income/ca_smc_general_assistance_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/local/ca/smc/general_assistance/income/ca_smc_general_assistance_countable_income.yaml @@ -85,3 +85,14 @@ members: [person1] output: ca_smc_general_assistance_countable_income: 300 + +- name: Case 6, railroad retirement benefits count as income. + # The GA fact sheet counts retirement benefits and other means of income or support. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: CA + in_smc: true + railroad_benefits: 12_000 + output: + ca_smc_general_assistance_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/tanf/cash/income/ca_tanf_other_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/tanf/cash/income/ca_tanf_other_unearned_income.yaml new file mode 100644 index 00000000000..d6b44bb2207 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/ca/cdss/tanf/cash/income/ca_tanf_other_unearned_income.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # EAS 44-113.3 counts "Net income from Social Security or from Railroad Retirement Benefits". + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: CA + railroad_benefits: 12_000 + output: + ca_tanf_other_unearned_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/ccap/co_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/ccap/co_ccap_countable_income.yaml new file mode 100644 index 00000000000..93404108560 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/co/ccap/co_ccap_countable_income.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # 8 CCR 1403-1 section 3.111(I) includes "Railroad retirement insurance". + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: CO + railroad_benefits: 12_000 + output: + co_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/co/cdhs/tanf/co_tanf_countable_gross_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/co/cdhs/tanf/co_tanf_countable_gross_unearned_income.yaml index 6857ce3f465..a7f1aab2daf 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/co/cdhs/tanf/co_tanf_countable_gross_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/co/cdhs/tanf/co_tanf_countable_gross_unearned_income.yaml @@ -119,3 +119,13 @@ output: # $2,400 + $3,600 + $1,200 (not enrolled, so child support counted) = $7,200 co_tanf_countable_gross_unearned_income: 7_200 + +- name: Case 9, railroad retirement benefits count as unearned income. + # 9 CCR 2503-6 section 3.605.3 counts "Railroad retirement payments". + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: CO + railroad_benefits: 12_000 + output: + co_tanf_countable_gross_unearned_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/dc/dhs/ccsp/dc_ccsp_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/dc/dhs/ccsp/dc_ccsp_countable_income.yaml new file mode 100644 index 00000000000..d5df88aaaff --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/dc/dhs/ccsp/dc_ccsp_countable_income.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # CCSP Policy Manual 2.5.2 counts "railroad retirement, private pensions, annuities". + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: DC + railroad_benefits: 12_000 + output: + dc_ccsp_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/de/dss/poc/de_poc_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/de/dss/poc/de_poc_countable_income.yaml new file mode 100644 index 00000000000..3a06758562d --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/de/dss/poc/de_poc_countable_income.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # 16 Del. Admin. Code 11003 counts "Pensions; viii. Railroad retirement; ix. Annuities". + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: DE + railroad_benefits: 12_000 + output: + de_poc_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/hi/bessd/ccap/hi_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/hi/bessd/ccap/hi_ccap_countable_income.yaml index 6c8c79471a7..ed4bcd735a5 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/hi/bessd/ccap/hi_ccap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/hi/bessd/ccap/hi_ccap_countable_income.yaml @@ -372,3 +372,13 @@ # Hawaii counts public assistance in the source list's first month. hi_ccap_tanf_income: 100 hi_ccap_countable_income: 1_100 + +- name: Case 17, railroad retirement benefits count as income. + # HAR 17-798.3-10(b)(2)(A) counts railroad retirement insurance checks from the U.S. government. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: HI + railroad_benefits: 12_000 + output: + hi_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/la/ldoe/ccap/la_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/la/ldoe/ccap/la_ccap_countable_income.yaml index 00e96802360..0d8d5d2802b 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/la/ldoe/ccap/la_ccap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/la/ldoe/ccap/la_ccap_countable_income.yaml @@ -144,3 +144,29 @@ state_code: LA output: la_ccap_countable_income: 0 + +- name: Case 8, railroad retirement benefits count as unearned income. + # LAC 28:CLXV.509.A.3.b counts recurring retirement (iv) and disability (v) + # benefits of all household members. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + railroad_benefits: 12_000 + child: + age: 4 + tax_units: + tax_unit: + members: [parent, child] + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: LA + output: + # 12,000 / 12 = 1,000 + la_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ma/eec/ccfa/ma_ccfa_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ma/eec/ccfa/ma_ccfa_countable_income.yaml index dfbdd4ebdc1..8b8062523fa 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ma/eec/ccfa/ma_ccfa_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ma/eec/ccfa/ma_ccfa_countable_income.yaml @@ -599,3 +599,19 @@ output: ma_ccfa_is_parent: [true, true, false] ma_ccfa_countable_income: 5_300 + +- name: Case 26, railroad retirement benefits count as retirement income. + # The October 2023 interim policies list retirement income among the only + # income sources counted. They exclude Social Security, which the Social + # Security Administration pays, and railroad annuities come from the + # Railroad Retirement Board. + period: 2023 + absolute_error_margin: 0.01 + input: + age: 35 + state_code: MA + railroad_benefits: 12_000 + output: + ma_ccfa_countable_income: + 2023-09: 1_000 + 2023-10: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/me/dhhs/ccap/me_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/me/dhhs/ccap/me_ccap_countable_income.yaml new file mode 100644 index 00000000000..6e3c5718734 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/me/dhhs/ccap/me_ccap_countable_income.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # 10-148 C.M.R. ch. 6, 2.C counts "railroad retirement, private pensions, annuities". + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: ME + railroad_benefits: 12_000 + output: + me_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ccap/mi_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ccap/mi_ccap_countable_income.yaml new file mode 100644 index 00000000000..76cabe23eb5 --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/mi/mdhhs/ccap/mi_ccap_countable_income.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # BEM 503 counts the gross Railroad Retirement benefit as unearned income for all programs. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: MI + railroad_benefits: 12_000 + output: + mi_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/mt/dphhs/ccap/mt_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/mt/dphhs/ccap/mt_ccap_countable_income.yaml index 320fb48e804..1ae3d8a320f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/mt/dphhs/ccap/mt_ccap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/mt/dphhs/ccap/mt_ccap_countable_income.yaml @@ -146,3 +146,13 @@ state_code: MT output: mt_ccap_countable_income: 1_500 # (12,000 + 6,000) / 12 + +- name: Case 8, railroad retirement benefits count as income. + # The Child Care 2-6 Income Evaluation Table marks Railroad Retirement as pension or retirement income, countable. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: MT + railroad_benefits: 12_000 + output: + mt_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/nc_lieap_gross_income_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/nc_lieap_gross_income_person.yaml new file mode 100644 index 00000000000..db97a2b048c --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/lieap/nc_lieap_gross_income_person.yaml @@ -0,0 +1,9 @@ +- name: Case 1, railroad retirement benefits count as unearned income. + # EP-300 300.09 A takes the types of income from FNS 300, whose 300.01 B lists railroad retirement as unearned income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NC + railroad_benefits: 12_000 + output: + nc_lieap_gross_income_person: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/tanf/nc_tanf_countable_gross_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/tanf/nc_tanf_countable_gross_unearned_income.yaml index cf6d11ee2ed..ef3aa5ad594 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/tanf/nc_tanf_countable_gross_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nc/ncdhhs/tanf/nc_tanf_countable_gross_unearned_income.yaml @@ -163,3 +163,13 @@ # recipients is counted (State Plan Financial Rules 3 and 4). # 1_200 + 600 = 1_800 nc_tanf_countable_gross_unearned_income: 1_800 + +- name: Case 9, railroad retirement benefits count as unearned income. + # WF-114 lists "Railroad Retirement" as countable unearned income. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NC + railroad_benefits: 12_000 + output: + nc_tanf_countable_gross_unearned_income: 12_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/ccap/income/nd_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/ccap/income/nd_ccap_countable_income.yaml index 451b29f017a..98d39548a4f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/ccap/income/nd_ccap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/ccap/income/nd_ccap_countable_income.yaml @@ -152,3 +152,29 @@ output: # 3,000 + 500 (19-year-old's earnings counted) = 3,500 nd_ccap_countable_income: 3_500 + +- name: Case 8, railroad retirement benefits count as unearned income. + # NDAC 75-02-01.3-08 disregards no recurring railroad retirement benefit. + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + railroad_benefits: 12_000 + child: + age: 4 + tax_units: + tax_unit: + members: [parent, child] + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: ND + output: + # 12,000 / 12 = 1,000 + nd_ccap_gross_income: 1_000 + nd_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/tanf/nd_tanf_gross_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/tanf/nd_tanf_gross_unearned_income.yaml index 3248d0004d8..d2f02c60ed6 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/tanf/nd_tanf_gross_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nd/dhs/tanf/nd_tanf_gross_unearned_income.yaml @@ -75,3 +75,13 @@ dividend_income: 1200 output: nd_tanf_gross_unearned_income: 300 + +- name: Case 9, railroad retirement benefits count as unearned income. + # Manual 400-19-55-20-15 lists "Railroad benefits" as countable unearned income. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: ND + railroad_benefits: 12_000 + output: + nd_tanf_gross_unearned_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/nj/dca/liheap/income/nj_liheap_countable_income_person.yaml b/policyengine_us/tests/policy/baseline/gov/states/nj/dca/liheap/income/nj_liheap_countable_income_person.yaml index 83f0b3a17e0..30e00741757 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/nj/dca/liheap/income/nj_liheap_countable_income_person.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/nj/dca/liheap/income/nj_liheap_countable_income_person.yaml @@ -231,3 +231,29 @@ nj_liheap_countable_income_person: [24_000, 9_600] # 2,000 and 800 a month are whole dollars, so rounding changes nothing. nj_liheap_countable_income: [24_000, 9_600] + +- name: Case 10, railroad benefits count before the Medicare Part B premium. + # Handbook 2.3.D counts benefits, pensions and annuities. The Part B deduction + # in 2.3.F.7 names Social Security benefits only. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: NJ + age: 70 + railroad_benefits: 24_000 + medicare_part_b_premium: 2_400 + output: + nj_liheap_countable_income_person: 24_000 + +- name: Case 11, a child's railroad benefits count while a child's Social Security does not. + # Two one-person households aged 10. Handbook 2.3.E.9 excludes Social Security + # benefits for children under 18 and names no other benefit. + period: 2026 + absolute_error_margin: 0.01 + input: + state_code: [NJ, NJ] + age: [10, 10] + railroad_benefits: [6_000, 0] + social_security: [0, 6_000] + output: + nj_liheap_countable_income_person: [6_000, 0] diff --git a/policyengine_us/tests/policy/baseline/gov/states/or/delc/erdc/or_erdc_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/or/delc/erdc/or_erdc_countable_income.yaml index 81bf6c33506..3e57537025f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/or/delc/erdc/or_erdc_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/or/delc/erdc/or_erdc_countable_income.yaml @@ -82,3 +82,29 @@ # Pension 12,000/year = 1,000/month, rental 6,000/year = 500/month, and # workers compensation 4,800/year = 400/month: 1,000 + 500 + 400 = 1,900. or_erdc_countable_income: 1_900 + +- name: Case 4, railroad retirement benefits count as unearned income. + # OAR 414-175-0035(52)(c)(A) counts monthly retirement benefits as unearned + # income. + period: 2027-01 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + railroad_benefits: 12_000 + child: + age: 4 + tax_units: + tax_unit: + members: [parent, child] + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: OR + output: + # 12,000 / 12 = 1,000 + or_erdc_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/pa/dhs/ccw/pa_ccw_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/pa/dhs/ccw/pa_ccw_countable_income.yaml index 43f099e6bba..942663a905d 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/pa/dhs/ccw/pa_ccw_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/pa/dhs/ccw/pa_ccw_countable_income.yaml @@ -161,3 +161,13 @@ # The retirement benefit exceeds the federal SSI benefit rate, so ssi is 0. # Monthly: (9,600 + 2,400 + 3,600 + 18,000) / 12 = 33,600 / 12 = 2,800 pa_ccw_countable_income: 2_800 + +- name: Case 8, railroad retirement benefits count as income. + # 55 Pa. Code chapter 3042, Appendix A, Part I(F) counts railroad retirement, disability or survivors' benefits. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: PA + railroad_benefits: 12_000 + output: + pa_ccw_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sc/dss/ccap/sc_ccap_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/sc/dss/ccap/sc_ccap_countable_income.yaml index afc12697219..8d5f95e8bfc 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sc/dss/ccap/sc_ccap_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sc/dss/ccap/sc_ccap_countable_income.yaml @@ -121,3 +121,13 @@ output: # SNAP is excluded per Policy Manual 2.1.15 sc_ccap_countable_income: 20_000 + +- name: Case 7, railroad retirement benefits count as unearned income. + # The Child Care Scholarship Policy Manual counts "Railroad retirement insurance benefits". + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: SC + railroad_benefits: 12_000 + output: + sc_ccap_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/sd/dss/cca/income/sd_cca_countable_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/sd/dss/cca/income/sd_cca_countable_income.yaml index 30510bbab1d..f438012e7e7 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/sd/dss/cca/income/sd_cca_countable_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/sd/dss/cca/income/sd_cca_countable_income.yaml @@ -302,3 +302,29 @@ state_code: SD output: sd_cca_countable_income: 3_000 + +- name: Case 14, railroad retirement benefits count as unearned income. + # Subsidy Manual Section 4 counts "pensions" and "retirement". + period: 2027-01 + absolute_error_margin: 0.1 + input: + people: + parent: + age: 40 + railroad_benefits: 12_000 + child: + age: 4 + tax_units: + tax_unit: + members: [parent, child] + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: SD + output: + # 12,000 / 12 = 1,000, with no disregard on unearned income. + sd_cca_gross_income: 1_000 + sd_cca_countable_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/ut/dwf/ccap/income/ut_ccap_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/ut/dwf/ccap/income/ut_ccap_gross_income.yaml index e8ce52fb69e..d0180a9775a 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/ut/dwf/ccap/income/ut_ccap_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/ut/dwf/ccap/income/ut_ccap_gross_income.yaml @@ -110,3 +110,27 @@ # court-appointed guardian — so their 3,000 in wages is counted # (R986-700-710(4)(a) and (4)(b)). ut_ccap_gross_income: 3_000 + +- name: Case 5, railroad retirement benefits count as income. + # R986-200-235(2)(a) counts "pensions and annuities such as Railroad Retirement". + period: 2026-01 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + railroad_benefits: 12_000 + child: + age: 5 + spm_units: + spm_unit: + members: [parent, child] + tax_units: + tax_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: UT + output: + ut_ccap_gross_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wa/dcyf/eceap/wa_eceap_family_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wa/dcyf/eceap/wa_eceap_family_income.yaml new file mode 100644 index 00000000000..6b14b503a0a --- /dev/null +++ b/policyengine_us/tests/policy/baseline/gov/states/wa/dcyf/eceap/wa_eceap_family_income.yaml @@ -0,0 +1,27 @@ +- name: Case 1, railroad retirement benefits count as family income. + # DCYF 05-006 Section 9 lists "Social Security or other retirement benefits". + # Wages above TANF's limit keep TANF, which the list also counts, at zero. + # TANF counts wages before labor supply responses, so the case sets those. + period: 2026 + absolute_error_margin: 0.01 + input: + people: + parent: + age: 40 + employment_income_before_lsr: 60_000 + railroad_benefits: 12_000 + child: + age: 4 + tax_units: + tax_unit: + members: [parent, child] + spm_units: + spm_unit: + members: [parent, child] + households: + household: + members: [parent, child] + state_code: WA + output: + # 60,000 + 12,000 = 72,000 + wa_eceap_family_income: 72_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/ccap/income/wy_ccap_gross_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/ccap/income/wy_ccap_gross_income.yaml index 5f72d0754e9..8c0f367fccf 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/ccap/income/wy_ccap_gross_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/ccap/income/wy_ccap_gross_income.yaml @@ -121,3 +121,13 @@ # disregard + 500 unearned = 2_300. wy_ccap_gross_income: 2_500 wy_ccap_countable_income: 2_300 + +- name: Case 6, railroad retirement benefits count as income. + # Child care rules, chapter 1, Appendix B, list RR retirement as unearned and nonexempt. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: WY + railroad_benefits: 12_000 + output: + wy_ccap_gross_income: 1_000 diff --git a/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/power/wy_power_gross_unearned_income.yaml b/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/power/wy_power_gross_unearned_income.yaml index 63ec95cbe0f..6cce2e6d68f 100644 --- a/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/power/wy_power_gross_unearned_income.yaml +++ b/policyengine_us/tests/policy/baseline/gov/states/wy/dfs/power/wy_power_gross_unearned_income.yaml @@ -63,3 +63,13 @@ taxable_interest_income: 2400 output: wy_power_gross_unearned_income: 400 + +- name: Case 8, railroad retirement benefits count as unearned income. + # Manual 901(J) marks "Railroad retirement or railroad unemployment" nonexempt. + period: 2026-01 + absolute_error_margin: 0.01 + input: + state_code: WY + railroad_benefits: 12_000 + output: + wy_power_gross_unearned_income: 1_000 diff --git a/policyengine_us/tests/test_state_benefit_railroad_income.py b/policyengine_us/tests/test_state_benefit_railroad_income.py new file mode 100644 index 00000000000..ef5e43511fc --- /dev/null +++ b/policyengine_us/tests/test_state_benefit_railroad_income.py @@ -0,0 +1,730 @@ +"""State and county benefit programs count Railroad Retirement Act benefits as income. + +Each row names an income list or variable, the rule that covers railroad +retirement benefits, and the variable that reads the list. A rule covers the +benefits by naming railroad retirement, by listing pensions, annuities, +retirement benefits or federal benefits as income, or by defining income as +everything outside its exclusions. + +Invariants: + +- R1: for every program below and amounts from $1 to $200,000, railroad benefits + raise the program's income by the amount received, at the variable's period. + Where the income total also counts cash aid that the same benefit reduces, the + test holds that aid at zero, so the row isolates the income list from the aid's + own income test. R1 shows that the list entry reaches the program's income, + and each row cites the rule whose text covers railroad benefits. +- R2: the same dollars count the same whether a person receives them as railroad + benefits or as a pension, for programs whose lists count pensions. +- R3 scans every state and county list outside the tax code, and the top level + and each function or class body of every state and county variable outside the + tax code. Each one that reads a Social Security amount counts railroad benefits + (the total or one of its parts), belongs to an R1 variable, or appears in + NO_RAILROAD_INCOME with the reason it doesn't. A new reader fails this test + until its author reads the rule and does one or the other. R3 reads names + written out in full, so a name built with an f-string or concatenation, or a + scalar parameter, passes unseen. So does a module that names Social Security in + one top-level list and railroad benefits in another, since R3 reads a + module's top level as one scope. +- R4: no two entries of a parameter list or variable adds list hold the same + railroad dollars. The total, the tier 1 amount, the taxable total and each + part map to the taxable and tax-free tier 1 benefits and the taxable and + tax-free rest of the annuity. +- R5: a state or county program that reads a federal total, list or test takes + its sources. R5 starts from each federal list that holds Social Security + without railroad benefits, each federal variable that adds such a list or + reads it in a formula, and each federal variable whose own code names a + Social Security amount and no railroad amount, as R3 checks state code. It + then follows those variables through up to two more formulas that name no + railroad amount. Each source that state or county code reads must appear in + FEDERAL_GAPS, and the test fails on an entry once its source counts railroad + benefits or no state or county code reads it. A new reader of a source + already in FEDERAL_GAPS passes. A third formula step would reach further + eligibility tests and benefit amounts, such as is_snap_eligible and aca_ptc, + and R5 doesn't follow it. R5 reads a parameter path written out in code, or + built through names assigned in the same module or function, or named in a + string. A path passed as a function argument or read with getattr passes + unseen. +""" + +import ast +import inspect +from functools import lru_cache +from pathlib import Path + +import pytest +import yaml +from hypothesis import given, settings +from hypothesis import strategies as st + +from policyengine_us import Simulation +from policyengine_us.system import system + +YEAR = 2026 +MONTH = "2026-06" +PACKAGE = Path(__file__).resolve().parents[1] + +# (state, variable, rule) +PROGRAMS = [ + ("AK", "ak_ccap_countable_unearned_income", "CCAP manual 4080-3(1)"), + ("AL", "al_ccsp_countable_income", "FFY 2025-2027 CCDF plan"), + ("AR", "ar_sra_countable_income", "FFY 2025-2027 CCDF plan"), + ("CA", "ca_child_care_countable_income", "5 CCR 18078"), + ("CA", "ca_tanf_other_unearned_income", "CalWORKs EAS 44-113.3"), + ("CA", "ca_cpuc_countable_income", "CARE and FERA application"), + ("CO", "co_ccap_countable_income", "8 CCR 1403-1"), + ("CO", "co_tanf_countable_gross_unearned_income", "9 CCR 2503-6 3.605"), + ("CT", "ct_c4k_countable_income", "RCSA 17b-749 definitions"), + ("DC", "dc_ccsp_countable_income", "CCSP policy manual 2.5.2"), + ("DC", "dc_tanf_gross_unearned_income", "D.C. Code 4-205.05"), + ("DE", "de_poc_countable_income", "16 Del. Admin. Code 11003"), + ("FL", "fl_sr_countable_income", "Fla. Admin. Code 6M-4.200"), + ("GA", "ga_caps_countable_income", "CAPS policy manual"), + ("HI", "hi_ccap_countable_income", "HAR 17-798.3-10(b)(2)(A)"), + ("ID", "id_iccp_countable_income", "IDAPA 16.06.12"), + ("IL", "il_aabd_gross_unearned_income", "89 Ill. Adm. Code 113.100"), + ("IL", "il_tanf_gross_unearned_income", "89 Ill. Adm. Code 112.100"), + ("IL", "il_hbwd_countable_unearned_income", "89 Ill. Adm. Code 120.330"), + ("IL", "il_isbe_countable_income", "ISBE household income form"), + ("KS", "ks_ccap_countable_income", "KEESM 6200"), + ("KS", "ks_liheap_countable_unearned_income", "KEESM 13361"), + ("KY", "ky_ccap_countable_income", "922 KAR 2:160"), + ("LA", "la_ccap_countable_income", "LAC 28:CLXV.509.A.3.b"), + ("MA", "ma_tcap_gross_unearned_income", "106 CMR 704.210(B)"), + ("MA", "ma_ccfa_countable_income_person", "EEC interim policies, Oct. 2023"), + ("MD", "md_ccs_countable_income", "COMAR 13A.14.06.02"), + ("ME", "me_ccap_countable_income", "CCAP rule"), + ("ME", "me_tanf_gross_unearned_income", "10-144 C.M.R. ch. 331"), + ("MI", "mi_ccap_countable_income", "BEM 503"), + ("MN", "mn_ccap_gross_unearned_income", "Minn. Stat. 256P.06"), + ("MN", "mn_mfip_gross_unearned_income", "Minn. Stat. 256P.06"), + ("MO", "mo_ccs_countable_income", "5 CSR 25-200.050"), + ("MO", "mo_snc_countable_income", "SNC manual 0610.005"), + ("MS", "ms_ccpp_countable_income", "CCPP policy manual"), + ("MT", "mt_tanf_gross_unearned_income_person", "TANF manual 501-1"), + ("MT", "mt_ccap_countable_income", "Child care manual CC 2-6"), + ("NC", "nc_lieap_gross_income_person", "EP-300 300.09 and FNS 300.01 B"), + ("NC", "nc_tanf_countable_gross_unearned_income", "WF-114"), + ("ND", "nd_tanf_gross_unearned_income", "Manual 400-19-55-20-15"), + ("ND", "nd_ccap_gross_income", "NDAC 75-02-01.3-08"), + ("NH", "nh_ccap_countable_income", "He-C 6910.06"), + ("NJ", "nj_liheap_countable_income_person", "LIHEAP handbook 2.3.D"), + ("NJ", "nj_ccap_countable_income", "FFY 2025-2027 CCDF plan"), + ("NV", "nv_ccdp_countable_income", "Child care manual 306"), + ("OK", "ok_ccs_gross_income", "OAC 340:40-7-11(c)(2)"), + ("OR", "or_erdc_countable_income", "OAR 414-175-0035(52)"), + ("PA", "pa_ccw_countable_income", "55 Pa. Code ch. 3042 app. A, part I(F)"), + ("RI", "ri_ccap_countable_income", "218-RICR-20-00-4"), + ("SC", "sc_ccap_countable_income", "CCS policy manual"), + ("SD", "sd_cca_gross_income", "CCA subsidy manual, section 4"), + ("TX", "tx_fpp_countable_unearned_income", "FPP manual 9000"), + ("TX", "tx_ceap_countable_income_person", "10 TAC 6.4"), + ("UT", "ut_ccap_gross_income", "Utah Admin. Code R986-200-235(2)(a)"), + ("VA", "va_ccsp_countable_income", "8 VAC 20-790-40(H)"), + ("VT", "vt_ccfap_countable_income", "CCFAP regulations"), + ("WA", "wa_wccc_countable_income", "WAC 110-15-0060(8)"), + ("WA", "wa_eceap_family_income", "DCYF 05-006, section 9"), + ("WI", "wi_shares_countable_income", "Wisconsin Shares handbook"), + ("WV", "wv_ccap_countable_income", "Child care policy 5.2.4"), + ("WY", "wy_power_gross_unearned_income", "SNAP and POWER manual"), + ("WY", "wy_ccap_gross_income", "Child care rules ch. 1, app. B"), + # County programs. + ("CA", "ca_ala_general_assistance_countable_income_person", "GA regulations"), + ("CA", "ca_oc_general_relief_gross_unearned_income", "GR manual section 70.1"), + ("CA", "ca_smc_general_assistance_countable_income", "GA fact sheet"), + ("CA", "la_general_relief_gross_income", "GR policy 44-101"), + ("CA", "ca_la_ez_save_countable_income", "EZ-SAVE income definition"), + ("CA", "ca_riv_liheap_countable_income", "2025 LIHEAP application"), + ("CA", "ca_riv_share_countable_income", "SHARE program page"), +] + + +# LIHEAP and energy programs whose formulas read Social Security directly and +# take railroad benefits from the program's unearned income list, which already +# named them. R1 confirms the benefits reach the total. +ALREADY_COUNTED = [ + ("CO", "co_leap_countable_income_person", "LEAP income list"), + ("CT", "ct_ceap_countable_income", "CEAP income list"), + ("GA", "ga_liheap_income", "LIHEAP income list"), + ("IA", "ia_liheap_countable_unearned_income", "LIHEAP Appendix C"), + ("ID", "id_liheap_countable_income", "LIHEAP income list"), + ("IN", "in_eap_income", "EAP income list"), + ("KY", "ky_liheap_income", "LIHEAP income list"), + ("LA", "la_liheap_countable_income", "LIHEAP income list"), + ("MT", "mt_liheap_countable_income", "LIHEAP income list"), + ("NV", "nv_liheap_countable_income", "LIHEAP income list"), + ("OH", "oh_liheap_countable_income", "HEAP income list"), + ("SD", "sd_liheap_countable_income", "LIEAP income list"), + ("TN", "tn_liheap_countable_income", "LIHEAP income list"), + ("WA", "wa_liheap_countable_income", "LIHEAP income list"), + ("WY", "wy_lieap_countable_income", "LIEAP income list"), +] + +# These totals count cash aid (ca_tanf, hi_tanf, ks_tanf or tanf) that falls as +# income rises, so a benefit the aid program counts removes aid from a household +# on the program. Wages above the aid programs' limits keep the aid at zero with and +# without the benefit. +WAGES_ABOVE_AID_LIMITS = {"employment_income": 60_000} +BASE_INPUTS = { + "ca_child_care_countable_income": WAGES_ABOVE_AID_LIMITS, + "ca_cpuc_countable_income": WAGES_ABOVE_AID_LIMITS, + "ca_la_ez_save_countable_income": WAGES_ABOVE_AID_LIMITS, + "ca_riv_liheap_countable_income": WAGES_ABOVE_AID_LIMITS, + "ca_riv_share_countable_income": WAGES_ABOVE_AID_LIMITS, + "hi_ccap_countable_income": WAGES_ABOVE_AID_LIMITS, + "ks_liheap_countable_unearned_income": WAGES_ABOVE_AID_LIMITS, + "la_general_relief_gross_income": WAGES_ABOVE_AID_LIMITS, + "wa_eceap_family_income": WAGES_ABOVE_AID_LIMITS, +} + +COUNTIES = { + "ca_oc_general_relief_gross_unearned_income": "ORANGE_COUNTY_CA", + "ca_smc_general_assistance_countable_income": "SAN_MATEO_COUNTY_CA", + "la_general_relief_gross_income": "LOS_ANGELES_COUNTY_CA", + "ca_la_ez_save_countable_income": "LOS_ANGELES_COUNTY_CA", + "ca_riv_liheap_countable_income": "RIVERSIDE_COUNTY_CA", + "ca_riv_share_countable_income": "RIVERSIDE_COUNTY_CA", +} + + +def household(state, inputs, county=None): + """A single 40-year-old parent with one child, as every program's rules apply.""" + people = { + "parent": {"age": {YEAR: 40}, **{k: {YEAR: v} for k, v in inputs.items()}}, + "child": {"age": {YEAR: 5}}, + } + members = list(people) + home = {"members": members, "state_code": {YEAR: state}} + if county: + home["county_str"] = {YEAR: county} + return { + "people": people, + "tax_units": {"tax_unit": {"members": members}}, + "families": {"family": {"members": members}}, + "marital_units": { + "parent_unit": {"members": ["parent"]}, + "child_unit": {"members": ["child"]}, + }, + "spm_units": {"spm_unit": {"members": members}}, + "households": {"household": home}, + } + + +def income(state, variable, inputs): + """The program's income for the parent, at the variable's own period.""" + period = YEAR if system.variables[variable].definition_period == "year" else MONTH + situation = household( + state, {**BASE_INPUTS.get(variable, {}), **inputs}, COUNTIES.get(variable) + ) + values = Simulation(situation=situation).calculate(variable, period) + return float(values[0]) + + +@lru_cache(maxsize=None) +def base_income(state, variable): + return income(state, variable, {}) + + +def received(variable, amount): + """An annual amount at the variable's period.""" + return ( + amount + if system.variables[variable].definition_period == "year" + else amount / 12 + ) + + +AMOUNTS = st.floats(min_value=1, max_value=200_000).map(lambda x: round(x, 2)) +R1_ROWS = PROGRAMS + ALREADY_COUNTED + + +@pytest.mark.parametrize("state, variable, rule", R1_ROWS, ids=[r[1] for r in R1_ROWS]) +@given(AMOUNTS) +@settings(max_examples=5, deadline=None) +def test_railroad_benefits_count_as_received(state, variable, rule, amount): + """R1. The whole benefit counts, from the first dollar.""" + change = income(state, variable, {"railroad_benefits": amount}) - base_income( + state, variable + ) + assert change == pytest.approx(received(variable, amount), abs=0.05), rule + + +# Programs whose modeled lists have no pension line, so R2 doesn't apply. Illinois +# marks pension_income as exempt in its TANF list; the other lists don't name it. +NO_PENSION_LINE = { + "ca_ala_general_assistance_countable_income_person", + "ca_tanf_other_unearned_income", + "il_aabd_gross_unearned_income", + "il_hbwd_countable_unearned_income", + "il_tanf_gross_unearned_income", + "la_general_relief_gross_income", +} +PENSION_PROGRAMS = [row for row in PROGRAMS if row[1] not in NO_PENSION_LINE] + + +@pytest.mark.parametrize( + "state, variable, rule", PENSION_PROGRAMS, ids=[r[1] for r in PENSION_PROGRAMS] +) +@given(AMOUNTS) +@settings(max_examples=5, deadline=None) +def test_railroad_benefits_count_as_a_pension_would(state, variable, rule, amount): + """R2. Railroad benefits and a pension of the same size count the same.""" + railroad = income(state, variable, {"railroad_benefits": amount}) + pension = income(state, variable, {"pension_income": amount}) + assert railroad == pytest.approx(pension, abs=0.05), rule + + +SOCIAL_SECURITY = { + "social_security", + "social_security_dependents", + "social_security_disability", + "social_security_retirement", + "social_security_survivors", + "tax_exempt_social_security", +} + +# Each railroad amount and the dollars it holds: the taxable and tax-free tier 1 +# social security equivalent benefits, and the taxable and tax-free rest of the +# annuity. PolicyEngine/policyengine-us#10696 splits railroad_benefits into the +# tier 1 amount and the two parts of the rest, and defines the tax amounts. +TIER_1_TAXABLE = "taxable_railroad_social_security_equivalent_benefits" +TIER_1_TAX_FREE = "tax_exempt_railroad_social_security_equivalent_benefits" +REST_TAXABLE = "taxable_railroad_pension_benefits" +REST_TAX_FREE = "tax_exempt_railroad_pension_benefits" +RAILROAD_DOLLARS = { + "railroad_benefits": { + TIER_1_TAXABLE, + TIER_1_TAX_FREE, + REST_TAXABLE, + REST_TAX_FREE, + }, + "railroad_social_security_equivalent_benefits": { + TIER_1_TAXABLE, + TIER_1_TAX_FREE, + }, + "tax_unit_railroad_social_security_equivalent_benefits": { + TIER_1_TAXABLE, + TIER_1_TAX_FREE, + }, + "tax_unit_railroad_social_security_equivalent_benefits_for_taxability": { + TIER_1_TAXABLE, + TIER_1_TAX_FREE, + }, + "taxable_railroad_benefits": {TIER_1_TAXABLE, REST_TAXABLE}, + "tax_unit_taxable_railroad_social_security_equivalent_benefits": {TIER_1_TAXABLE}, + TIER_1_TAXABLE: {TIER_1_TAXABLE}, + TIER_1_TAX_FREE: {TIER_1_TAX_FREE}, + REST_TAXABLE: {REST_TAXABLE}, + REST_TAX_FREE: {REST_TAX_FREE}, +} +# A dependent's own return taxes the dependent's tier 1 benefits, so this amount +# and the filers' taxable tier 1 amount hold different people's dollars. +RAILROAD = set(RAILROAD_DOLLARS) | { + "dependent_taxable_railroad_social_security_equivalent_benefits" +} + +SIGNAL = ( + "Reads receipt of a benefit as a yes-or-no test, not an income amount. " + "railroad_benefits doesn't separate disability annuities, so it can't stand " + "in for Social Security disability in a disability test." +) +SOCIAL_SECURITY_ONLY = "The rule or list covers Social Security benefits alone." +DISABILITY_ONLY = ( + "The list holds disability income, and railroad_benefits doesn't separate " + "railroad disability annuities. CalWORKs counts railroad benefits with its " + "other unearned income (ca_tanf_other_unearned_income, an R1 row)." +) +MINOR_SOCIAL_SECURITY_OR_DISABILITY = ( + "The guide counts a minor's Social Security or disability income. It names " + "Social Security, not railroad benefits, and a minor's railroad annuity pays " + "a child or survivor, not a disabled worker." +) +UNRESOLVED = "The cited sources don't settle whether railroad benefits count." +_STATES = "parameters/gov/states/" +_COUNTY = "parameters/gov/local/ca/" +_STATE_CODE = "variables/gov/states/" + +# Readers outside the tax code that leave railroad benefits out, and why. A +# variable whose formula reads Social Security directly and takes railroad benefits +# from a list passes through ALREADY_COUNTED instead. +NO_RAILROAD_INCOME = { + _COUNTY + "cc/general_assistance/countable_income/sources.yaml": UNRESOLVED, + _COUNTY + "riv/general_relief/income/sources/unearned.yaml": UNRESOLVED, + _STATES + "ca/cdss/tanf/cash/income/sources/db_unearned.yaml": DISABILITY_ONLY, + # An exemption list: counting railroad benefits here would exempt them. + _STATES + "ca/chhs/wdp/eligibility/income/sources/disability.yaml": ( + "This list exempts disability income, and railroad_benefits doesn't " + "separate disability annuities, so every railroad payment counts." + ), + _STATES + "in/fssa/ccdf/income/sources.yaml": UNRESOLVED, + _STATES + + "mn/dcyf/ccap/income/countable_income/rsdi_sources.yaml": SOCIAL_SECURITY_ONLY, + _STATES + "ms/dom/wd/eligibility/income/sources/unearned.yaml": UNRESOLVED, + _STATES + "nc/ncdhhs/scca/income/sources.yaml": UNRESOLVED, + _STATES + "sc/oeo/liheap/income/minor_unearned_sources.yaml": ( + MINOR_SOCIAL_SECURITY_OR_DISABILITY + ), + # RCW 84.36.383(7)(g) counts Social Security and railroad retirement benefits + # that AGI leaves out. The tax-free railroad amounts come with the federal + # treatment in PolicyEngine/policyengine-us#10696, and + # PolicyEngine/policyengine-us#10722 adds them to this list. + _STATES + "wa/dor/property_tax_exemption/senior_disabled/income/sources.yaml": ( + "Main has no tax-free railroad amount for this list to add." + ), + "variables/gov/local/ca/cc/ga/eligibility/" + "ca_cc_general_assistance_eligible_person.py": SIGNAL, + _STATE_CODE + "az/des/liheap/points/az_liheap_energy_need_points.py": SIGNAL, + _STATE_CODE + "ca/chhs/wdp/eligibility/ca_wdp_disability_eligible.py": SIGNAL, + _STATE_CODE + "dc/dhs/ccsp/eligibility/qualified_activity_or_need/" + "dc_ccsp_qualified_need_eligible.py": SIGNAL, + _STATE_CODE + "hi/dhs/liheap/hi_liheap_points.py": SIGNAL, + _STATE_CODE + "il/hfs/hbwd/eligibility/il_hbwd_disability_eligible.py": SIGNAL, + _STATE_CODE + "in/ihcda/eap/in_eap.py": SIGNAL, + _STATE_CODE + "la/lhc/liheap/la_liheap.py": SIGNAL, + _STATE_CODE + "md/dhs/fia/paa/md_paa_eligible.py": SIGNAL, + _STATE_CODE + "ms/dom/wd/eligibility/ms_wd_disability_eligible.py": SIGNAL, + _STATE_CODE + "nm/hca/liheap/nm_liheap.py": SIGNAL, + _STATE_CODE + "pa/dhs/liheap/payment/pa_liheap_supplement.py": SIGNAL, + _STATE_CODE + "va/dss/eap/va_eap_disabled.py": SIGNAL, + _STATE_CODE + "wa/dor/property_tax_exemption/senior_disabled/" + "wa_pte_retired_due_to_disability.py": SIGNAL, + _STATE_CODE + "wi/dhs/ssp/wi_ssp_countable_for_hud.py": SIGNAL, + _STATE_CODE + "wi/doa/wheap/wi_wheap_household_size.py": SIGNAL, +} + + +def string_lists(node): + """Every list of strings in a parsed parameter file.""" + if isinstance(node, dict): + for value in node.values(): + yield from string_lists(value) + elif isinstance(node, list): + if all(isinstance(item, str) for item in node): + yield node + else: + for value in node: + yield from string_lists(value) + + +def parameter_lists(text): + """Lists in a parameter file. BaseLoader keeps year-zero dates as strings.""" + return list(string_lists(yaml.load(text, Loader=yaml.BaseLoader))) + + +def code_strings(nodes, docstrings): + """String constants under the nodes, leaving out docstrings and comments.""" + return { + node.value + for root in nodes + for node in ast.walk(root) + if isinstance(node, ast.Constant) + and isinstance(node.value, str) + and id(node) not in docstrings + } + + +def code_scopes(text): + """String constants of the module's top level, each function and each class body.""" + tree = ast.parse(text) + docstrings = { + id(node.body[0].value) + for node in ast.walk(tree) + if isinstance(node, (ast.Module, ast.ClassDef, ast.FunctionDef)) + and node.body + and isinstance(node.body[0], ast.Expr) + and isinstance(node.body[0].value, ast.Constant) + } + functions = (ast.FunctionDef, ast.AsyncFunctionDef, ast.Lambda) + yield code_strings( + [ + child + for child in tree.body + if not isinstance(child, (*functions, ast.ClassDef)) + ], + docstrings, + ) + for node in ast.walk(tree): + if isinstance(node, functions): + yield code_strings([node], docstrings) + elif isinstance(node, ast.ClassDef): + body = [child for child in node.body if not isinstance(child, functions)] + yield code_strings(body, docstrings) + + +def lacks_railroad(names): + """Whether names count Social Security and no railroad benefit.""" + return bool(SOCIAL_SECURITY & set(names)) and not RAILROAD & set(names) + + +def reads_without_railroad(path, text): + """Whether a file reads Social Security and leaves railroad benefits out.""" + if path.suffix == ".yaml": + return any(lacks_railroad(names) for names in parameter_lists(text)) + return any(lacks_railroad(names) for names in code_scopes(text)) + + +def readers_without_railroad(): + """Non-tax state and county files that read Social Security without railroad benefits.""" + found = set() + for tree, suffix in (("parameters/gov", ".yaml"), ("variables/gov", ".py")): + for area in ("states", "local"): + for path in (PACKAGE / tree / area).rglob(f"*{suffix}"): + relative = path.relative_to(PACKAGE).as_posix() + if "/tax/" in relative: + continue + text = path.read_text(encoding="utf8") + if "social_security" in text and reads_without_railroad(path, text): + found.add(relative) + return found + + +def test_each_social_security_reader_counts_railroad_benefits_or_says_why(): + """R3. Tax provisions read the three railroad parts, so this test leaves them out.""" + checked_by_r1 = {variable for _, variable, _ in R1_ROWS} + found = { + path + for path in readers_without_railroad() + if Path(path).stem not in checked_by_r1 + } + unexplained = sorted(found - set(NO_RAILROAD_INCOME)) + assert not unexplained, ( + "These read Social Security and leave out railroad_benefits. Read the rule, " + "then add railroad_benefits or add the file to NO_RAILROAD_INCOME with the " + f"reason: {unexplained}" + ) + stale = sorted(set(NO_RAILROAD_INCOME) - found) + assert not stale, f"These no longer leave railroad_benefits out: {stale}" + + +def counts_railroad_twice(names): + """Whether two entries of a list hold the same railroad dollars.""" + dollars = [part for name in names for part in RAILROAD_DOLLARS.get(name, ())] + return len(dollars) != len(set(dollars)) + + +def test_no_list_holds_railroad_benefits_twice(): + """R4. Two entries that hold the same dollars would count them twice.""" + repeated = [] + for path in (PACKAGE / "parameters" / "gov").rglob("*.yaml"): + text = path.read_text(encoding="utf8") + if "railroad" in text and any( + counts_railroad_twice(names) for names in parameter_lists(text) + ): + repeated.append(path.relative_to(PACKAGE).as_posix()) + repeated += [ + name + for name, variable in system.variables.items() + if isinstance(variable.adds, (list, tuple)) + and counts_railroad_twice(list(variable.adds)) + ] + assert not repeated, repeated + + +# Federal totals, lists and tests that state and county programs read. Each one +# counts Social Security, or tests for Social Security disability, and leaves +# railroad benefits out, so the programs that read it leave them out too. +FEDERAL_GAPS = { + # Issue PolicyEngine/policyengine-us#10551 covers the TANF, SNAP and school + # meals lists. Under 7 CFR 273.9(b)(2)(ii), state SNAP agencies count + # "Annuities; pensions; retirement, veteran's, or disability benefits". + # Under BEM 503, Michigan's MDHHS counts for FIP, its TANF program, the + # "gross benefit amount" that the Railroad Retirement Board pays. + "tanf_gross_unearned_income": "TANF baseline list: 64 TANF variables in 37 states", + "gov.usda.snap.income.sources.unearned": "SNAP unearned list: Nebraska LIHEAP", + "school_meal_fpg_ratio": "School meals list: Texas CSFP, California CVRP", + # Under IRC 36B(d)(2)(B)(iii), taxpayers add to AGI the untaxed share of + # "social security benefits (as defined in section 86(d))", tier 1 included. + # On main, neither adjusted_gross_income nor the MAGI additions list counts + # railroad benefits. PolicyEngine/policyengine-us#10696 adds the taxable + # amounts to AGI and the tax-free tier 1 amount to the additions list. + "medicaid_magi": "MAGI: Colorado CHP+, Healthy Michigan, Montana HELP", + "tax_unit_medicaid_income_level": "MAGI ratio: 17 CHIP premiums, IN HIP, MI, MT", + "medicaid_income_level": "MAGI ratio: AR, CO, IL, MS, OR and WA programs", + "aca_magi": "ACA MAGI: CA, MD, NM and VT premium help", + "aca_magi_fraction": "ACA MAGI ratio: premium help in nine states", + # Under 7 U.S.C. 2012(j)(7), SNAP agencies treat a railroad disability + # annuitant as disabled, and railroad_benefits doesn't separate disability + # annuities from the others. + "is_usda_disabled": "SNAP disability test: NC, NV, NY and OK energy aid", + # Under 42 U.S.C. 426(b)(2)(B), a person under 65 gets Medicare Part A + # after 24 months as "a disabled qualified railroad retirement beneficiary". + # is_medicare_eligible tests age 65, or Social Security disability for 24 + # months, and the premiums, enrollment and Medicaid tests below read it. + "is_medicare_eligible": "Medicare test: IL SMIB and fares, MBTA, MS HMW, DART", + "base_part_b_premium": "Part B premium: IL SMIB", + "medicare_enrolled": "Medicare enrollment: RI Medicaid third-party coverage", + "medicare_part_a_premium": "Part A premium: energy aid income in eight states", + "medicare_part_b_premium": "Part B premium: energy aid in 28 states, WA exemption", + "is_adult_for_medicaid": "Medicaid adult test: MS HMW other coverage", + "is_medicaid_eligible": "Medicaid test: CA CCHIP, CO CHP+", + "medicaid_optional_senior_or_disabled_countable_income": "MS HMW income", +} +# Formulas R5 follows past each federal variable it starts from. +FEDERAL_FORMULA_STEPS = 2 + + +def is_state_or_local(relative): + """Whether a path below the package sits in the states or local tree.""" + parts = Path(relative).parts + return len(parts) > 2 and parts[2] in ("states", "local") + + +def federal_lists_without_railroad(): + """Dotted paths of federal parameter lists that count Social Security alone.""" + found = set() + for path in (PACKAGE / "parameters" / "gov").rglob("*.yaml"): + relative = path.relative_to(PACKAGE).as_posix() + if is_state_or_local(relative): + continue + text = path.read_text(encoding="utf8") + if "social_security" in text and reads_without_railroad(path, text): + parts = path.relative_to(PACKAGE / "parameters").with_suffix("").parts + found.add(".".join(parts)) + return found + + +def parameter_paths(node, aliases): + """The dotted paths from gov onward that an expression can name.""" + parts = [] + while isinstance(node, ast.Attribute): + parts.append(node.attr) + node = node.value + parts.reverse() + if "gov" in parts: + return {".".join(parts[parts.index("gov") :])} + if isinstance(node, ast.Name): + return {".".join([base, *parts]) for base in aliases.get(node.id, ())} + return set() + + +def scope_nodes(scope): + """The nodes of a module or function, without the bodies of functions inside.""" + pending = list(ast.iter_child_nodes(scope)) + while pending: + node = pending.pop() + yield node + if not isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + pending.extend(ast.iter_child_nodes(node)) + + +def scope_paths(scope, outer): + """The parameter paths a scope reads, through the names it assigns paths to.""" + aliases = {name: set(paths) for name, paths in outer.items()} + nodes = list(scope_nodes(scope)) + assignments = [ + (node.targets[0].id, node.value) + for node in nodes + if isinstance(node, ast.Assign) + and len(node.targets) == 1 + and isinstance(node.targets[0], ast.Name) + ] + grew = True + while grew: + grew = False + for name, value in assignments: + new = parameter_paths(value, aliases) - aliases.get(name, set()) + if new: + aliases.setdefault(name, set()).update(new) + grew = True + paths = set() + for node in nodes: + if isinstance(node, ast.Attribute): + paths |= parameter_paths(node, aliases) + return paths, aliases + + +def reads_a_list(tree, lists): + """The listed parameter paths that parsed code reads or names in a string.""" + paths, aliases = scope_paths(tree, {}) + for node in ast.walk(tree): + if isinstance(node, (ast.FunctionDef, ast.AsyncFunctionDef)): + paths |= scope_paths(node, aliases)[0] + return lists & (paths | code_strings([tree], set())) + + +def federal_variables(): + """Each federal variable: its adds, the variables it names, its code and scopes.""" + found = {} + for name, variable in system.variables.items(): + try: + source = Path(inspect.getfile(type(variable))).resolve() + relative = source.relative_to(PACKAGE).as_posix() + except (TypeError, ValueError): + continue + if is_state_or_local(relative): + continue + adds = variable.adds + text = source.read_text(encoding="utf8") + tree = ast.parse(text) + names = code_strings([tree], set()) + if isinstance(adds, (list, tuple)): + names |= set(adds) + scopes = list(code_scopes(text)) + found[name] = (adds, names & set(system.variables), tree, scopes) + return found + + +def counts_social_security_alone(adds, names, tree, scopes, lists): + """Whether a federal variable takes Social Security with no railroad amount.""" + if isinstance(adds, str): + listed = adds in lists + else: + listed = isinstance(adds, (list, tuple)) and lacks_railroad(adds) + if (listed or reads_a_list(tree, lists)) and not names & RAILROAD: + return True + return any(lacks_railroad(scope) for scope in scopes) + + +def federal_sources_without_railroad(): + """Federal lists and variables that count Social Security without railroad.""" + lists = federal_lists_without_railroad() + variables = federal_variables() + found = { + name + for name, (adds, names, tree, scopes) in variables.items() + if name not in SOCIAL_SECURITY + and counts_social_security_alone(adds, names, tree, scopes, lists) + } + for _ in range(FEDERAL_FORMULA_STEPS): + found |= { + name + for name, (_, names, _, _) in variables.items() + if name not in SOCIAL_SECURITY and names & found and not names & RAILROAD + } + return lists, found + + +def inherited_federal_sources(): + """Federal sources without railroad benefits that state or county code reads.""" + lists, variables = federal_sources_without_railroad() + found = set() + for tree, suffix in (("parameters/gov", ".yaml"), ("variables/gov", ".py")): + for area in ("states", "local"): + for path in (PACKAGE / tree / area).rglob(f"*{suffix}"): + if "/tax/" in path.relative_to(PACKAGE).as_posix(): + continue + text = path.read_text(encoding="utf8") + if suffix == ".yaml": + for names in parameter_lists(text): + found |= variables & set(names) + continue + parsed = ast.parse(text) + found |= variables & code_strings([parsed], set()) + found |= reads_a_list(parsed, lists) + return found + + +def test_state_programs_that_read_a_federal_list_without_railroad_are_known(): + """R5. A program that reads a federal total, list or test takes its sources.""" + inherited = inherited_federal_sources() + unknown = sorted(inherited - set(FEDERAL_GAPS)) + assert not unknown, ( + "State or county code reads these federal sources, which count Social " + "Security and leave out railroad benefits. Read the program's rule, then " + f"count railroad benefits or add the source to FEDERAL_GAPS: {unknown}" + ) + stale = sorted(set(FEDERAL_GAPS) - inherited) + assert not stale, ( + "No state or county code reads these as sources without railroad " + f"benefits; remove them: {stale}" + ) diff --git a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py index 1fe256cb6fd..8e24a06d003 100644 --- a/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py +++ b/policyengine_us/variables/gov/states/mo/dss/ssp/mo_snc_countable_income.py @@ -17,4 +17,4 @@ class mo_snc_countable_income(Variable): "https://dssmanuals.mo.gov/supplemental-nursing-care/0610-000-00/0610-005-00/", "https://dssmanuals.mo.gov/supplemental-nursing-care/0610-000-00/0610-025-00/", ) - adds = ["market_income", "social_security", "ssi"] + adds = ["market_income", "social_security", "railroad_benefits", "ssi"]