Skip to content

Count Federal Public Housing Assistance receipt in Lifeline categorical eligibility - #9378

Merged
hua7450 merged 7 commits into
PolicyEngine:mainfrom
hua7450:lifeline-fpha-categorical
Sep 3, 2026
Merged

Count Federal Public Housing Assistance receipt in Lifeline categorical eligibility#9378
hua7450 merged 7 commits into
PolicyEngine:mainfrom
hua7450:lifeline-fpha-categorical

Conversation

@hua7450

@hua7450 hua7450 commented Sep 2, 2026

Copy link
Copy Markdown
Collaborator

Summary

Adds receives_housing_assistance to gov.fcc.lifeline.categorical_eligibility so households reporting Federal Public Housing Assistance (Section 8 Housing Choice Vouchers, public housing, project-based rental assistance) qualify for Lifeline regardless of income, per 47 CFR § 54.409(a)(2).

Fixes #9376
Fixes #9385

Background

The regulation lists five categorical qualifying programs: Medicaid, SNAP, SSI, Federal Public Housing Assistance, and the Veterans and Survivors Pension. The list covered only the first three. Reproduced in the household API: a single adult at $60k with receives_housing_assistance: true returned lifeline = 0, while the same household with receives_snap: true returned 111.

Design notes

  • YEAR variable in a period: month list is fine. medicaid_enrolled is already YEAR-defined and in the list; is_lifeline_eligible is a YEAR variable that sums each entry over the year with add. Verified locally: the $60k case returns 111 after the change and 0 without the flag.
  • Modeled housing_assistance deliberately not added. It is positive for any renter under 80% AMI with takeup defaulted to true, and vouchers are not an entitlement, so a modeled amount would auto-qualify renters well above 135% FPG. Reported receipt is the only receipt signal in the model.
  • ACP reads the same list, so is_acp_eligible widens correspondingly for the years ACP was in effect. Correct under 47 U.S.C. § 1752(a)(6).
  • Veterans and Survivors Pension stays in the "not implemented" comment; tracked separately.

Tribal households (#9385, review follow-up)

Review pointed out that the new Tribal test locked in a pre-existing bug. 47 CFR § 54.409(b) makes the Tribal-specific list additive: a consumer on Tribal lands qualifies through paragraph (a) or through a Tribal program. is_lifeline_eligible and is_acp_eligible used where(is_on_tribal_land, tribal > 0, non_tribal > 0), which swapped the lists and denied the standard route to Tribal households. Both formulas now read non_tribal > 0 | (is_on_tribal_land & tribal > 0).

Test changes: the Tribal SSI case and the new Tribal housing case flip to eligible, the Tribal "no categorical eligibility" case now also zeros SNAP, SSI and Medicaid since the standard route is read, and the ACP vectorization test expects the Tribal Medicaid household to be eligible.

Files

  • parameters/gov/fcc/lifeline/categorical_eligibility.yaml — one entry
  • variables/gov/fcc/lifeline/is_lifeline_eligible.py, variables/gov/fcc/acp/is_acp_eligible.py — Tribal list is additive per 54.409(b)
  • tests/policy/baseline/gov/fcc/lifeline/is_lifeline_eligible.yaml — reported receipt above 135% FPG qualifies, for non-Tribal and Tribal households
  • tests/policy/baseline/gov/fcc/lifeline/lifeline.yaml — the partner's $60k MA case pays 9.25 × 12
  • changelog.d/lifeline-fpha-categorical.fixed.md

Test plan

  • CI passes

🤖 Generated with Claude Code

…al eligibility

47 CFR 54.409(a)(2) lists Federal Public Housing Assistance as a
categorical qualifying program, but the Lifeline list covered only
Medicaid, SNAP and SSI. Add receives_housing_assistance so households
reporting Section 8, public housing or project-based rental assistance
qualify regardless of income. ACP inherits the change through the shared
list.

Fixes PolicyEngine#9376

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@hua7450
hua7450 marked this pull request as ready for review September 3, 2026 04:24
hua7450 and others added 2 commits September 3, 2026 00:38
…rd programs

47 CFR 54.409(b) makes the Tribal-specific list additive: a consumer on
Tribal lands qualifies through paragraph (a) or through a Tribal program.
The formulas swapped the lists instead, denying the standard route to
Tribal households. Fix both is_lifeline_eligible and is_acp_eligible,
and flip the two tests that had locked the swap in.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 3, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 100.00%. Comparing base (837a1ad) to head (af33835).
⚠️ Report is 24 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff            @@
##              main     #9378   +/-   ##
=========================================
  Coverage   100.00%   100.00%           
=========================================
  Files            3         2    -1     
  Lines           46        36   -10     
=========================================
- Hits            46        36   -10     
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.

hua7450 and others added 3 commits September 3, 2026 00:47
…r Lifeline

47 CFR 54.409(b) lists Bureau of Indian Affairs General Assistance as a
Tribal qualifying program. The model has no BIA-specific variable, so
agency cash aid reported under general_assistance by a household on
Tribal land stands in for it (25 CFR 20.300-20.303).

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The middle household lives on Tribal land and is enrolled in Medicaid, so
under 47 CFR 54.409(b) it is eligible.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review — PR #9378: FCC Lifeline/ACP categorical eligibility

Add Federal Public Housing Assistance (FPHA) as a Lifeline/ACP categorical-eligibility
route and fix Tribal eligibility from exclusive to additive.

Headline: Both changes are regulatorily verified correct against 47 CFR §54.409, and
CI is green (33/33). There are no critical findings. The should-address items are
citation-precision and ACP test coverage — none of them block correctness.

Program review

Two changes, both in the FCC federal rule tree (gov/fcc/...):

  1. FPHA categorical eligibility. Adds the pre-existing receives_housing_assistance
    (bool, SPMUnit; variables/gov/hud/receives_housing_assistance.py) to the non-tribal
    list in gov.fcc.lifeline.categorical_eligibility, encoding §54.409(a)(2)'s "Federal
    Public Housing Assistance". The variable is reused, not reinvented, and is already the
    accepted FPHA proxy elsewhere in the model (LIHEAP, CT TFA, RI Works, CA OC general
    relief). Because is_acp_eligible.py reads the same Lifeline list, FPHA flows into ACP
    automatically — regulatorily correct (47 U.S.C. §1752(a)(6)(A) qualifies a consumer who
    participates in a Lifeline-qualifying program).

  2. Tribal-additive fix. Replaces the exclusive
    np.where(is_on_tribal_land, tribal>0, non_tribal>0) with the additive
    (non_tribal>0) | (is_on_tribal_land & (tribal>0)) in BOTH is_lifeline_eligible.py
    and is_acp_eligible.py. The old exclusive form was a genuine pre-existing bug: a
    Tribal-land household enrolled in a standard program (Medicaid/SNAP/SSI/FPHA) read ONLY
    the Tribal list and was wrongly denied. The new form encodes §54.409(b) exactly — meets
    (a) OR participates in a Tribal-specific program — and correctly guards the Tribal branch
    with is_on_tribal_land & so off-reservation households are not granted eligibility via
    the Tribal-only list.

Source documents

  • 47 CFR §54.409 — Consumer qualification for Lifeline. Verified via eCFR
    (versioner 2026-01-01). §54.409(a)(2) enumerates exactly five program-based routes:
    Medicaid; SNAP; SSI; Federal Public Housing Assistance; Veterans and Survivors Pension
    Benefit. §54.409(b) makes the Tribal list additive to (a), not a replacement. Both changes
    are corroborated by the confirmed regulatory text.
  • 47 U.S.C. §1752 — ACP statute; a Lifeline-qualifying program qualifies a consumer for
    ACP, so the §54.409(b) rule and the FPHA route both carry over to ACP correctly.

Critical

None.

Note on reclassification: the test-coverage review initially flagged two ACP-coverage items
as CRITICAL (the ACP Tribal-additive fix guarded only by a monkeypatched unit test, and the
FPHA→ACP flow untested). Per the review rules, "missing edge-case tests for an
already-tested variable" is SHOULD ADDRESS, not CRITICAL: is_acp_eligible is covered by
test_vectorization.py, and the regulatory review confirms the ACP logic is correct — so
there is no zero-coverage formula and no confirmed defect. Both items are downgraded to
SHOULD ADDRESS below.

Should address

1. categorical_eligibility.yaml reference is section-level, not (a)(2) (reference precision)

parameters/gov/fcc/lifeline/categorical_eligibility.yaml carries a valid structured
title/href dict citing §54.409, and it corroborates the whole list — but it stops at the
section, not the (a)(2) subsection where the five-program list (including FPHA) actually
lives. Tighten the title, e.g.:
title: 47 CFR § 54.409(a)(2) - Program-based qualification (Federal Public Housing Assistance).
The Cornell href already resolves; no URL-health issue.

2. §54.409(b) Tribal-additive basis is comment-only — not in any structured reference (reference precision)

The substantive logic change encodes §54.409(b), but that basis is cited only in inline code
comments (and the changelog):

  • is_lifeline_eligible.py: reference is section-level §54.409 (contains (b), but does
    not pinpoint the subsection).
  • is_acp_eligible.py: reference cites only 47 U.S.C. §1752 and does not reference
    §54.409 at all, despite now running its (b) logic.
    Add a §54.409(b) CFR reference to the bare-string ref list in both variables (bare strings
    may be a list), e.g. "https://www.law.cornell.edu/cfr/text/47/54.409#b".

3. ACP Tribal-additive fix has no end-to-end YAML test (downgraded from CRITICAL)

The ACP formula change is guarded only by
test_vectorization.py::test_acp_lifeline_categorical_eligibility_is_vectorized, which uses
a monkeypatched fake parameter tree and fake_add. It exercises the boolean algebra but
bypasses the real parameter list and add()/entity plumbing, so a parameter-list regression
would slip through. Add a YAML case to acp/is_acp_eligible.yaml for a Tribal-land household
that qualifies via a standard (non-tribal) Lifeline program (e.g. Medicaid) above 200% FPG —
is_acp_eligible: true. This case fails under the old exclusive np.where.

4. FPHA→ACP flow is untested (downgraded from CRITICAL)

FPHA now flows into ACP via the shared Lifeline list, but there is no test — not in
is_acp_eligible.yaml, not in integration.yaml, and not in the Python unit test (whose
fake tree hardcodes the Lifeline list as ["medicaid_enrolled"]). Add a YAML case to
acp/is_acp_eligible.yaml: receives_housing_assistance: true above 200% FPG →
is_acp_eligible: true. Fails before FPHA was added to the list.

5. No FPHA negative control in Lifeline

The Lifeline suite proves FPHA → eligible but has no paired control showing the SAME
above-135%-FPG household WITHOUT receives_housing_assistance is ineligible. The existing
"no categorical eligibility >135% FPG is ineligible" case is close but does not set
receives_housing_assistance: false. Either add that input to the existing case, or add a
control mirroring the new lifeline.yaml $60k case with receives_housing_assistance: false
and is_lifeline_eligible: false. Related hardening: two control cases (the Lifeline
"no categorical eligibility" and "Tribal + no categorical eligibility" cases) do not zero the
new inputs (snap, ssi, receives_housing_assistance), so a future default-input change
could silently flip them eligible via FPHA.

Suggestions

  • Veterans & Survivors Pension Benefit (follow-up). §54.409(a)(2)'s fifth program-based
    route remains unimplemented — a clearly-labeled in-file comment in
    categorical_eligibility.yaml. This is a pre-existing, documented gap the PR does not
    worsen (the PR is net-additive), and there is no modeled VA-pension-receipt input to wire
    up today. Track as a follow-up issue (add an upstream input, then append it here); do not
    block this PR.
  • Deep-link anchors. Cornell CFR pages support subsection anchors (#a_2, #b) so a
    click lands on the exact provision. Minor polish.
  • Optional FR provenance. eCFR CITA shows §54.409 amended through 88 FR 84447 (Dec 5
    2023) and 89 FR 70124 (Aug 29 2024); a Federal Register cite could be added if desired.
    Not required — the standing CFR text is authoritative.
  • Optional documentation. A one-line comment in is_acp_eligible.py noting the Lifeline
    categorical list (incl. FPHA) is intentionally inherited would make the ACP side-effect
    explicit.
  • Pre-existing code tidiness (not introduced). is_acp_eligible.py still computes its
    own ACP list via np.any([add(...) for ...], axis=0) while the edited Lifeline block uses
    the cleaner add(...) > 0; a future cleanup could align them. np is now unused in
    is_lifeline_eligible.py (no lint impact — F401 ignored). No action needed.

Validation summary

  • CI: 33/33 green.
  • Both regulatory changes verified correct against the eCFR text of §54.409(a)(2) and (b).
  • FPHA path (Lifeline) is well-pinned at both unit (is_lifeline_eligible.yaml) and
    integration (lifeline.yaml: MA, $60k, housing assistance → full $9.25×12) layers, and
    fails under old code.
  • Tribal-additive fix (Lifeline) is pinned by YAML that flips false→true under the fix.
  • Code change is fully vectorized, identical across both files, correctly parenthesized;
    entity levels consistent; no hard-coded values; changelog .fixed fragment present and
    cites §54.409(a)(2) and (b).
  • Gaps are ACP end-to-end coverage (items 3–4) and Lifeline FPHA negative control (item 5).

Review severity

REQUEST_CHANGES — 0 critical, but several should-address items (2 citation-precision,
3 test-coverage). Per the standing rule, any should-address item maps to request-changes.
The correctness of both changes is not in question and CI is green; the requested changes are
reference precision and ACP test coverage.

Counts: 0 critical / 5 should-address / 5 suggestions.

Next steps

Run /fix-pr 9378 to apply the should-address items: tighten the two §54.409 references
(item 1–2), add the two ACP YAML cases (items 3–4), and add the Lifeline FPHA negative
control plus zero the new inputs on the two control cases (item 5). Open a follow-up issue
for the Veterans & Survivors Pension route.

🤖 Generated with Claude Code /review-program

…controls

- Cite 47 CFR 54.409(a)(2) on the Lifeline categorical list and 54.409(b)
  on is_lifeline_eligible and is_acp_eligible.
- Add ACP YAML cases for a Tribal-land household qualifying through
  Medicaid and for the FPHA route.
- Zero the new inputs on the Lifeline no-categorical-eligibility controls
  and add a no-housing-assistance mirror of the $60k integration case.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@DTrim99

DTrim99 commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Program Review (re-review) — FCC Lifeline FPHA categorical eligibility

Source

Response to prior review

The author pushed commit af33835c to address the prior 5 should-address items.

  • Item 1 — categorical_eligibility.yaml ref section-level not §54.409(a)(2): RESOLVED. The parameter reference was upgraded from 47 CFR § 54.409 / .../54.409 to the pinpoint 47 CFR § 54.409(a)(2) / .../54.409#a_2. Correct provision for the FPHA (receives_housing_assistance) qualifier.
  • Item 2 — is_acp_eligible.py cited only §1752, not §54.409: PARTIALLY RESOLVED. The ACP variable reference was upgraded from a single §1752 string to a list, and §54.409(b) was added. But only the Tribal-additive paragraph (b) was cited; the standard-program provision §54.409(a)/(a)(2) — which contains FPHA and the (a)-route the reworked formula now enables for all households — is still not cited. See Should Address Basic prototype #1.
  • Item 3 — ACP Tribal-additive fix had no end-to-end YAML test: RESOLVED. is_acp_eligible.yaml gained the key boundary case "Tribal family enrolled in Medicaid is eligible … above 200% of FPG, through the standard Lifeline programs" (all Tribal programs zeroed) — exactly the OR-logic case the old np.where would have wrongly excluded.
  • Item 4 — FPHA→ACP flow untested: RESOLVED. Added "Family reporting Federal Public Housing Assistance receipt is eligible … above 200% of FPG, due to Lifeline categorical eligibility" (receives_housing_assistance: true, fcc_fpg_ratio: 2.1, other programs zeroed → eligible).
  • Item 5 — No FPHA negative control in Lifeline: RESOLVED. Added positive FPHA cases (non-Tribal >135% FPG; Tribal via base route) plus negative controls that explicitly set receives_housing_assistance: false in is_lifeline_eligible.yaml, and an end-to-end negative in lifeline.yaml ("Single adult above 135% of FPG without housing assistance is ineligible").

Critical (Must Fix)

None.

The two formula variables (is_lifeline_eligible, is_acp_eligible) and the new parameter entry all have direct positive and negative test coverage, including the flipped OR-logic elements. No zero-coverage formula variable, and no regulatory or value error: FPHA is correctly placed as a §54.409(a)(2) BASE qualifier (not mis-filed to Tribal), the Tribal OR-logic fix is monotonic (no household loses eligibility), ACP correctly inherits FPHA at 200% FPG, and receives_housing_assistance is reused rather than reinvented.

Should Address

  1. is_acp_eligible.py:14-19 — ACP reference omits §54.409(a)/(a)(2), the provision containing the standard-program (incl. FPHA) routes the change now relies on. The list cites §1752 (top authority) and §54.409#b (Tribal-additive) only. Because §1752(a)(6) reaches eligibility by cross-reference to both subsection (a) and (b), and the reworked formula makes ACP eligibility flow through the standard §54.409(a)(2) programs (including the newly counted FPHA) for all households, add https://www.law.cornell.edu/cfr/text/47/54.409#a_2 to the reference list. As written, a reader clicking the CFR link lands on the Tribal paragraph and never sees the standard-program provision that actually drives the (a)-route eligibility this PR enables. (This is the residual of prior Item 2.)
  2. is_acp_eligible.yaml — no ACP negative control that explicitly zeroes receives_housing_assistance. The Lifeline file gained both an FPHA-false unit case and an end-to-end FPHA-false case, but the ACP file's only always-false baseline ("Family with no qualifications is ineligible") does not set receives_housing_assistance: false; it passes only because the variable defaults to false. Per the prior review's own control standard (controls should explicitly zero the new input so a future default change cannot silently mask the route), add receives_housing_assistance: false to the ACP ineligible case. Edge test on an already-tested variable → SHOULD ADDRESS, not critical.

Suggestions

  1. is_lifeline_eligible.py:8-12 — uses subsection-level #a anchor rather than the pinpoint #a_2. The categorical logic keys off the standard-program list in §54.409(a)(2) (the parameter file now cites #a_2). #a still lands in the correct provision, so this is a vagueness refinement, not a wrong-section error; consider .../54.409#a_2 to match the parameter file. (#b for the Tribal route is already correct.)
  2. tribal_categorical_eligibility.yaml — align the tribal parameter reference to the anchored form. The non-tribal parameter was upgraded to #a_2; the tribal parameter still points at bare .../54.409 with a generic title. For symmetry, point it at .../54.409#b titled 47 CFR § 54.409(b). Cosmetic; the tribal list contents (head_start, fdpir, tanf) already correctly match §54.409(b).
  3. acp/categorical_eligibility.yaml — lacks unit: list metadata that the lifeline lists carry (pre-existing, not touched by this PR). Out of scope; noting for a future tidy-up.
  4. A single 2026 eligibility assertion in is_lifeline_eligible.yaml would confirm the FPHA route is period-stable at the eligibility layer, not only via the 2026 dollar-amount test in lifeline.yaml. The FPHA/OR-logic eligibility cases currently all use 2022.
  5. An end-to-end (people/spm_units) FPHA case for ACP would add symmetry with the Lifeline end-to-end cases; not required, since the ACP formula reuses the same Lifeline-categorical computation already exercised end-to-end on the Lifeline side.
  6. categorical_eligibility.yaml:1 description style (pre-existing) does not end with a period nor use a standard verb form. Out of scope here; noting for a future cleanup.

Validation Summary

Check Result
Regulatory PASS — FPHA correctly a §54.409(a)(2) BASE qualifier; Tribal OR-logic fix correct and monotonic; ACP inherits FPHA at 200% FPG
References PASS with 1 should-address — parameter pinpoint fixed (Item 1); ACP variable still omits §54.409(a)/(a)(2)
Code patterns PASS — additive-OR rewrite vectorization-safe (`
Test coverage PASS with 1 should-address — flipped OR-logic elements directly asserted; ACP ineligible control should zero receives_housing_assistance
CI status Passing (33/33)

Branch Status

⚠ PR branch is 15 commits behind main. Consider rebasing before merging. Review was scoped to the PR's actual changes — staleness did not affect findings.

Review Severity: APPROVE

No critical findings. FPHA is correctly modeled as a §54.409(a)(2) base qualifier, the Tribal OR-logic fix is regulatorily correct and provably non-regressive, and all four of the prior review's blocking test/citation gaps (Items 1, 3, 4, 5) are resolved. The two remaining should-address items are minor and non-blocking: a supplementary CFR pinpoint on the ACP variable (residual of Item 2) and an explicit-zero on one ACP negative control. Recommend approving with those two as follow-ups.

🤖 /review-program

@DTrim99 DTrim99 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approving — the prior blocking items (regulatory correctness, the FPHA→ACP and Tribal-additive end-to-end tests, and the Lifeline FPHA negative control) are resolved. The two remaining should-address items (a supplementary §54.409(a)(2) pinpoint on the ACP variable, and an explicit receives_housing_assistance: false on one ACP control) are minor, non-blocking follow-ups. Full findings posted above. 🤖 /review-program

@hua7450
hua7450 merged commit 5d50019 into PolicyEngine:main Sep 3, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants