Skip to content

Model Basic Health Program benefit value#8667

Open
daphnehanse11 wants to merge 5 commits into
PolicyEngine:mainfrom
daphnehanse11:codex/8528-bhp-benefit-value
Open

Model Basic Health Program benefit value#8667
daphnehanse11 wants to merge 5 commits into
PolicyEngine:mainfrom
daphnehanse11:codex/8528-bhp-benefit-value

Conversation

@daphnehanse11

Copy link
Copy Markdown
Collaborator

Summary

  • add a 2026 Basic Health Program payment proxy using BHP-specific CMS factors
  • build BHP reference premium helpers that reuse ACA SLCSP rating data without depending on pays_aca_premium
  • include basic_health_program in both household_health_benefits and healthcare_benefit_value
  • add regression coverage for the Minnesota 2026 issue case and New York family-tier BHP premiums

Root Cause

BHP eligibility correctly blocked ACA Marketplace premiums and PTC, but enrolled BHP households had no modeled replacement health benefit value. That left households with zero ACA PTC and zero BHP benefit value.

Notes

This models the initial 2026 BHP value as the adjusted reference premium minus the ACA household contribution amount, multiplied by the CMS income reconciliation factor and 95 percent federal payment rate. The CSR component remains zero, matching current CMS payment guidance while there is no available CSR appropriation.

Fixes #8528.

Tests

  • uv run python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/hhs/basic_health_program/basic_health_program.yaml --batches 1
  • uv run python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/hhs/basic_health_program/is_basic_health_program_eligible.yaml --batches 1
  • uv run python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/household/household_health_benefits.yaml --batches 1
  • uv run python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/household/healthcare_benefit_value.yaml --batches 1
  • uv run --extra dev ruff check policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_adjusted_reference_premium.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_age_curve_amount_person.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_family_tier_amount.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_family_tier_category.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_family_tier_dependent_child.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_family_tier_multiplier.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_ny_age_29_dependent_child.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_reference_premium.py policyengine_us/variables/gov/hhs/basic_health_program/basic_health_program_tax_unit_enrolled.py policyengine_us/variables/household/healthcare_benefit_value.py
  • uv run pytest policyengine_us/tests/test_parameter_files.py -q

@codecov

codecov Bot commented Jun 17, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (206b9e7) to head (f943db1).
⚠️ Report is 2 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #8667   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            8        11    +3     
  Lines          127       162   +35     
=========================================
+ Hits           127       162   +35     
Flag Coverage Δ
unittests 100.00% <100.00%> (ø)

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@daphnehanse11 daphnehanse11 changed the title [codex] Model Basic Health Program benefit value Model Basic Health Program benefit value Jun 17, 2026
@daphnehanse11

Copy link
Copy Markdown
Collaborator Author

PR Review — Basic Health Program benefit value

Read-only review (no code changes). The implementation deliberately mirrors the established ACA SLCSP family-tier/age-curve architecture (slcsp_family_tier_*, aca_ptc), and the high-level payment formula matches the CMS BHP federal funding methodology: PTC = max(0, ARP − contribution) × IRF × 95%, ARP = RP × PAF × PHF × WF, CSR = 0. The structure is sound. Findings below.

🔴 Critical (Must address before merge)

  1. Income reconciliation factor (0.9454) is unverified and runs opposite to the historical pattern. parameters/gov/hhs/basic_health_program/payment/income_reconciliation_factor.yaml:4. Published BHP IRF values for Medicaid-expansion states (NY and MN are both expansion states) were ≈100.63% (PY2022) and 100.66% (PY2023) — i.e. above 1.0. The PR uses 0.9454 (94.54%), which reduces the PTC component ~5.5% and is directionally opposite to every prior year. This may be a real 2026 change, but it isn't justified in the PR, and the cited bulletin (cib12102025.pdf) returns HTTP 403 to automated fetches so it could not be independently corroborated. Please quote the exact page of the 2026 CMS bulletin confirming IRF = 0.9454 for expansion states. If that page can't be produced, treat the value as likely wrong.

  2. CI: codecov/patch and codecov/project failing. All functional test suites (baseline + contrib shards, lint, smoke-import) pass; the only failures are the two coverage gates, driven by the large untested family-tier branching (see Test Coverage below). Adding the missing cases should clear both.

🟡 Should address

  1. Confirm the remaining 2026 factor values against the bulletin and fix the waiver_factor reference. PAF = 1.188 (= 1.20/1.01, the standing value since PY2018), PHF = 1.0, and WF = 1.0 are all plausible long-standing constants, but like the IRF none were independently verifiable (medicaid.gov blocks automated access). Please confirm each literally appears in cib12102025.pdf. Also, waiver_factor.yaml:14 cites the 2023 BHP Final Rule rather than the 2026 bulletin — point it at the 2026 source for consistency.

  2. Add #page=XX anchors to all five PDF references. The four cib12102025.pdf links and the FR-2022-12-20 link have no page anchor; per parameter-reference standards a PDF href should land on the page showing the value.

  3. Document the per-household simplification in basic_health_program.py. The CMS PTC equation averages max(0, ARP − I·PTCF) over 1-point FPL increments within the rate-cell income band; the code uses the household's own aca_magi × aca_required_contribution_percentage (basic_health_program.py:33-35). This is a defensible micro-level proxy consistent with how aca_ptc is computed, but the documentation string should state that the rate-cell FPL-band averaging is approximated by the household's actual MAGI.

  4. Test coverage gaps (cause of the codecov failures). The test file has 2 cases (MN single adult happy path; NY ONE_ADULT category). Missing, all "should":

    • Pre-2026 base formulabasic_health_program: 0 (e.g. a 2025 case).
    • Income-ineligible (ptc_income_eligible False) → 0.
    • PTC component floored at max_(0, …) when contribution > ARP → 0.
    • TWO_ADULTS, ONE_ADULT_AND_ONE_OR_MORE_CHILDREN, TWO_ADULTS_AND_ONE_OR_MORE_CHILDREN, and NY CHILD_ONLY family-tier categories + multipliers.
    • extra_adults = max_(adult_count - 2, 0) path (3+ adults).
    • Vermont multipliers — every where(in_ny, p.ny…, p.vt…) VT branch is untested.
    • NY age-29 dependent child (basic_health_program_ny_age_29_dependent_child + the age_29_multiplier branch).
    • basic_health_program_family_tier_dependent_child middle branch (dependent above max_child_age, below family_tier_dependent_child_age_threshold).
    • MN multi-person age-curve aggregation (adult + child).

🟢 Suggestions

  1. state_code comparison inconsistency. basic_health_program_family_tier_category.py:17,59 compares state_code_str == "NY" while _family_tier_multiplier.py:18-19 and _ny_age_29_dependent_child.py use the enum state_code == state_code.possible_values.NY. Both work and the inconsistency is inherited from the cloned SLCSP source, but standardizing on the enum form would be cleaner.
  2. Scope defined_for on the intermediate MONTH variables (basic_health_program_adjusted_reference_premium.py, basic_health_program_reference_premium.py) to basic_health_program_tax_unit_enrolled — they currently compute nationwide. Results are correct (the downstream benefit is gated) and this matches SLCSP precedent, so it's optional.
  3. Add reference attributes to the five family-tier variables (they derive from ACA SLCSP rating rules) and point basic_health_program.reference at the CMS methodology bulletin rather than the generic medicaid.gov/basic-health-program landing page.
  4. Add a one-line comment in basic_health_program.py noting the intentional YEAR-reads-MONTH summation (engine sums 12 monthly ARP values), and pin basic_health_program_adjusted_reference_premium / _reference_premium assertions in Case 1 so a factor change is caught directly.

Confirmed correct (checked, no change needed)

  • CSR component = 0 is explicitly correct per the methodology (Eq. 3 — no CSR appropriation for 2026).
  • YEAR-reads-MONTH period handling mirrors aca_ptc reading slcsp; dimensionally consistent.
  • adds / tax_unit.sum(mask) aggregation idioms are correct.
  • max_(adult_count - 2, 0) and child_only … else 0 are structural constants (copied verbatim from slcsp_family_tier_multiplier), not policy values needing parameters.
  • basic_health_program added to both healthcare_benefit_value.adds and household_health_benefits — independent sibling aggregators, no double-count (test confirms all three equal 4001.53).
  • No TODOs/stubs/dead code; changelog fragment present.

Validation summary

Check Result
Regulatory accuracy 1 critical (IRF value unverified), structure correct
Reference quality 0 critical; verify 4 values, add page anchors, fix waiver ref
Code patterns 0 critical; 3 minor suggestions
Test coverage ~13 missing branch scenarios (drives codecov failure)
CI status All test suites pass; codecov/patch + codecov/project fail

Recommendation: REQUEST CHANGES — primarily to confirm the 2026 IRF value (Critical 1) and close the coverage gaps causing the codecov failures. The implementation architecture itself is solid.

Generated via /policyengine:review-pr (read-only). To auto-fix: /fix-pr 8667.

- Add 10 family-tier and benefit-value regression tests (TWO_ADULTS,
  one/two adults with children, NY CHILD_ONLY, extra-adult increment,
  NY age-29 dependent child, under-26 dependent, Vermont multipliers,
  pre-2026 zero, and income-ineligible zero) to close the codecov gaps.
- Document the per-household PTC simplification and the intentional
  YEAR-reads-MONTH annualization in basic_health_program.
- Point the basic_health_program reference and waiver_factor reference
  at the 2026 CMS funding methodology bulletin.
- Add reference attributes to the family-tier variables and standardize
  the family-tier category state comparison on the state_code enum.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@daphnehanse11

Copy link
Copy Markdown
Collaborator Author

Fixes pushed (commit bb7407b)

Addressed the review findings I could resolve safely:

  • Test coverage (codecov failures): added 10 regression cases — TWO_ADULTS, one/two adults with children, NY CHILD_ONLY, the 3-adult extra_adults increment, NY age-29 dependent child, an under-26 dependent, Vermont multipliers, pre-2026 zero, and the income-above-ceiling zero branch. All 12 cases pass locally; test_parameter_files.py passes.
  • Documentation: basic_health_program now documents the per-household PTC simplification (vs. the CMS rate-cell FPL-band averaging) and the intentional YEAR-reads-MONTH annualization.
  • References: basic_health_program and waiver_factor now point at the 2026 CMS methodology bulletin; added reference attributes to the family-tier variables.
  • Consistency: standardized the family-tier category state check on the state_code enum (matching the multiplier and age-29 variables).

Still needs author action (could not be resolved automatically)

  • 🔴 Income reconciliation factor = 0.9454 — unchanged, because cib12102025.pdf is fully blocked by Akamai (HTTP 403 / Access Denied to all automated access), so the value can't be verified against the source. Published expansion-state BHP IRFs were ≈100.6% (PY2022–2023), so a sub-1.0 value is worth a manual confirmation against the bulletin page before merge. The same applies to confirming PAF (1.188), PHF (1.0), and WF (1.0).
  • #page= anchors — not added, since the PDF is inaccessible and any page number would be a guess pointing reviewers to the wrong location.

@daphnehanse11

Copy link
Copy Markdown
Collaborator Author

Source confirmation follow-up

Pushed 07b5d4a to add page anchors to the BHP methodology references:

  • PAF 1.188: 2026 CMS bulletin page 2.
  • Federal payment rate 0.95: 2026 CMS bulletin page 2 and 42 CFR 600.605.
  • IRF 0.9454: 2026 CMS bulletin page 4. The bulletin states that the expansion-state IRF is 94.54% and that all currently operating BHP states are expansion states.
  • PHF 1.00: 2026 CMS bulletin page 6.
  • WF: 2026 CMS bulletin page 1 for continued use of the 2023 methodology, plus the 2023 final rule page 13 for the Section 1332 waiver-factor discussion.

I did not change the factor values. The earlier concern about 0.9454 being directionally different from prior years is real historically, but the 2026 bulletin explicitly confirms it.

Checks:

  • uv run pytest policyengine_us/tests/test_parameter_files.py -q
  • uv run python policyengine_us/tests/test_batched.py policyengine_us/tests/policy/baseline/gov/hhs/basic_health_program/basic_health_program.yaml --batches 1

@daphnehanse11 daphnehanse11 marked this pull request as ready for review June 17, 2026 20:23
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Model Basic Health Program benefit value instead of zeroing enrolled households

1 participant