Skip to content
Merged
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/trustees-wage-base-cap.fixed.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
Keep the Social Security wage base on each Trustees assumption's path when a reform changes only the payroll cap.
16 changes: 13 additions & 3 deletions policyengine_us/parameters/uprating_extensions.py
Original file line number Diff line number Diff line change
Expand Up @@ -820,14 +820,24 @@ def extend_social_security_payroll_cap(
def extend_social_security_wage_indexed_parameters(
parameters: ParameterNode,
end_year: int,
*,
cap: Optional[Parameter] = None,
) -> None:
"""Extend SSA wage-indexed benefit parameters with statutory lag and rounding."""
"""Extend SSA wage-indexed benefit parameters with statutory lag and rounding.

The wage base for each year from 2027 is set to the contribution and
benefit base. `cap` defaults to `gov.irs.payroll.social_security.cap`.
Pass another parameter that holds the base, such as a copy of it, to read
the wage base from that parameter instead, so a reform that changes only
the payroll cap doesn't move the wage base when this runs after it.
"""
ssa = parameters.gov.ssa
social_security = ssa.social_security
nawi = ssa.nawi

wage_base = social_security.wage_base
payroll_cap = parameters.gov.irs.payroll.social_security.cap
if cap is None:
cap = parameters.gov.irs.payroll.social_security.cap
qc_threshold = social_security.quarters_of_coverage_threshold
earnings_test = social_security.earnings_test
pia = social_security.pia.formula_factors
Expand All @@ -839,7 +849,7 @@ def extend_social_security_wage_indexed_parameters(

wage_base.update(
period=period,
value=payroll_cap(f"{year}-01-01"),
value=cap(f"{year}-01-01"),
)

qc_threshold.update(
Expand Down
34 changes: 31 additions & 3 deletions policyengine_us/reforms/ssa/trustees_2025.py
Original file line number Diff line number Diff line change
Expand Up @@ -201,6 +201,34 @@ def apply_trustees_2025_economic_assumptions(
last_projected_year=2035,
end_year=end_year,
)
# The wage base and the other wage-indexed amounts follow the cap and the
# NAWI, so re-derive them from the Trustees path as well (#10452).
extend_social_security_wage_indexed_parameters(parameters, end_year=end_year)
# Extend the owners' draw copy on the same rule from its own values, not
# from the live cap, so a policy reform's cap can't become the copy's
# no-decrease floor when the reform set runs again.
current_law_cap = parameters.gov.contrib.ssa.owners_draw.current_law_cap
extend_social_security_payroll_cap(
parameters,
last_projected_year=2035,
end_year=end_year,
cap=current_law_cap,
)
# Extend the benefit wage base on the same rule from its own values
# through 2035. The rule reads the wage base's 1994 base, its value for
# the year before and the NAWI, but neither the live cap nor the copy, so
# a reform that changes only one of those doesn't move the wage base when
# the reform set runs again. A reform that means to move it sets the wage
# base itself.
wage_base = parameters.gov.ssa.social_security.wage_base
extend_social_security_payroll_cap(
parameters,
last_projected_year=2035,
end_year=end_year,
cap=wage_base,
)
# The other wage-indexed amounts follow the NAWI, so re-derive them from
# the Trustees path as well (#10452). This reads the wage base from
# itself, so it rewrites the wage base with its own values.
extend_social_security_wage_indexed_parameters(
parameters,
end_year=end_year,
cap=wage_base,
)
27 changes: 24 additions & 3 deletions policyengine_us/reforms/ssa/trustees_2026.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,10 @@
- the base's owners' draw copy, ``gov.contrib.ssa.owners_draw.current_law_cap``,
on the same rule from the copy's own values through 2027, so a policy reform
that changes the live base doesn't reach it;
- the wage base, quarter-of-coverage threshold, earnings-test exempt amounts,
PIA bend points and SGA amounts, from 2027;
- the wage base on the same rule from its own values through 2027, so a
policy reform that changes the live base or the copy doesn't reach it;
- the quarter-of-coverage threshold, earnings-test exempt amounts, PIA bend
points and SGA amounts, from 2027;
- every parameter whose ``uprating`` metadata names either path. Core's
``uprate_parameters`` writes their values when the system builds, so a later
change to a path doesn't reach them. The module derives each again as core
Expand Down Expand Up @@ -480,7 +482,26 @@ def apply_trustees_2026_economic_assumptions(
end_year=end_year,
cap=parameters.gov.contrib.ssa.owners_draw.current_law_cap,
)
extend_social_security_wage_indexed_parameters(parameters, end_year=end_year)
# Extend the benefit wage base on the same rule from its own values
# through 2027. The rule reads the wage base's 1994 base, its value for
# the year before and the NAWI, but neither the live cap nor the copy, so
# a reform that changes only one of those doesn't move the wage base when
# the reform set runs again. A reform that means to move it sets the wage
# base itself.
wage_base = parameters.gov.ssa.social_security.wage_base
extend_social_security_payroll_cap(
parameters,
last_projected_year=TRUSTEES_2026_LAST_DETERMINED_CAP_YEAR,
end_year=end_year,
cap=wage_base,
)
# Reads the wage base from itself, so this rewrites the wage base with its
# own values and extends the other wage-indexed amounts on the NAWI.
extend_social_security_wage_indexed_parameters(
parameters,
end_year=end_year,
cap=wage_base,
)
apply_trustees_2026_path_readers(parameters, end_year=end_year)


Expand Down
Loading
Loading