Add Vermont premium assistance (Vermont Premium Assistance) - #9245
Conversation
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## main #9245 +/- ##
=========================================
Coverage 100.00% 100.00%
=========================================
Files 4 3 -1
Lines 62 46 -16
=========================================
- Hits 62 46 -16
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Harness. 🚀 New features to boost your workflow:
|
998dc41 to
2f35ed3
Compare
Review fixes applied (
|
hua7450
left a comment
There was a problem hiding this comment.
PR #9245 — Add Vermont premium assistance: consolidated review
Program Review
Source Documents
- PDF: 2026 Eligibility Income Thresholds for QHP Subsidies (1 page)
- Statute: 33 V.S.A. § 1812
- Program page: Vermont Health Connect — Financial Help
- Year: 2026
- Scope: PR changes only
- Reviewed head SHA: 2f35ed3
- Mode: full
Branch Status
The branch is 38 commits behind main and 3 ahead — recommend a rebase before merge. Staleness did not affect any finding below.
Critical (Must Fix)
C1. in_effect.yaml cites a broken (404) reference URL for the DVHA thresholds PDF.
policyengine_us/parameters/gov/states/vt/dvha/premium_assistance/in_effect.yaml:18 cites https://info.healthconnect.vermont.gov/sites/hcexchange/files/documents/2026-Eligibility-Income-Thresholds-for-QHP-Subsidies.pdf — verified HTTP 404 (fetched live 2026-08-10 with a browser User-Agent). The same document is cited correctly in fpl_limit.yaml:13 under the sites/vhc/ path, verified HTTP 200, application/pdf, 185,759 bytes. A dead reference cannot corroborate the parameter it supports. Fix: change in_effect.yaml to https://info.healthconnect.vermont.gov/sites/vhc/files/documents/2026-Eligibility-Income-Thresholds-for-QHP-Subsidies.pdf (and drop the #page=1 anchor per A2). The reference title in in_effect.yaml is accurate and can stay. Note for future verifiers: info.healthconnect.vermont.gov returns 403 to plain automated fetches — retry with a browser User-Agent before flagging its URLs as dead. (Flagged by references, code, and PDF-audit validators; the XREF queue item was adjudicated: vhc resolves, hcexchange 404s.)
C2. vt_premium_assistance is missing from gov/household/household_health_benefits.yaml — silently excluded from household net income.
policyengine_us/variables/household/healthcare_benefit_value.py:29 (touched by the PR) and the parameter list policyengine_us/parameters/gov/household/household_health_benefits.yaml (NOT touched) were in lockstep before this PR — the identical 7 entries. The PR appends vt_premium_assistance only to healthcare_benefit_value, so the two lists now diverge. The parameter list is read by policyengine_us/variables/household/income/household/household_health_benefits.py:20, which feeds household net income when gov.simulation.include_health_benefits_in_net_income is enabled — VPA will appear in healthcare_benefit_value but be silently absent from net income with health benefits included. Same failure mode as the recorded Kentucky SSP lesson: a benefit needs BOTH registrations; missing one silently excludes it. Fix: add vt_premium_assistance to the 2022-01-01 list in household_health_benefits.yaml.
C3. Stale "placeholders for the ci-fixer" comments and internal REQ/Gap scaffolding remain in the committed tests; integration Scenario 10's stated purpose is false.
tests/policy/baseline/gov/states/vt/dvha/premium_assistance/integration.yamlScenario 10 still says "Exact derived figures cannot be hand-computed here, so they are placeholders for the ci-fixer to fill from the model," yet the outputs carry concrete values with# derivedmarkers. Worse, the scenario name/header claims the cap arm is exercised ("cap arm exercised (Gap A)… lets min pick the cap residual") while its own trailing comment correctly concludes the opposite: residual $4,383.82 > gap $681.00, so the 1.5% gap arm binds. The test-coverage audit proved the cap arm is mathematically unreachable in a fully derived 2026 case (federal percentage ≥ 2.1% > 1.5%, SLCSP $15,588 far above the max gap ~$705), which is why only injected cases (Scenario 5, unit Cases 3/11/12) reach it. Rewrite the title/header as a near-ceiling derived gap-binding case (or drop as redundant with Scenario 9) and replace the placeholder language with the hand derivation.tests/policy/baseline/household/healthcare_benefit_value.yamlCase 8 carries the same stale "placeholders for the ci-fixer to fill from the model" sentence with filled values.- Internal planning markers
REQ-1…REQ-12and "Gap A"/"Gap B"/"Gap D" appear throughout all four test files (integration.yaml,vt_premium_assistance.yaml,vt_premium_assistance_eligible.yaml,healthcare_benefit_value.yamlCase 8). Per the recorded Connecticut SSP lesson, internal requirement markers must be stripped before merge — they reference a scope document not in the repo. - Important mitigation: the test validator hand-re-derived both flagged "placeholder" cases and confirmed the expected values are correct (Scenario 10: fraction 45,400/15,650 truncates to 2.90 → fed_pct 9.656% → aca_ptc 11,204.176 → VPA 681.00; Case 8: fraction 2.49 → fed_pct 8.4032% → assigned_aca_ptc 12,310.752 + VPA 585.00 = 12,895.752). The numbers stand; only the scaffolding and mislabeling must go — replace the placeholder sentences with this hand arithmetic so a reviewer can verify without running the model.
Should Address
A1. healthcare_benefit_value counts VPA without the ACA take-up gate every sibling uses (and without a filer gate).
policyengine_us/variables/household/healthcare_benefit_value.py:24 adds vt_premium_assistance alongside assigned_aca_ptc. But assigned_aca_ptc = aca_ptc × takes_up_aca_if_eligible (variables/gov/aca/ptc/assigned_aca_ptc.py:13-15), and the sibling state add-ons gate on enrollment (basic_health_program, co_omnisalud), while VPA gates only on any(is_aca_ptc_eligible) — eligibility, not enrollment. Direction and magnitude: in microsimulation, a tax unit assigned takes_up_aca_if_eligible = False (take-up is assigned at microdata construction) receives $0 federal APTC in the aggregate yet a positive VPA of 1.5% of income — a phantom state benefit overstating VPA aggregates for the non-taking-up share of eligible units. Given VPA's small program scale ($4.3M/yr, ~11k recipients per DVHA marketplace reporting), this shifts a state-level aggregate modestly and flips no one's eligibility, hence should-address rather than critical. VPA is paid only for actual enrollees: DVHA states "The State sends your VPA to your insurance company for you" (Financial Help), and § 1812(a)(3) ties the assistance to enrolled qualified health benefit plans. Fix shape: mirror the federal pattern — a take-up-gated assigned_/receives_ counterpart wired into both aggregator lists (see C2), or gate vt_premium_assistance itself. Related nuance: aca_ptc is zeroed for non-filers (tax_unit_is_filer gate) but VPA has no filer gate, so a non-filer VT unit gets residual = full annual SLCSP and VPA = 1.5% × income despite $0 modeled federal credit; § 1812(a)(1) conditions VPA on being "eligible for" § 36B credits, so this is at most a modeling-consistency nuance — a tax_unit_is_filer: false test case would freeze the intent either way.
A2. Drop the #page=1 anchors — the thresholds PDF is a single page.
fpl_limit.yaml:13 and in_effect.yaml:18 both append #page=1 to the DVHA thresholds PDF. The downloaded PDF has exactly 1 page (pdfinfo → Pages: 1). Repo convention (user rule and the Connecticut SSP lesson) is to omit the page anchor on single-page documents. Remove from both hrefs while fixing C1. (Flagged by references, regulatory, code, and PDF-audit validators.)
A3. in_effect.yaml's comment misstates the program's legal history — document the 2013 statutory start and the model-scope reason for pre-2026 false.
in_effect.yaml:2-8 frames § 1812 as "open-ended from the verified program year (2026)." The statute took effect October 1, 2013 ("Added 2013, No. 50, § E.307.1, eff. Oct. 1, 2013" — statute history note) and VPA has paid benefits continuously since plan year 2014, so false for 2014–2025 is a model-coverage limitation, not a legal fact; as written, a reader could conclude the program began in 2026. The session-law verification confirmed the same values (0.015 / 300% FPL) have been in § 1812(a) unchanged since original enactment — 2013 Act No. 50 § E.307.1 contains both figures verbatim; 2015 Act No. 23 § 54 changed only "federal poverty guideline"→"federal poverty level" in subsection (b)(1); 2017 Act 88 (Adj. Sess.) § 4 touched only (b)(1)/(b)(3). So the 2026-only in_effect is a defensible conservative scope, not a statutory necessity. Reword the comment to say: in force since Oct 1, 2013; pre-2026 years unmodeled/unverified because the federal aca_ptc computes only from 2018 and 2021–2025 involved ARPA-era § 36B schedule interactions (federal applicable percentage already 0% below 150% FPL) plus temporary DVHA adjustments not yet sourced. Backdating 2018–2020 (standard § 36B schedule − 1.5pp) is feasible follow-up work. verified_start_year: 2026 in programs.yaml is the correct registry mechanism and needs no change.
A4. Integration Scenario 1's arithmetic comment derives the federal percentage from the wrong bracket.
integration.yaml Scenario 1 says the applicable percentage "interpolates within the 250-300% bracket (8.44% → 9.96%): position = (2.49−2.50)/0.50 falls just below 250%" — a negative position in the wrong bracket. The asserted 0.08403 is correct, but the real derivation is the 200–250% bracket at the truncated fraction 2.49: 6.60% + (2.49−2.00)/0.50 × (8.44−6.60)pp = 8.4032%. As written, the comment misleads the next reviewer at exactly the step the test exists to prove.
Suggestions
S1. "Percentage points" wording is off by 100× in applicable_percentage_reduction.yaml.
The description says the state reduces the applicable percentage "by this many percentage points" and the comment says "Stored here as 0.015 percentage points" — the stored value 0.015 is a /1 fraction equal to 1.5 percentage points. Reword, e.g. "…by this fraction (1.5 percentage points stored as 0.015)". Also, the free comment block sits between values: and metadata:; convention places explanatory comments directly under description. The value itself is correct and corroborated.
S2. Redundant max_(0, …) in the contribution gap.
vt_premium_assistance.py:56: since vt_percentage = max_(0, federal_percentage − reduction), the gap equals income × min_(federal_percentage, reduction), which is non-negative whenever federal_percentage ≥ 0 (guaranteed) and income is already clamped at 0. The outer max_ can never bind — remove it (or simplify the whole gap to income * min_(federal_percentage, reduction)), per the recorded lesson on non-binding clamps.
S3. Prefer add(tax_unit, period, ["is_aca_ptc_eligible"]) > 0 over tax_unit.any(tax_unit.members(...)).
vt_premium_assistance_eligible.py:31 materializes members and calls .any(); the aggregation convention prefers the add(...) > 0 form.
S4. documentation class field on both new variables.
vt_premium_assistance.py:14-30 and vt_premium_assistance_eligible.py:14-24 use the deprecated documentation attribute (Kentucky SSP lesson: use reference plus inline comments). Content is accurate, and ~1,179 existing variables (including aca_ptc) still use documentation, so this is convention drift, not a blocker — keep the valuable "not modeled" disclosures (§ 1812(b) CSR, reconciliation mechanics) wherever they land.
S5. Document the 300%-boundary truncation consequence, and fix the "rounds" verb.
aca_magi_fraction floor-truncates to two decimals (Form 8962 Line 5 Worksheet 2), so a size-1 MAGI up to $47,006 truncates to 3.00 and passes <= 3.0, while the DVHA table's dollar limit is $46,950. This is defensible under § 1812(c) ("use the same mechanisms provided in the Affordable Care Act"), and integration Scenario 2 asserts it, but: (a) a one-line note in vt_premium_assistance_eligible.py would preempt future "over-grants past $46,950" reports; (b) Scenario 2's comment says the model "rounds" 47,000/15,650 to 3.00 — it truncates (3.005 would round to 3.01 but truncate to 3.00); (c) a derived just-above-ceiling case (e.g., employment_income 47,200 → 3.0159 → truncates to 3.01 → ineligible) would prove the ceiling from raw income on the failing side too.
S6. Test-robustness improvements.
(a) Integration Scenarios 3, 5, 6, 7, 8 re-assert directly injected inputs (aca_magi, aca_magi_fraction, slcsp, aca_ptc) as outputs — echo assertions that can never fail; consider trimming to the genuinely derived assertions. (b) The file-wide absolute_error_margin: 0.01 tolerates a full percentage point of error on aca_required_contribution_percentage (a /1 rate ~0.08); in derived scenarios the aca_ptc dollar assertions do the real work, but consider a tighter per-output margin (0.0001) on the rate assertions. (c) No single test case exercises two households (one VT, one NY) in one array pass — the classic defined_for vectorization guard; one such case is cheap.
S7. Unit-test case numbering is out of order.
vt_premium_assistance.yaml runs Cases 1–8, 10, 11, 12, 13, then 9 (negative-MAGI clamp), then 14. Renumber so names match file order (cosmetic; fix if the file is otherwise touched — which C3 requires).
S8. Comment accuracy in the formula and test headers.
(a) vt_premium_assistance.py:37-43 — "this is intentional, not a units bug" is reviewer-directed prose; condense to a one-line # NOTE: keeping the percentage-point-vs-proportional explanation. (b) integration.yaml:11 (and the unit-test header) document VPA = where(eligible, min(gap, residual), 0) but the implementation uses defined_for = "vt_premium_assistance_eligible"; same net behavior, but the pseudo-code should match the mechanism (similarly unit Case 5 attributes the >300% zero to the "defined_for gate" when it is the eligibility formula's income test that fails).
S9. programs.yaml agency field deviates from the dominant State-section style.
programs.yaml:1392 uses agency: DVHA (Vermont Health Connect); 20 of 27 State-section entries use agency: State. Precedent exists for specific agency names, so acceptable — all other field values and the alphabetical placement are correct.
PDF Audit Summary
| Category | Count | Detail |
|---|---|---|
| Confirmed correct | 7 | 0.015 vs § 1812(a)(2); 0.015 as percentage-point cut vs program page; fpl_limit 3 + inclusive <= vs § 1812(a)(1); all 8 household sizes + increment in the PDF's 300% column are exactly 3× the 2025 FPL (no per-size table needed); prior-year FPL vintage via aca_magi_fraction matches the PDF's "2026 benefits on 2025 FPL"; in_effect backward-extrapolation guard |
| Mismatches confirmed | 0 | — |
| Mismatches rejected | 0 | — |
| Unmodeled items | 2 | Pre-2026 program years (EXT verification: same 0.015/300% values in statute since 2013 — see A3); § 1812(b) cost-sharing assistance (94/87/77/73 AV tiers) — explicitly disclosed as not modeled |
| Pre-existing issues | 0 | — |
The Phase-5 queue held 2 items, both resolved: the XREF (two URL variants for the same PDF) adjudicated as vhc = live / hcexchange = 404 (→ C1); the EXT (session-law history) verified SAME values since 2013 Act 50 (→ A3).
Validation Summary
| Validator | Findings | Critical |
|---|---|---|
| Regulatory Accuracy | 8 (0 critical, 4 should, 4 suggestions) — all six review questions verified correct: 1.5pp interpretation, entity logic, FPL basis, formula, no reinvented variables | 0 |
| Reference Quality | 6 (1 critical: 404 URL; values 0.015/3.0/2026 all corroborated with verbatim quotes; subsection citations all accurate) | 1 |
| Code Patterns | 12 (2 critical, 3 major, 7 minor) — hard-coded values, naming, periods, entities all PASS | 2 |
| Formatting | Parameter metadata complete and well-formed; changelog fragment correct; make format-clean per CI |
0 |
| Test Coverage | 9 (0 critical) — all requested edge cases covered; flagged "placeholder" values hand-verified correct | 0 |
| PDF Value Audit | 7 matches / 0 mismatches / 2 flags (both resolved in Phase 5) | 0 |
| CI Status | All checks passing | — |
Review Severity: REQUEST_CHANGES
Three criticals: a broken (404) reference URL on in_effect.yaml (C1), a missing household_health_benefits.yaml registration that silently drops VPA from household net income (C2), and shipped placeholder/REQ scaffolding with a mislabeled test scenario (C3). All are mechanical fixes; the substantive values, formula, and eligibility logic are fully corroborated against primary sources.
Next Steps
To auto-fix issues: run the fix-pr workflow for this PR.
8621b57 to
98d2ac6
Compare
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Reword applicable_percentage_reduction description: 0.015 is a 1.5 percentage-POINT reduction to the federal section 36B applicable percentage, not a proportional "share"; add a statute-vs-DVHA reconciliation comment (statute says "1.5 percent" and defers the applicable-% table to 36B; DVHA operationalizes it as the pp cut). - Quote the DVHA financial-help page's "1.5% of household income" language in its reference title; add the 2026 QHP thresholds PDF to in_effect so the 2026 start year is traceable. - Comment the vt_premium_assistance formula: why 0.015 is subtracted (not scaled) and the low-income floor branch (unreachable in 2026 since the federal applicable % floors at 2.10% > 1.5%, retained for robustness). - Add tests: income-derived integration scenario (derived slcsp/aca_ptc, VPA 681 at 290% FPL), a derived healthcare_benefit_value case, the tightest 3.0001 FPL just-above-ceiling case, and a zero-MAGI case. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
98d2ac6 to
803c3ca
Compare
Resolves three conflicts caused by sibling premium-assistance PRs landing in main (#9239 WA, #9240 MA, #9244 NJ): - variables/household/healthcare_benefit_value.py: both sides appended to the adds list; keeps vt_premium_assistance and wa_cascade_care_savings in alphabetical order. - tests/policy/baseline/household/healthcare_benefit_value.yaml: main's cases 16-20 (WA, MA, NJ) are kept; the two Vermont cases are renumbered 21-22. The derived case's stale cross-reference to "Case 7" now points at Case 21, the forced Vermont case it contrasts with. - programs.yaml: both entries land at the tail of the state section; keeps vt_premium_assistance ahead of main's wa_cascade_care_savings. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Summary
Closes #9225.
Adds Vermont Premium Assistance (VPA), a state premium subsidy administered by the Department of Vermont Health Access (DVHA) through Vermont Health Connect. VPA is a flat 1.5 percentage-point reduction off the federal §36B applicable percentage, layered on top of the federal Advance Premium Tax Credit. It is available to federal-APTC-eligible Vermont enrollees with income at or below 300% of the federal poverty level. Modeled for program year 2026.
Authority: 33 V.S.A. § 1812(a).
This follows the state-premium-assistance pattern already established for MD/NM/CA/CO/WA/MA/NJ. VPA has the simplest mechanic of the set — it shifts the whole federal applicable-percentage schedule down 1.5pp rather than defining its own target-percentage schedule (MD/NM) or a per-member-per-month amount (WA/NJ/MA).
Regulatory authority
33 V.S.A. § 1812(a) (statute, authoritative for both the reduction and the ceiling):
Corroborating sources:
#page=1) — places VPA in the 300% FPL column; PY2026 thresholds are set from the 2025 FPL (prior-year FPL, matchingaca_magi_fraction).Eligibility
A tax unit is VPA-eligible (
vt_premium_assistance_eligible, TaxUnit / YEAR,defined_for = StateCode.VT) when all of:defined_for = StateCode.VT).tax_unit.any(members("is_aca_ptc_eligible")). §1812(a)(1),(3).aca_magi_fraction <= fpl_limit(3.0). §1812(a)(1).in_effect(true from 2026-01-01, open-ended — a standing statutory obligation with no sunset).No income floor, no asset test, no applicant/recipient distinction (§1812(a) imposes none).
Benefit calculation
VPA reuses the federal ACA PTC machinery, reducing the applicable percentage by 1.5pp and returning only the incremental state portion above the federal APTC (
vt_premium_assistance, USD, TaxUnit / YEAR,defined_for = "vt_premium_assistance_eligible"):The algebra:
contribution_gap = income × (federal_pct − vt_pct) = income × min(federal_pct, 0.015), capped at the premium remaining after the federal APTC.Normal case (2026): in the PY2026 federal applicable-percentage schedule the figure floors at 2.1%, so
federal_pct ≥ 0.015always holds andfederal_pct − vt_pct = 0.015exactly. VPA is therefore 1.5% of household MAGI — precisely the DVHA description — capped at the remaining benchmark premium.Low-income branch (defensive): when
federal_pct ≤ 0.015,vt_pctfloors to 0, the gap becomesincome × federal_pct, and themin(gap, premium_after_aptc)cap lets VPA cover the full residual premium (net premium → $0). Given the 2.1% 2026 floor this branch is unreachable organically in 2026, but it is implemented and tested so the formula stays correct if the federal schedule floor ever drops below 1.5%. No value is hardcoded — themax_(0, ...)floor and themin_(...)cap produce this behavior structurally.Requirements coverage
All 22 requirements in scope are implemented and tested (38/38 tests passing across 4 files, including two integration scenarios derived from raw income that drive the full federal ACA chain).
vt_premium_assistance_eligibleaca_magi_fraction <= 3.0)vt_premium_assistance_eligible(299/300/301% boundary)vt_premium_assistance_eligible0.015, not banded)applicable_percentage_reduction.yamlvt_premium_assistancevt_premium_assistancein_effectfrom 2026-01-01, no sunsetin_effect.yaml(2025 off / 2026-27 on)defined_for = StateCode.VTadd(tax_unit, period, ["slcsp"])vt_premium_assistancevt_premium_assistance.yaml,integration.yamlaca_magi≥ 0vt_premium_assistancehealthcare_benefit_value.addshealthcare_benefit_value.pyprograms.yamlentryprograms.yamlchangelog.d/Wiring note: the raw
vt_premium_assistanceis added tohealthcare_benefit_value.adds, matching the CA/NM/CO/WA/MA/NJ precedent (not a takeup-gatedassigned_variant). A takeup-consistentassigned_vt_premium_assistancecould be introduced later if the team wants parity with the federal PTC's takeup gating; out of scope here.Not modeled
is_aca_ptc_eligibleand the 300% FPL test.Files added
Files modified: