Appearance
2026-05-08 (continuation) — Phase D provider parity + facility autocomplete fix + CI guard Phase 1
This entry continues the Phase 11 session from earlier the same UTC date (2026-05-08-phase-11-cross-cluster-privatelink). Phase 11 wired the cross-cluster Atlas PrivateLink path from prod → staging. This entry covers everything shipped on top of that read path the same day.
Scope
Three coherent ships:
- Phase D — provider-network-tier fallback. Mirror of the drug-tier fallback shipped earlier today (Eliquis verified). For providers: when CMS returns
coverage=Coveredwithoutnetwork_tier, fill the tier fromproviders_stagingvia the cross-cluster path. - Facility/pharmacy autocomplete fix. User reported searching "Walmart" returned no results. Backend supports all 3 CMS provider types (Individual / Facility / Group), but the UI hook hardcoded
type: "Individual"— every facility-class result silently filtered out for every user. - CI guard Phase 1 — static check. Build-time enforcement that the staging cluster's data-classification posture (per ADR 0004) doesn't drift. Any PR adding a
getReferenceDb()call against a non-allow-listed collection is rejected at CI before merge.
Actor
[email protected] driving from ~/Developer/ask-florence-doctor-rx/; agent: Claude Opus 4.7 (1M context).
Tickets / ADRs
- Created: #106 / ENG-245 (pharmacy network lookup), #107 (drug coverage checker product idea, extracted from #17), #108 (clear-all button)
- Closed: GH #17, #18, #20; Linear ENG-234, ENG-240, ENG-163
- In Progress (Phase 1 done; Phase 2 carved out): #100 / ENG-239 CI guard
- Built on: ADR 0004 (cross-cluster PrivateLink), Phase 11 session log
External systems touched
| System | Account / Project | Delta |
|---|---|---|
AWS prod (039624954211) | prod ECS cluster askflorence-prod | Deploy run 25590973086 registered task def revision 54 + rolled service. Container image baked from merge commit including all three commits below. 2/2 tasks running, rolloutState=COMPLETED. |
| GitHub Actions | n/a | Filed 3 new issues; closed 3 GH issues with closeout comments; CI Workflow Staging Collections Guard (static) ran first time at run 25591499570 on push of CI guard, success in 42s. |
| Linear | Engineering team | Closed ENG-163 (SelectHealth POC), ENG-234 (Phase D); ENG-239 moved to In Progress with progress comment + due 2026-05-15; ENG-245 (NEW pharmacy network) due 2026-05-15 in Data ingestion project, cycle 1; bumped 7 stale due dates to 2026-05-11 (ENG-227, ENG-228, ENG-230, ENG-231, ENG-232, ENG-233, ENG-236). |
| Atlas | n/a | No changes (everything reads through the Phase 11 path). |
What shipped
Phase D — provider-network-tier fallback (commit 1465c6d)
src/lib/provider-network-fallback.ts(NEW) — exact mirror ofdrug-tier-fallback.ts. FunctionlookupStagingProviderNetworks(npiPlanPairs)returningMap<string, ProviderNetworkFallback>keyed by${npi}|${plan_id}. One indexed query per NPI; readsproviders_stagingviagetReferenceDb(). Internal canonical-tier strings mapped to CMS-style strings (Preferred,NonPreferred,Standard, etc.) via small mapping table.src/app/api/providers/covered/route.ts— enrichment loop wired after CMS call. Filters CMSCovered/PartialCoveredentries lackingnetwork_tier; calls fallback; merges. CMS coverage stays authoritative; staging fills only the tier-metadata gap.
Facility/pharmacy autocomplete fix (same commit 1465c6d)
src/app/api/providers/autocomplete/route.ts— server-side fan-out: when caller passestypeomitted or"All", executes 3 parallelprovidersAutocomplete()calls (Individual + Facility + Group), dedupes by NPI, returns merged. Backwards-compatible: a specific type returns the prior single-type behavior.src/lib/hooks/use-doctor-autocomplete.ts—ProviderTypeunion extended to include"Group"+"All"; default changed"Individual"→"All".src/components/plans/CoveragePanel.tsx+src/components/plans/detail/PlanCoverageCheck.tsx— passtype: "All"with explanatory comment.
CI guard Phase 1 — static check (commit 67cb315)
src/lib/db.ts— newSTAGING_ALLOWED_COLLECTIONSconstant (10 collections:formularies_staging,providers_staging,zip_county,plans,regions,plan_years,mrpuf_issuers_staging,mrf_ingest_log_staging,coverage_disagreements_staging,mrf_file_state_staging) +StagingAllowedCollectiontype.scripts/audit/staging-collections-guard.ts(NEW) — zero-dep regex-based static guard. Walkssrc/+scripts/(skippingnode_modules,.next,.git, etc.), finds everyawait getReferenceDb()binding via regex, scans downstream<binding>.collection("…")calls + the inline(await getReferenceDb()).collection("…")form. Cross-checks each collection name against allow-list. Three violation classes:not_on_allow_list(string literal not in set),dynamic_name(variable argument — flagged because static analysis can't verify runtime safety), and inline-call equivalents.- Defensive choices: self-skip (the script literally documents its own regex patterns and would self-flag), comment-stripping (line + block comments stripped before regex scan so doc-strings don't trigger), allow-list duplicated in the script (defense-in-depth — prevents a developer from making the script "self-approve" by importing the source-of-truth list from
db.ts). .github/workflows/staging-collections-guard.yml(NEW) — runs on PRs tomain/staging/doctor-rx-flow+ on push tomain+ on demand. Sets up Node 20, installstsx, runs the guard. On failure surfaces::error::annotations with pointer to ADR 0004.
Diagnostic + new issues filed
scripts/diag/check-walgreens-coverage.js(NEW, commit40a4a3a) — Mongo aggregation script that surfaced the pharmacy-network finding. 20 of 22 retail pharmacy NPIs (Walgreens, CVS, Walmart, Smith's, Harmons) ARE inproviders_stagingwith thousands of plan edges representing medical services they provide. NONE link to SelectHealth plan68781UT0200014. Confirmed by reading SelectHealth's published §1311index.jsondirectly: provider file lists medical providers only; pharmacy network lives at separate RxEOB SPA tool (selecthealth.rxeob.com).- New GH issues: #106 pharmacy network lookup (Part A UX clarity + Part B data ingest), #107 drug coverage checker product idea (extracted from closed #17 to preserve flagship-product framing), #108 clear-all button for saved doctors + medications.
Verification
Phase D — end-to-end on prod
POST askflorence.health/api/providers/covered with Walgreens NPI 1023023066:
plan 42261UT0060023 → coverage=Covered, network_tier="Preferred"
plan 42261UT0060026 → coverage=Covered, network_tier="Preferred"The network_tier="Preferred" field is only populated by lookupStagingProviderNetworks() reading from providers_staging via the cross-cluster path. CMS returned Covered without tier; staging filled it.
Autocomplete fan-out — end-to-end on prod
POST askflorence.health/api/providers/autocomplete:
{"q":"Walmart","zipcode":"84094"}(notype) → 13 results mergingFacility(WALMART INC. NPIs) +Group(SLC WALMART EYE DOCS){"q":"Walgreens","zipcode":"84094"}→ 21 results mergingIndividual(SARAH WALGREEN — actual person named Walgreen) +Facility(WALGREENS #XXXXX)
Pre-fix returned only Individual results, so Walmart returned [].
CI guard static check
- Workflow run 25591499570 on first push to
main— success in 42s on real code. - Synthetic positive test: dropped temporary file
scripts/db/_test_violation.tswith 3 known-bad patterns:All 3 caught with correct diagnosis. Exit code 1 as expected.scripts/db/_test_violation.ts:7 collection: agent_audit_log reason: not_on_allow_list (string literal) scripts/db/_test_violation.ts:14 collection: name reason: dynamic_name (variable arg) scripts/db/_test_violation.ts:20 collection: members reason: not_on_allow_list (inline call) - Cleanup: removed synthetic file → guard returned to ✅ PASS, exit 0.
Deviations from plan
- The user originally said the Walmart issue "could be a separate issue" but I addressed it inline in the Phase D commit since both touch the providers UX and were tightly coupled. Rationalized in the commit message.
- Pharmacy-network finding emerged DURING Phase D verification — wasn't in the original Phase D scope. Filed as separate issue (#106) so it doesn't block the Phase D close-out.
- CI guard Phase 2 (live nightly drift check) carved out as sub-task because the design has an open question: should the live check verify role-based permissions (Atlas API call on
app_read_staging) or do collection enumeration (which would false-positive on legitimate staging-app collections that aren't cross-cluster-readable)? Phase 1 alone already protects the in-code contract.
Compliance posture impact
| Framework | Control | Update |
|---|---|---|
| SOC 2 | CC7.2 (new additional row) | Detection of unauthorized cross-cluster data-flow scope drift |
| SOC 2 | CC8.1 (new row) | Change-management invariant guard for non-prod-isolation |
| SOC 2 | CC8.1 (this session) | Phase D + Walmart fix + CI guard all landed via standard PR/commit/CI/Deploy chain — full evidence trail |
| HIPAA | §164.312(e)(1) | Unchanged — cross-cluster TLS + AWS-backbone path |
| EDE Phase 3 | Environment separation | Reinforced — CI guard algorithmically enforces what was previously hand-discipline |
What was NOT done (deferred)
- CI guard Phase 2 (live nightly drift check) — sub-task on #100 / ENG-239. Will likely be a separate session; opens the role-permissions-vs-collection-enumeration design question.
- Pharmacy network UX clarity (Part A of #106) — separate issue, due 2026-05-15.
- Pharmacy network data ingest (Part B of #106) — multi-week effort, no due date yet.
- Architecture refresh (#102) — P2, separate session.
- Re-validate §1311 audit at 100% (#92 / ENG-230) — cycle 1, due 2026-05-11.
Rollback
bash
# Code rollback (per commit):
git revert 67cb315 # CI guard Phase 1
git revert 1465c6d # Phase D fallback + autocomplete fan-out
git revert 40a4a3a # Diagnostic script
git push origin main
# Phase D rollback effect:
# - lookupStagingProviderNetworks() unwired from /api/providers/covered
# - CMS coverage stays authoritative; network_tier field becomes empty for
# issuers that don't supply it directly. Drug-tier-fallback unaffected.
# Autocomplete rollback effect:
# - useDoctorAutocomplete() reverts to type: "Individual" default
# - Facility-class providers (pharmacies) silently filtered out again
# - Walmart search returns []
# CI guard rollback effect:
# - Static check no longer runs on PRs
# - Cross-cluster scope drift relies on hand-discipline + ADR review
# - Workflow file CAN be left in place but disabled (comment out the npx step)
# to preserve the SOC 2 CC8.1 evidence trail