Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions changelog.d/vet-rrb-income-totals.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Count veterans' benefits and Railroad Retirement Board benefits in household and SPM benefits, and in the copies of those lists that the BOOST, End Child Poverty Act and Texas rebate reforms define, so both net incomes and SPM resources include them.
11 changes: 11 additions & 0 deletions policyengine_us/parameters/gov/household/household_benefits.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,10 @@ values:
- educational_assistance
- financial_assistance
- survivor_benefits
# Census money income counts veterans' payments, and railroad retirement
# as a government transfer (P60-200 Appendix A).
- veterans_benefits
- railroad_benefits
- household_head_start_benefits
# One-time energy relief payments.
# Paid at the same time as the Alaska Permanent Fund Dividend,
Expand Down Expand Up @@ -52,6 +56,10 @@ values:
- educational_assistance
- financial_assistance
- survivor_benefits
# Census money income counts veterans' payments, and railroad retirement
# as a government transfer (P60-200 Appendix A).
- veterans_benefits
- railroad_benefits
- household_head_start_benefits
# One-time energy relief payments.
# Paid at the same time as the Alaska Permanent Fund Dividend,
Expand All @@ -72,3 +80,6 @@ metadata:
unit: list
period: year
label: Household benefits
reference:
- title: "Census Bureau, P60-200 Appendix A, Definitions and Explanations #page 3-4"
href: https://www2.census.gov/programs-surveys/cps/methodology/Appendix%20A%20P60-200%20income%20measurement%20definition.pdf#page=3
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ values:
# (a) Annuities, pensions, and other periodic payments.
- pension_income
- social_security
# NOTE: Household net income, SNAP, HUD annual income and other program
# income lists count social_security and ssi but not railroad_benefits, so
# an SSI recipient's railroad annuity drops out of them.
# NOTE: SNAP, HUD annual income and other program income lists count
# social_security and ssi but not railroad_benefits, so an SSI recipient's
# railroad annuity drops out of them.
# https://github.com/PolicyEngine/policyengine-us/issues/10551
- railroad_benefits
- disability_benefits
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,10 @@ def formula(household, period, parameters):
"educational_assistance",
"financial_assistance",
"survivor_benefits",
# Census money income counts veterans' payments, and railroad
# retirement as a government transfer (P60-200 Appendix A).
"veterans_benefits",
"railroad_benefits",
# Broadband subsidies.
"acp",
"ebb",
Expand Down Expand Up @@ -122,6 +126,10 @@ def formula(spm_unit, period, parameters):
"educational_assistance",
"financial_assistance",
"survivor_benefits",
# Census money income counts veterans' payments, and railroad
# retirement as a government transfer (P60-200 Appendix A).
"veterans_benefits",
"railroad_benefits",
# Broadband subsidies.
"acp",
"ebb",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ def formula(spm_unit, period, parameters):
"educational_assistance",
"financial_assistance",
"survivor_benefits",
# Census money income counts veterans' payments, and railroad
# retirement as a government transfer (P60-200 Appendix A).
"veterans_benefits",
"railroad_benefits",
# Broadband subsidies.
"acp",
"ebb",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ def formula(household, period, parameters):
"educational_assistance",
"financial_assistance",
"survivor_benefits",
# Census money income counts veterans' payments, and railroad
# retirement as a government transfer (P60-200 Appendix A).
"veterans_benefits",
"railroad_benefits",
# Broadband subsidies.
"acp",
"ebb",
Expand Down Expand Up @@ -150,6 +154,10 @@ def formula(spm_unit, period, parameters):
"educational_assistance",
"financial_assistance",
"survivor_benefits",
# Census money income counts veterans' payments, and railroad
# retirement as a government transfer (P60-200 Appendix A).
"veterans_benefits",
"railroad_benefits",
# Broadband subsidies.
"acp",
"ebb",
Expand Down
4 changes: 4 additions & 0 deletions policyengine_us/reforms/states/tx/rebate/tx_rebate.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,10 @@ def formula(spm_unit, period, parameters):
"educational_assistance",
"financial_assistance",
"survivor_benefits",
# Census money income counts veterans' payments, and railroad
# retirement as a government transfer (P60-200 Appendix A).
"veterans_benefits",
"railroad_benefits",
"spm_unit_energy_subsidy",
"tanf",
# Washington (WA) cash-assistance programs. wa_sfa and wa_rca
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1534,3 +1534,30 @@
salt_deduction: 21_728
itemized_taxable_income_deductions_reduction: 2_661
itemized_taxable_income_deductions: 19_067

# Form 1040 line 11b plus "any nontaxable items", veterans' benefits among
# them, sets the row (2025 Instructions for Schedule A, page 5). In the 2025
# table, a Texas family of one may deduct $931 in the $100,000-$120,000 row
# and $992 in the $120,000-$140,000 row (page 16). Veterans' benefits stay out of AGI
# under 38 U.S.C. 5301(a)(1) but raise this deduction for a filer who
# itemizes and deducts sales tax.
- name: A Texas filer with $100,000 of AGI uses the $100,000-$120,000 row, 2025
period: 2025
input:
state_code: TX
employment_income: 100_000
output:
adjusted_gross_income: 100_000
state_sales_tax_income_bracket: 10
state_sales_tax: 931

- name: $30,000 of veterans' benefits moves the same filer to the $120,000-$140,000 row, 2025
period: 2025
input:
state_code: TX
employment_income: 100_000
veterans_benefits: 30_000
output:
adjusted_gross_income: 100_000
state_sales_tax_income_bracket: 11
state_sales_tax: 992
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
- name: Veterans' benefits stay out of federal gross income.
period: 2026
input:
age: 40
veterans_benefits: 30_000
output:
# 38 U.S.C. 5301(a)(1): VA benefits are exempt from taxation.
irs_gross_income: 0
adjusted_gross_income: 0

- name: Veterans' benefits add nothing to a worker's federal gross income.
period: 2026
input:
age: 40
employment_income: 45_000
veterans_benefits: 20_000
output:
irs_gross_income: 45_000
adjusted_gross_income: 45_000
Original file line number Diff line number Diff line change
Expand Up @@ -173,3 +173,33 @@
output:
household_benefits: 2_000
spm_unit_benefits: 2_000

- name: Veterans' benefits and railroad retirement reach household benefits.
period: 2026
input:
people:
veteran:
age: 40
employment_income: 500_000
veterans_benefits: 12_000
railroad_annuitant:
age: 40
employment_income: 500_000
railroad_benefits: 30_000
tax_units:
veteran_unit:
members: [veteran]
railroad_unit:
members: [railroad_annuitant]
spm_units:
spm_unit:
members: [veteran, railroad_annuitant]
households:
household:
members: [veteran, railroad_annuitant]
state_code: TX
output:
# Census P60-200 Appendix A: veterans' payments are money income, and
# railroad retirement is a government transfer. 12,000 + 30,000 = 42,000.
household_benefits: 42_000
spm_unit_benefits: 42_000
Original file line number Diff line number Diff line change
Expand Up @@ -302,3 +302,33 @@
output:
spm_unit_benefits: [1_000, 1_000]
household_benefits: 2_000

- name: Each SPM unit counts its own veterans' and railroad retirement benefits.
period: 2026
input:
people:
veteran:
age: 40
employment_income: 500_000
veterans_benefits: 9_000
railroad_annuitant:
age: 40
employment_income: 500_000
railroad_benefits: 24_000
tax_units:
veteran_unit:
members: [veteran]
railroad_unit:
members: [railroad_annuitant]
spm_units:
veteran_unit:
members: [veteran]
railroad_unit:
members: [railroad_annuitant]
households:
household:
members: [veteran, railroad_annuitant]
state_code: FL
output:
spm_unit_benefits: [9_000, 24_000]
household_benefits: 9_000 + 24_000
4 changes: 4 additions & 0 deletions policyengine_us/tests/test_aggregate_list_integrity.py
Original file line number Diff line number Diff line change
Expand Up @@ -290,12 +290,16 @@ def test_reform_aggregates_keep_baseline_contrib_entries(
"ma_eaedc",
"nj_property_tax_relief",
"ny_drive_clean_rebate",
"railroad_benefits",
"state_property_tax_refunds",
"veterans_benefits",
},
"household_benefits": {
"ak_energy_relief",
"household_local_benefits",
"ny_drive_clean_rebate",
"railroad_benefits",
"veterans_benefits",
},
}

Expand Down
81 changes: 80 additions & 1 deletion policyengine_us/tests/test_household_spm_resource_parity.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
cash. Each total keeps its own list of what it adds, in two different files, and
the lists drift: a cash program reaches one total and not the other.

Seven invariants hold for every input.
Nine invariants hold for every input.

1. The list difference is exactly the documented one. Every program that one
total counts and the other does not appears below with its reason.
Expand All @@ -24,6 +24,12 @@
same amount. A state income tax credit list or state_property_tax_refunds
carries each payment into both totals, and the bill reductions documented
below reach neither, since neither total subtracts property tax.
8. For a household with no means-tested benefits to lose, a dollar of veterans'
or railroad benefits adds a dollar to both benefit totals and to market
income plus benefits.
9. Veterans' benefits never change federal gross income or AGI, and they leave
federal income tax unchanged for a filer under 65 without a disability who
doesn't itemize.

Child care stays out of this file: household_state_benefits and the SPM treat
subsidies differently on purpose, and #9405 and #10569 cover that difference.
Expand Down Expand Up @@ -601,3 +607,76 @@ def change(name):
expected = 0 if name in PROPERTY_TAX_BILL_REDUCTIONS else amount
assert household_change == pytest.approx(expected, abs=TOLERANCE), name
assert spm_change == pytest.approx(expected, abs=TOLERANCE), name


# Federal cash benefits, paid in every state to the person who receives them.
# Census P60-200 Appendix A counts veterans' payments as money income and
# railroad retirement as a government transfer, so both totals count them.
FEDERAL_CASH_BENEFITS = ("veterans_benefits", "railroad_benefits")


@settings(max_examples=30, deadline=None)
@given(
name=st.sampled_from(FEDERAL_CASH_BENEFITS),
amount=st.integers(min_value=1, max_value=60_000),
state=st.sampled_from(STATES),
)
@example(name="veterans_benefits", amount=12_000, state="TX")
@example(name="railroad_benefits", amount=30_000, state="VA")
def test_a_dollar_of_federal_cash_benefits_adds_a_dollar_to_both_totals(
name, amount, state
):
year = 2026
# Earnings this high rule out every means-tested response.
zero = Simulation(
situation=one_person(state, 500_000, year, **{name: ("person", 0)})
)
paid = Simulation(
situation=one_person(state, 500_000, year, **{name: ("person", amount)})
)
for names in (
("spm_unit_benefits",),
("household_benefits",),
SPM_RESOURCES,
HOUSEHOLD_RESOURCES,
):
change = total(paid, names, year) - total(zero, names, year)
assert change == pytest.approx(amount, abs=TOLERANCE), names


@settings(max_examples=20, deadline=None)
@given(
amount=st.integers(min_value=1, max_value=60_000),
earnings=st.integers(min_value=0, max_value=300_000),
state=st.sampled_from(STATES),
)
@example(amount=30_000, earnings=0, state="TX")
@example(amount=20_000, earnings=45_000, state="VT")
def test_veterans_benefits_stay_out_of_federal_gross_income(amount, earnings, state):
"""38 U.S.C. 5301(a)(1) exempts VA benefits from taxation.

The IRS still counts them as income for the optional sales tax tables
(state_sales_tax.yaml), so a filer who itemizes and deducts sales tax can
owe less federal income tax with them. The test checks income tax only
when neither household itemizes. The person is 40 and has no disability,
so the credit for the elderly or disabled doesn't apply. 26 U.S.C.
22(c)(3)(A)(i)(III) reduces that credit by VA pensions, and the model
doesn't yet.
"""
year = 2026
zero = Simulation(
situation=one_person(state, earnings, year, veterans_benefits=("person", 0))
)
paid = Simulation(
situation=one_person(
state, earnings, year, veterans_benefits=("person", amount)
)
)
for name in ("irs_gross_income", "adjusted_gross_income"):
assert total(paid, [name], year) == pytest.approx(
total(zero, [name], year), abs=TOLERANCE
), name
if not any(total(s, ["tax_unit_itemizes"], year) for s in (zero, paid)):
assert total(paid, ["income_tax"], year) == pytest.approx(
total(zero, ["income_tax"], year), abs=TOLERANCE
)
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,8 @@ class disability_benefits(Variable):
documentation = (
"Disability benefits from employment (not Social Security), except for "
"worker's compensation. Includes both the taxable and the tax-exempt part; "
"see taxable_disability_benefits and tax_exempt_disability_benefits."
"see taxable_disability_benefits and tax_exempt_disability_benefits. "
"VA disability compensation goes in veterans_benefits."
)
definition_period = YEAR
uprating = "calibration.gov.irs.soi.employment_income"
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,10 @@ class survivor_benefits(Variable):
value_type = float
entity = Person
label = "survivor benefits"
documentation = "Survivor benefits other than Social Security survivor benefits."
documentation = (
"Survivor benefits other than Social Security survivor benefits. VA "
"dependency and indemnity compensation goes in veterans_benefits, and "
"Railroad Retirement Board survivor annuities in railroad_benefits."
)
unit = USD
definition_period = YEAR
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,26 @@ class veterans_benefits(Variable):
entity = Person
label = "Veterans benefits"
unit = USD
documentation = "Veterans benefits from past military service."
documentation = (
"Tax-exempt payments for past military service: benefits the "
"Department of Veterans Affairs pays, such as disability compensation, "
"pensions, dependency and indemnity compensation to survivors, and "
"education allowances, and Combat-Related Special Compensation (CRSC), "
"which the military departments pay under 10 U.S.C. 1413a. "
"38 U.S.C. 5301(a)(1) exempts VA benefits from taxation, and IRS "
"Publication 525 lets retirees exclude CRSC from income, so federal "
"gross income leaves both out. Census money income counts VA payments "
"as veterans' payments, and household and SPM benefits include this "
"variable. Taxable military retirement pay goes in "
"military_retirement_pay instead. The model marks anyone with "
"veterans_benefits as a veteran (is_veteran), so set is_veteran to "
"false for a survivor or dependent whose VA payments go here."
)
definition_period = YEAR
uprating = "gov.ssa.uprating"
reference = (
"https://www.law.cornell.edu/uscode/text/38/5301#a_1",
"https://www.law.cornell.edu/uscode/text/10/1413a",
"https://www.irs.gov/pub/irs-prior/p525--2024.pdf#page=19",
"https://www2.census.gov/programs-surveys/cps/methodology/Appendix%20A%20P60-200%20income%20measurement%20definition.pdf#page=3",
)
Loading
Loading