This chapter presents the quantitative results of the analysis of eight Social Security benefit taxation reform options. All estimates represent changes in federal tax revenue, with projections extending through 2100.
We present conventional scoring estimates as our primary results, which incorporate labor supply behavioral responses based on Congressional Budget Office elasticities (doubled for workers aged 65+). Static estimates without behavioral responses are provided for comparison. Positive values indicate revenue increases (deficit reducing), while negative values indicate revenue losses (deficit increasing). All values are in billions of dollars.
# Import necessary libraries
import pandas as pd
import numpy as np
import plotly.express as px
import plotly.graph_objects as go
import warnings
warnings.filterwarnings('ignore')
# Define PolicyEngine's color palette
BLACK = "#000000"
BLUE_LIGHT = "#D8E6F3"
BLUE_PRIMARY = "#2C6496"
DARK_BLUE_HOVER = "#1d3e5e"
DARK_GRAY = "#616161"
DARKEST_BLUE = "#0C1A27"
GRAY = "#808080"
LIGHT_GRAY = "#F2F2F2"
MEDIUM_DARK_GRAY = "#D2D2D2"
MEDIUM_LIGHT_GRAY = "#BDBDBD"
WHITE = "#FFFFFF"
We present two types of conventional scoring estimates:
Conventional scoring (primary results) incorporates labor supply elasticities based on CBO estimates, with elasticities doubled for workers aged 65 and older based on meta-analysis findings. This captures behavioral responses to changes in effective tax rates.
Static scoring assumes no behavioral responses to policy changes, isolating the pure mechanical effect.
For most options, conventional and static estimates differ by less than 5%. The main exceptions are the Roth-style swap options (Options 5 and 6), which show larger differences (7-27%) due to the behavioral effects of taxing employer payroll contributions on labor supply decisions.
The following table presents the 10-year budgetary impact for each option. Conventional scoring (incorporating labor supply responses) represents our primary estimates, with static scoring (no behavioral responses) shown for comparison:
from IPython.display import display, HTML
# Calculate 10-year totals by reform and scoring type
ten_year = df[(df['year'] >= 2026) & (df['year'] <= 2035)].copy()
totals_10yr = ten_year.groupby(['reform_display', 'scoring_type'])['revenue_impact'].sum().reset_index()
# Data is already in billions, no conversion needed
# Pivot to show conventional and static side by side
pivot_10yr = totals_10yr.pivot(index='reform_display', columns='scoring_type', values='revenue_impact')
# Sort by option number
def extract_option_number(name):
return int(name.split(':')[0].replace('Option ', ''))
pivot_10yr['sort'] = pivot_10yr.index.map(extract_option_number)
pivot_10yr = pivot_10yr.sort_values('sort').drop('sort', axis=1)
# Calculate difference (showing how static differs from conventional)
pivot_10yr['Difference'] = pivot_10yr['static'] - pivot_10yr['dynamic']
pivot_10yr['% Diff'] = (pivot_10yr['Difference'] / pivot_10yr['dynamic'].abs() * 100).round(1)
# Reorder columns: Conventional first, then Static
pivot_10yr = pivot_10yr[['dynamic', 'static', 'Difference', '% Diff']]
pivot_10yr.columns = ['Conventional', 'Static', 'Difference', '% Diff']
# Format values
def format_billions(x):
if abs(x) < 0.5:
return "$0"
return f"${x:,.0f}" if x >= 0 else f"-${abs(x):,.0f}"
pivot_10yr_display = pivot_10yr.copy()
pivot_10yr_display['Conventional'] = pivot_10yr['Conventional'].apply(format_billions)
pivot_10yr_display['Static'] = pivot_10yr['Static'].apply(format_billions)
pivot_10yr_display['Difference'] = pivot_10yr['Difference'].apply(format_billions)
pivot_10yr_display['% Diff'] = pivot_10yr['% Diff'].apply(lambda x: f"{x:.1f}%")
pivot_10yr_display = pivot_10yr_display.reset_index()
pivot_10yr_display.columns.name = None
pivot_10yr_display = pivot_10yr_display.rename(columns={'reform_display': 'Policy Option'})
html_table = pivot_10yr_display.to_html(index=False, classes='dataframe')
display(HTML(html_table))
Loading...
The following chart visualizes the 10-year impacts, comparing static and conventional scoring:
The following table presents the complete 75-year cumulative fiscal impact for each reform option. These long-term projections reveal how policies that appear revenue-positive in standard 10-year budget windows may evolve over longer time horizons as demographic trends intensify.
# Calculate 75-year cumulative impacts for all reforms
windows = [
('10-Year', 2026, 2035),
('25-Year', 2026, 2050),
('50-Year', 2026, 2075),
('75-Year', 2026, 2100)
]
results = []
for window_name, start_year, end_year in windows:
window_data = df[(df['year'] >= start_year) & (df['year'] <= end_year)]
for scoring in ['dynamic', 'static']:
scoring_data = window_data[window_data['scoring_type'] == scoring]
totals = scoring_data.groupby('reform_display')['revenue_impact'].sum()
for reform in totals.index:
results.append({
'Window': window_name,
'Scoring': 'Conventional' if scoring == 'dynamic' else 'Static',
'Reform': reform,
'Impact': totals[reform]
})
results_df = pd.DataFrame(results)
# Create pivot table for conventional scoring (primary)
conventional_pivot = results_df[results_df['Scoring'] == 'Conventional'].pivot(
index='Reform',
columns='Window',
values='Impact'
)
# Reorder columns
conventional_pivot = conventional_pivot[['10-Year', '25-Year', '50-Year', '75-Year']]
# Sort by option number
conventional_pivot['sort'] = conventional_pivot.index.map(extract_option_number)
conventional_pivot = conventional_pivot.sort_values('sort').drop('sort', axis=1)
# Format for display
conventional_display = conventional_pivot.copy()
for col in conventional_display.columns:
conventional_display[col] = conventional_display[col].apply(format_billions)
conventional_display = conventional_display.reset_index()
conventional_display = conventional_display.rename(columns={'Reform': 'Policy Option'})
print("\nConventional Scoring (Primary Estimates)")
print("Cumulative Revenue Impact by Projection Window (Billions $)\n")
from IPython.display import display, HTML
html_table = conventional_display.to_html(index=False, classes='dataframe')
display(HTML(html_table))
The 75-year projections reveal several important patterns. Options that raise revenue in the 10-year window continue to do so over longer horizons, with the magnitude of revenue gains increasing substantially. Option 2 (85% taxation), for example, grows from 429billionover10yearsto15 trillion over 75 years, reflecting both nominal growth and the expanding share of Social Security benefits in the economy as the population ages.
Most notably, the Roth-style swap options (5 and 6) show complex long-term trajectories. Both options are initially revenue-positive, with cumulative gains peaking around 2051 at approximately 1.4trillion.However,demographicpressurescausethesegainstoerodeoversubsequentdecades.By2070,cumulativerevenueturnsnegative,ultimatelyreaching−23.5 trillion (Option 5) and -$22.3 trillion (Option 6) by 2100. These patterns reflect the fundamental asymmetry between eliminating a revenue source tied to a growing retiree population while adding one tied to a workforce growing more slowly relative to retirees.
The scale of these long-term impacts underscores the importance of demographic considerations in evaluating Social Security taxation policies. Options that appear revenue-positive in standard budget windows and even remain positive for multiple decades can create substantial long-term fiscal challenges as the worker-to-retiree ratio declines and benefit obligations grow.
The following chart presents the total cumulative revenue impact over the complete 75-year projection window for all reform options, using conventional scoring:
# Calculate 75-year totals for bar chart
totals_75yr = df[df['scoring_type'] == 'dynamic'].groupby('reform_display')['revenue_impact'].sum().reset_index()
totals_75yr['sort'] = totals_75yr['reform_display'].map(extract_option_number)
totals_75yr = totals_75yr.sort_values('sort').drop('sort', axis=1)
# Create bar chart
fig_75yr_bar = px.bar(
totals_75yr,
x='revenue_impact',
y='reform_display',
orientation='h',
title='75-Year Cumulative Revenue Impact (2026-2100)<br>Conventional Scoring',
labels={
'revenue_impact': 'Cumulative Revenue Impact (Billions $)',
'reform_display': ''
}
)
# Color bars based on positive/negative
colors = ['#D32F2F' if x < 0 else BLUE_PRIMARY for x in totals_75yr['revenue_impact']]
fig_75yr_bar.update_traces(marker_color=colors)
# Add vertical line at zero
fig_75yr_bar.add_vline(x=0, line_dash="dash", line_color="black", opacity=0.5)
# Update layout
fig_75yr_bar.update_layout(
font=dict(family="Inter"),
xaxis_tickformat="$,.0f",
font_color=BLACK,
margin={"l": 250, "r": 50, "b": 100, "t": 100, "pad": 4},
showlegend=False,
annotations=[
{
"x": 1,
"y": -0.25,
"xref": "paper",
"yref": "paper",
"text": "Source: PolicyEngine US Microsimulation (Conventional Scoring)",
"showarrow": False,
"font": {"family": "Inter", "size": 10, "color": DARK_GRAY},
}
],
)
fig_75yr_bar.show()
scoring_type: “conventional” (with labor supply responses) or “static” (no behavioral responses)
taxable_payroll: OASDI taxable payroll in billions (SSA Table VI.G6)
gdp: Gross Domestic Product in billions (SSA Table VI.G6)
pct_of_payroll: Revenue impact as % of taxable payroll
pct_of_gdp: Revenue impact as % of GDP
The table below shows sample years for Option 1 (Full Repeal):
# Generate downloadable data file with economic context
import pandas as pd
# Load SSA economic projections
ssa_econ = pd.read_csv('../data/ssa_economic_projections.csv')
df_download = df.merge(ssa_econ, on='year')
# Fix terminology
df_download['scoring_type'] = df_download['scoring_type'].replace({'dynamic': 'conventional'})
# Calculate percentages
df_download['pct_of_payroll'] = (df_download['revenue_impact'] / df_download['taxable_payroll'] * 100).round(3)
df_download['pct_of_gdp'] = (df_download['revenue_impact'] / df_download['gdp'] * 100).round(3)
# Keep only relevant columns
df_export = df_download[['reform_name', 'year', 'revenue_impact', 'scoring_type',
'taxable_payroll', 'gdp', 'pct_of_payroll', 'pct_of_gdp']]
# Save
df_export.to_csv('_static/revenue_impacts_with_payroll_pct.csv', index=False)
# Show sample
sample = df_export[
(df_export['reform_name'] == 'option1') &
(df_export['scoring_type'] == 'conventional') &
(df_export['year'].isin([2026, 2035, 2050, 2075, 2100]))
]
print("Sample: Option 1 (Full Repeal) - Conventional Scoring")
print("=" * 70)
for _, row in sample.iterrows():
print(f"{row['year']:.0f}: ${row['revenue_impact']:>7,.0f}B | {row['pct_of_payroll']:>6.2f}% of payroll | {row['pct_of_gdp']:>6.2f}% of GDP")
print("\nFull repeal costs: 0.91%-1.82% of payroll, or 0.32%-0.61% of GDP")
print("✓ Saved to _static/revenue_impacts_with_payroll_pct.csv")
Sample: Option 1 (Full Repeal) - Conventional Scoring
======================================================================
2026: $ -101B | -0.91% of payroll | -0.32% of GDP
2035: $ -202B | -1.25% of payroll | -0.44% of GDP
2050: $ -459B | -1.62% of payroll | -0.56% of GDP
2075: $ -1,310B | -1.82% of payroll | -0.61% of GDP
2100: $ -3,387B | -1.80% of payroll | -0.60% of GDP
Full repeal costs: 0.91%-1.82% of payroll, or 0.32%-0.61% of GDP
✓ Saved to _static/revenue_impacts_with_payroll_pct.csv
Long-term fiscal trajectory of Roth-style swap policies¶
While Options 5 and 6 show positive revenue impacts in standard 10-year budget windows, their long-term fiscal trajectories diverge significantly from their near-term performance. This section examines the complete 75-year projection to understand the structural drivers of these policies’ evolving fiscal impacts.
The following visualizations illustrate how cumulative and annual revenue impacts evolve across the projection period, showing the transition from initial revenue gains to long-term losses as demographic pressures intensify.
Decomposing the Roth swap: why employer payroll revenue falls short¶
To understand why Option 5’s cumulative gains eventually turn to losses, we can decompose it into its two components by comparing it to Option 1 (which only eliminates Social Security benefit taxation). The difference between Option 5 and Option 1 isolates the revenue raised from taxing employer payroll contributions.
The following visualization shows how these two revenue streams evolve over time, revealing the critical crossover point where annual employer payroll tax revenue falls behind the annual Social Security benefit taxation being eliminated:
The fundamental driver of the long-term divergence becomes clear when examining the annual growth rates of the two revenue streams. While both grow with the economy, they do so at persistently different rates that compound over decades:
The growth rate differential reveals the structural challenge. Social Security benefit taxation grows at an average annual rate of 5.64%, while employer payroll tax revenue grows at 3.33%, creating a persistent 2.31 percentage point gap. This differential remains remarkably stable across the projection period: 2.25 percentage points in the early years (2027-2050) and 2.34 percentage points in later years (2051-2100).
This seemingly modest differential compounds dramatically over time. In the 10-year budget window, the cumulative effect still favors the employer payroll tax, which offsets 120% of the Social Security benefit taxation losses. By 2051, cumulative revenue peaks at 1.4trillion.However,byyear75,thisratiodeterioratessubstantially,asthe2.3percentagepointannualgrowthdifferentialcompoundstoproducecumulativelossesof23.5 trillion.
The growth rate patterns reflect underlying demographic and economic forces. Employer payroll tax revenue grows with the total wage bill in the economy, determined by workforce size and wage levels. Social Security benefit taxation grows with retiree income and benefit levels, which are amplified by increasing longevity, an aging population structure, and a declining worker-to-retiree ratio. The result is a policy structure that exchanges a revenue source growing with demographic headwinds for one constrained by demographic tailwinds, creating an unsustainable long-term fiscal trajectory despite appearing revenue-positive in standard budget windows and for several decades thereafter.
Understanding the long-term trajectory of Roth-style swaps¶
The long-term revenue trajectory observed in Options 5 and 6 reflects fundamental demographic and economic trends rather than modeling artifacts. Both options implement the same basic policy exchange: eliminating Social Security benefit taxation while adding taxation of employer payroll contributions. In 2026, these two revenue sources are roughly equal at approximately $2.5 trillion each, which explains why the policies appear strongly revenue-positive in early years.
However, the two tax bases evolve differently over the 75-year projection window. Social Security benefit taxation grows at an annual compound growth rate of approximately 4.7%, driven by an aging population structure, increasing life expectancy leading to longer benefit collection periods, wage indexing of benefits, and a declining worker-to-retiree ratio.
The employer payroll tax base follows a similar but slightly slower trajectory at an annual rate of approximately 4.6%. This base grows with the working-age population and wage levels but lacks the demographic tailwind that accelerates Social Security benefit growth. The differential growth rates create a widening gap between the revenue source being eliminated and the revenue source being added.
While the difference between 4.7% and 4.6% growth may appear modest, it compounds significantly over 75 years. In the 10-year window, employer payroll taxation more than offsets the loss of benefit taxation, generating net positive revenue. This advantage persists for over four decades, with cumulative revenue peaking at 1.4trillionaround2051.However,thegrowthdifferentialeventuallydominates:annualemployerpayrollrevenuegrowthfallsbehindannualbenefittaxationlosses,causingcumulativetotalstoturnnegativein2070andultimatelyreach−23.5 trillion by 2100.
Option 6’s phased implementation creates an additional dynamic. The phase-in schedule ramps up employer payroll taxation quickly (reaching 100% by 2033) while phasing out Social Security benefit taxation slowly (completing by 2045). This timing mismatch produces an 18-year window during which both revenue sources are collected simultaneously, generating slightly higher early-period cumulative revenue before converging to a trajectory similar to Option 5.
From a public finance perspective, these options demonstrate the critical importance of 75-year scoring for policies involving demographic dynamics. While appearing revenue-positive for 25 years and neutral even at 45 years, the cumulative losses of $22-24 trillion over the full 75-year window reflect the compounding effect of exchanging a revenue source growing with an aging population for one tied to a workforce that is shrinking relative to the retiree population.