Repository navigation
Conversation
…ans-tested transfer Adds statutory eligibility for the Part D low-income subsidy (Extra Help): countable income below 150% of the poverty guideline for the family size, countable resources within the CPI-indexed limit plus the burial funds exclusion, and deemed eligibility for Medicaid enrollees, SSI recipients and Medicare Savings Program enrollees. The subsidy is valued at the Medicare Trustees' average payment per subsidy enrollee (Table IV.B9), as CBO assigns it, and joins gov.household.cbo_means_tested_transfers. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
YAML tests for every new variable, households computed from their inputs (Medicare Savings Program deeming, the subsidy's own tests, the pre-2024 partial range, take-up), and the CBO means-tested list. Python invariants: IV.B9 transcription, IV.B10 conservation, agreement with the per-enrollee series cbo_medicare_benefit subtracts, the CPI rule behind the resource limits, Hypothesis bounds, restated eligibility rules over a seeded sample with income, interest and asset variants, and a differential test against msp_countable_income. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Oct 11, 2026
…idy average Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This was referenced Nov 3, 2026
The subsidy pays part of a Part D plan's premium and cost sharing, so 42 U.S.C. 1395w-114(a)(3)(A)(i) defines a subsidy eligible individual as one enrolled in a prescription drug plan or MA-PD plan, and 20 CFR 418.3101(b) says an applicant "must enroll in one of these plans to actually receive a subsidy". medicare_part_d_enrolled reached main in #10115 after this branch began, so part_d_lis_enrolled now requires it, including for Medicaid enrollees and SSI recipients: Medicare enrolls them in a plan automatically (42 CFR 423.34), but they can opt out of Part D, for example for TRICARE for Life or VA drug coverage. takes_up_medicare_part_d_if_eligible defaults to true and Microcosm does not yet set it, so the microsimulation is unchanged; a household that sets it to false now gets no subsidy. The seeded household sample in test_part_d_lis.py now draws Part D enrollment and its restated rule requires it; the YAML tests add an opt-out case for each path. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
#10118 extended calibration.gov.hhs.medicare.part_d_low_income_subsidy_ per_enrollee and the other Medicare per-enrollee series back to 2013, the first year of per_capita_cost. The per-recipient average started in 2015, so part_d_lis raised ParameterNotFoundError for a 2013 or 2014 enrollee. The 2023 Trustees Report's Table IV.B9 (file page 158), the newest that shows both years, gives $2,023 per subsidy enrollee in 2013 (11.5 million enrollees) and $2,052 in 2014 (11.8 million); its Table IV.B10 gives $23.2 billion and $24.3 billion, which the products match within rounding, and the 2021 report's Table V.B3 enrollment (52,504 and 54,115 thousand) turns them into #10118's per-enrollee values within 0.4%. The invariant tests now cover both years. Before 2015, cbo_household_means_tested_transfers still raises ParameterNotFoundError through Medicaid and SNAP parameters, as it does without this change. The 2026 report's reference now lists both cited pages (119 and 158), and the Habib reference both of its pages (51 and 52), per the repository's reference rules. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
|
- Index the resource limits after 2026 by the unrounded September CPI-U ratio (42 U.S.C. 1395w-114(a)(3)(E)(i)), and add September 2026's CPI-U. - Follow the statute's chain for the couple limit from 2015, entered as a source-conflict candidate against CMS's 2015 memo. - Count non-home real estate for the household head's couple, floor each income source at zero, count both returns' dependents for spouses who file separately, and document the auto-enrollment data contract. - Fix the per-recipient and jurisdiction citations. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Add part_d_lis next to msp_cost in household_health_benefits and healthcare_benefit_value, which count only when include_health_benefits_in_net_income is on. - YAML cases for the indexed 2027 limits, the statutory couple limit, non-home real estate and its attribution, rental losses, separate filers' family size and the Part D input of Medicaid enrollees. - Python tests: the CPI rule through 2027 with no published exception, projected years and a synthetic tree for the extension, exhaustive loops for the bounds, and a sample with separate filers, rental losses, real estate and households on each limit, plus a reach check. - Move the registry entry to the HHS block and fix its notes. - Read only September instants as published CPI-U values. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
16,590 x 336.940 / 324.800 is 17,210.08 and 33,120 x 336.940 / 324.800 is 34,357.92. The rounded limits and the tests' inputs don't change. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CMS enrolls a qualifying covered retiree only if they are a full-benefit dual eligible whose plan sponsor it approved for the retiree drug subsidy and they confirm that they want a plan; it enrolls no other covered retiree. The docstring had limited the exception to retirees whose employer gets the subsidy. Documentation and a test comment only. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A household with two flagged heads gives its real estate to the older one, and a non-September instant in the September CPI-U node is not a published September. Both rules had no test; the round 3 mutants that reversed them survived. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This branch has not been deployed
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
This PR builds on #10118, which builds on #10111 (#10107 merged on 2026-10-12). Once #10111 and #10118 merge, it retargets to
mainand merges it.Summary
#10118 makes the default CBO household income count Medicare the way CBO does: net of premiums, and without the Part D low-income subsidy (LIS, "Extra Help"). CBO counts the LIS as a means-tested transfer instead. pe-us had no LIS variable, so after #10118, CBO income after transfers leaves out the LIS: about $18 billion in 2026, which #10118 takes out of Medicare at $255.86 per enrollee.
This PR models the LIS and counts it in two places:
gov.household.cbo_means_tested_transfers, CBO's means-tested transfers;gov.household.household_health_benefitsandhealthcare_benefit_value, besidemedicaid_costandmsp_cost.The model pieces:
part_d_lis_eligiblecovers Medicare enrollees in the 50 states and DC whom the law deems eligible or who pass the income and resource tests. Medicaid enrollees, SSI recipients and people eligible for a Medicare Savings Program count as deemed.part_d_lis_enrolledpays only people in a Part D plan (medicare_part_d_enrolled, from Charge the Part D income-related premium only to Part D enrollees #10115). In a plan, Medicaid enrollees and SSI recipients get the subsidy automatically; other eligible people needtakes_up_part_d_lis_if_eligible.part_d_lisgives each enrollee the Medicare Trustees' average subsidy per LIS enrollee, CBO's measure: $1,254 in 2026.gov.hhs.medicare.part_d.low_income_subsidy, one calibration series (2013–2035), BLS's September 2026 CPI-U, a projection of the resource limits after 2026 inuprating_extensions.py, aprograms.yamlentry, a take-up flag and a source-conflict candidate.What changes:
household_net_incomeandhousehold_benefitsdon't change. The health benefit lists reach them only whengov.simulation.include_health_benefits_in_net_incomeis on, and it's off by default.healthcare_benefit_value, and sohousehold_net_income_including_health_benefits, rise by the subsidy.medicare_cost, whose spending per enrollee already includes all Part D spending, the LIS among it. So nothing counts twice.In the 2026 microsimulation, 9.89 million people receive $12.40 billion. CBO income after transfers and taxes and
healthcare_benefit_valueeach rise by that amount (see Impact).Why an average, not a computed benefit
Most of the LIS is the cost-sharing subsidy. That depends on each enrollee's drug use, and pe-us has no Part D claims. The premium subsidy depends on the region's benchmark premium and the plan chosen. CBO values the LIS the same way for everyone:
This PR keeps CBO's average but takes recipients from the law, not from CBO's pool. CBO draws recipients until their count matches the administrative total. The model pays every eligible person in a Part D plan who is on Medicaid or SSI or takes the subsidy up, so its count comes from the data (see Impact).
The Trustees publish the average directly: Table IV.B9's "Subsidy amount" per LIS enrollee. The series takes 2016–2035 from the 2026 report (file page 158), 2015 from the 2025 report and 2013–2014 from the 2023 report. Enrollment times the amount reproduces Table IV.B10's payments, for example 14.4 million × $1,254 = $18.1 billion in 2026.
Institutionalized enrollees. CBO removes institutionalized LIS recipients and their benefits (Habib 2018, file page 52). Its adjusted totals (file page 51) are:
CBO's 2016 benefits come from its own baseline. Its recipient counts are 88–90% of the Trustees'. So the adjustment shows up mostly as fewer recipients, which the model's noninstitutional microdata already reflects.
Eligibility
defined_for = "medicare_enrolled";part_d_lis_enrolledrequiresmedicare_part_d_enrolledpart_d_lis_income_eligiblepart_d_lis_family_size: the marital unit plus the dependents on the couple's returns, on both returns when the spouses file separatelypart_d_lis_countable_income: SSI's unearned sources lessincome/excluded_sources.yaml, then_apply_ssi_exclusionson the couple's totalslimit/individual.yaml,limit/couple.yaml;extend_part_d_lis_resource_limitsafter 2026burial_exclusion.yamlsources.yaml(bank, stock and bond assets) andhousehold_sources.yaml(household_other_real_estate_equity, for the household head's couple)part_d_lis_deemed_eligibleineligible_jurisdictions.yamlBefore 2024, income from 135% to 150% of the guideline, or resources between the lower (D) and the higher (E) limit, gave a partial subsidy. The Inflation Reduction Act (§ 11404) made all of it a full subsidy from 2024. The model uses the outer bound in every year, because CBO and this PR give every recipient the same average.
The LIS income test takes SSI's exclusions "without regard to the application of section 1396a(r)(2)" ((a)(3)(C)(i)). So
part_d_lis_countable_incomekeeps SSI's $20 general exclusion even in Illinois and Mississippi, whose Medicare Savings Program income takes larger 1902(r)(2) exclusions.Rental losses. SSI counts net rental income, and POMS SI 00830.505 says not to "use [excess expenses] to offset other income".
part_d_lis_countable_incometherefore counts each unearned source at no less than zero, for each spouse. pe-us's own SSI and Medicare Savings Program income sums the sources with their signs, so a rental loss there still offsets Social Security; a separate task covers those.Real estate. 423.772 counts "real estate that is not the applicant's primary residence", and 20 CFR 418.3405(b) counts it whether or not it can be sold within 20 workdays. pe-us holds real estate equity by household, and a household amount doesn't say whose it is. So
part_d_lis_resource_eligiblecountshousehold_other_real_estate_equityin full for the household head's couple (the oldest member's, when no one is head), once per household.Rental property equity (
household_rental_property_equity) stays out. 20 CFR 418.3425(c) and (d) exclude property essential to self-support: property used in a trade or business in full, and other income-producing property up to $6,000 of equity when it returns 6% a year (POMS SI 01130.500, SI 01130.501). The model can't tell which applies. The default dataset holds neither input, so in the microsimulation real estate changes nothing.Part D plan. The statute's subsidy eligible individual "is enrolled in a prescription drug plan or MA–PD plan" ((a)(3)(A)(i)). CMS's rule reads "enrolled in, or seeking to enroll in a Part D plan" (423.773(a)), and SSA's rule tells people found eligible before they enroll that they "must enroll in one of these plans to actually receive a subsidy" (418.3101(b)). The model can't see who seeks to enroll, so
part_d_lis_eligibleholds the deemed, income and resource tests, andpart_d_lis_enrolledadds the plan: only people withmedicare_part_d_enrolled(from #10115) receive the subsidy.Enrollment. CMS tells deemed people they don't need to apply (423.773(c)(2)). It enrolls subsidy-eligible people who don't choose a plan, and any of them can decline (42 CFR 423.34(d)(1), (e)). Qualifying covered retirees, whose employer retiree drug coverage qualifies for the retiree drug subsidy, are the exception: CMS enrolls only the full-benefit dual eligibles among them whose plan sponsor it approved for that subsidy, and only if they confirm that they want a plan (423.34(d)(3)). So:
part_d_lis_enrolledis automatic formedicaid_enrolledorssi > 0people in a Part D plan;takes_up_part_d_lis_if_eligible, which defaults to True. pe-us models Medicare Savings Program eligibility, not enrollment, so MSP-deemed people go through the flag too;takes_up_medicare_part_d_if_eligibleshould set it True for Medicaid enrollees and SSI recipients other than those who decline and covered retirees who don't choose a plan. A person whose Part D input is False gets no subsidy.part_d_lis_enrolled's documentation states this contract.Microcosm sets neither
takes_up_medicare_part_d_if_eligiblenortakes_up_part_d_lis_if_eligible(git grepon microcosmorigin/main3db65154b, 2026-11-07); its only mention of the first is a comment. Both default to True. In the microsimulation, therefore, every Medicare enrollee holds a Part D plan, and every eligible one receives the subsidy. microcosm#1206 proposes a Part D take-up input.Values
Resource limits through 2026.
us:part-d-lis-couple-resource-limit-2015(source_conflicts/candidates.yaml), which follows the statute under the statute-first rule.~/reviews/medicare-part-d-lis-2026-10/scripts/verify_published_chain.py, from POMS HI 03030.025's prior-year list and BLS's Septembers).Resource limits after 2026.
extend_part_d_lis_resource_limits(uprating_extensions.py) projects each later year:get_september_cpi_u_valuestakes both Septembers fromgov.bls.cpi.cpi_u_nsa_septemberwhen BLS has published both, and otherwise both from the model's CPI-U projection, never one of each;END_YEAR.Medicaid home equity, as a side effect.
get_cms_cpi_u_percentage_increasenow reads its Septembers through the same helper. With September 2026 published, CMS's rounded percentage for the 2027 Medicaid long-term care home equity limits becomes 3.7% (336.940 / 324.800) instead of 3.1% from the projected series. The 2027 limit rises from $1,165,000 to $1,172,000.The helper also reads only September instants now. pe-us backdates each parameter's first value to 2015-01-01, which the old code counted as a published 2015 September. No year the home equity projection fills used it.
Per recipient. IV.B9, 2013–2035:
calibration.gov.hhs.medicare.per_capita_cost, like the per-enrollee series beside it.programs.yamlgives 2015 as the first verified year.Impact
2026, the default dataset (
populace_us_2024.h5@populace-us-2024-spm-20260909: 57,240 households, 166,321 people), one full run per side through the shared heavy lock. Base: this branch's merge-base with #10118's branch, c9dfbc2 (#10118's head). PR: fe4af16 (the later commits, through the head 2c447a3, change only documentation and tests). Each run imported its owngit archivesnapshot.cbo_medicare_benefitmedicare_costhealthcare_benefit_valuehousehold_net_incomepart_d_lispart_d_lis_enrolledhealthcare_benefit_valueequal totalpart_d_listo within $220 and $112, float rounding on $12.4 billion.part_d_lis_enrolledgets anything.Recipients by path:
By quintile, ranked as CBO ranks: households by size-adjusted income before transfers and taxes, in quintiles of equal numbers of people. Households with negative income stay out of the lowest quintile; they gain $51 million.
What the review fixes moved. Against the previous head (e415392), record by record on the same dataset:
Against the Trustees. The model reaches 69% of IV.B9's 14.4 million LIS enrollees and 68% of IV.B10's $18.1 billion. Two things account for much of the gap:
months_receiving_social_security_disability), and no Microcosm build sets that input (Medicare: add a Part B enrollment input and impute SSDI months so Part B rules reach the right people #10457, microcosm#876). So no disabled beneficiary under 65 can receive the subsidy here.With #10118. #10118 takes $255.86 per Medicare enrollee out of
cbo_medicare_benefit, which comes to $14.96 billion over the model's 58.5 million enrollees. This PR adds $12.40 billion back to the 9.89 million people it finds eligible.Scripts and outputs:
~/reviews/medicare-part-d-lis-2026-10/impact/(pair_inner.sh,compare.2026.fe4af16ec1.json,aggregates.2026.fe4af16ec1.json,r2_vs_r3.2026.json).Invariants and tests
YAML: 68 cases across 9 files in
tests/policy/baseline/gov/hhs/medicare/part_d/low_income_subsidy/. They include:Elsewhere:
cbo_household_income.yamlgains a case where the LIS is the only means-tested transfer, and its list test now includespart_d_lis(andmsp, which does not count);healthcare_benefit_value.yamlgains a case with the LIS;household_health_benefits.yamlgains two: the flag on counts the LIS, and the flag off leaves it out.Python,
tests/core/test_part_d_lis.py: 90 tests. Locally the file takes 24 s, 12–13 s of it the model load, and peaks at 1.46 GB against 1.13 GB for a bare model load. It runs in CI's core groups, which peaked at 6.0–9.7 GiB on main's 2026-11-07 run (42409791828).extend_part_d_lis_resource_limitsruns on every model build and takes 0.05 s of it. Builds take 7–9 s locally: three fresh builds at the base and three at the head, with no difference beyond their spread (~/reviews/medicare-part-d-lis-2026-10/ci-cost/load_cost.txt).cpi_u_nsa_septemberwherever the parameter has them.get_september_cpi_u_valuesreturns, and the 2035 limit exceeds 2027's.adjust_part_d_lis_resource_limit_for_inflationrounds to the nearest $10 with halves up and never lowers a limit; the published 2026 limits need the unrounded ratio.end_year. Inget_september_cpi_u_values, an instant other than September 1 (such as the backdated 2015-01-01 entry) doesn't count as a published September.part_d_lis_countable_incomeequals 12 ×msp_countable_income, a separate implementation of the same SSI exclusions (single people and couples who both have Medicare). The sample has no Illinois or Mississippi households, whose MSP income takes the larger exclusions.The sample reaches every branch, and a test fails if one goes unreached. In the base run:
Mutations: each mutant edits a fresh
git archiveof a commit, which then runs the LIS YAML folder and the Python file, plus the suites the edit can reach (the household health benefit YAML, the Medicaid home equity YAML and the uprating tests). A mutant counts as caught only when a test fails, not when collection breaks.~/reviews/medicare-part-d-lis-2026-10/mutants/holds the results (results.txt,results_r3b.txt).healthcare_benefit_value.youngest_head: with two flagged heads, the younger one holds the real estate. No case had two heads; 2c447a3 adds one.september_filter_dropped: an instant other than September 1 counts as a published September. On the real tree this changes nothing. The only such instant is the backdated 2015-01-01 entry, and the parameter's Septembers start in 2018, so no year gains a complete pair of Septembers through it. 2c447a3 adds a synthetic test ofget_september_cpi_u_values.With three people of the same age, the household amount goes to exactly one of them, and to a flagged head when there is one. That held for all 24 combinations of member order and head flags tried (
scripts/tie_check.py).Local runs at 4ecac84, one process at a time (
local-runs/summary.4ecac84cc2.txt):test_reform_folders_run_each_yaml_and_python_file_separately[True-contrib/crfb]and[True-reform], came from the local venv missingcoverage. With uv.lock's 7.13.2 installed, all four of that test's cases pass.Review
part_d_lis_enrolleddocuments the auto-enrollment contract (law 4);part_d_lissays how its recipients differ from CBO's, and this body reports the impact (law 6);Not modeled and follow-ups
gov.simulation.include_health_benefits_in_net_incomeon,household_net_incomealready holdshousehold_health_benefits, andhousehold_net_income_including_health_benefitsaddshealthcare_benefit_valueon top, so every health benefit counts twice there and inmarginal_tax_rate_including_health_benefits. That includes Medicaid, MSP and CHIP, and nowpart_d_lis. Net income including health benefits counts health coverage twice when health benefits are in net income #9581 (open) tracks it. This PR addspart_d_listo both lists, so they stay identical, as Register the remaining five state premium programs in household_health_benefits #9514 made them.us:part-d-lis-couple-resource-limit-2015goes into rulespec-us'sknown-source-conflicts.yaml, and a follow-up PR here marks the deciding line.axiom: TheAxiomFoundation/rulespec-us#1681 queued (42 U.S.C. 1395w-114(a)(3) and 42 CFR 423.772–773; the corpus has both, but rulespec-us's pin,
us-rulespec-2026-10-29-ecfr-section-text, includes neither; 20 CFR 418 ingestion is TheAxiomFoundation/axiom-corpus#818)Sources
ffa56b91…8fa8)1b3de7a4…4d95)89daaf29…fa67)ef3d1fa7…fc8e)🤖 Generated with Claude Code