Skip to content

feat(S5+S6): Population Dashboard (Director) + Task assignment/real-time FHIR Subscription - #7

Merged
manjula25 merged 11 commits into
mainfrom
feature/caresync-s6-realtime-assignment
Jul 5, 2026
Merged

feat(S5+S6): Population Dashboard (Director) + Task assignment/real-time FHIR Subscription#7
manjula25 merged 11 commits into
mainfrom
feature/caresync-s6-realtime-assignment

Conversation

@manjula25

Copy link
Copy Markdown
Collaborator

Summary

main does not yet contain S5 (PR #6 merged S5 into the S4 branch instead of main, apparently by
mistake, breaking the usual one-PR-per-slice pattern used by PRs #3-#5). This PR targets main
directly per the repo's documented convention and brings in S5 and S6 together in one combined
diff, catching main up to the current tip.

S5 — Population Dashboard + drill-in (Director):

  • Director home route (W02): population risk×urgency scatter (native Canvas, no chart library),
    critical-zone count, projected cost-avoidance, team KPIs — from a new audited population aggregate
    API over HAPI.
  • Deterministic 500-patient cohort generator (seeded, no Math.random()) with per-patient
    RiskAssessment, replacing the S1-deferred Synthea load.
  • Scatter quadrant click drills into a filtered patient list, then into the existing patient detail view.
  • Chunked FHIR import ($batch) fixing a real headers-timeout bug on large cohort loads.

S6 — Task assignment + real-time FHIR Subscription (GD7):

  • PATCH /api/tasks/:id/assign — Director-scoped, audited Task.owner update (logical reference, not
    a literal Practitioner/{id} — this POC seeds no Practitioner resources).
  • A real HAPI FHIR Subscription (rest-hook on Task create/update), bootstrapped idempotently at boot.
  • A webhook receiver + in-process SSE relay hub, wired so an assignment updates the Coordinator's "My
    Patients" panel live and shows a toast — no manual refresh.
  • HAPI's actual rest-hook delivery shape (verb/path-mimicking PUT {endpoint}/Task/{id}, occasional
    double-delivery) was discovered via live investigation and is now documented in code + verification.md.
  • New Playwright spec driving the real (unmocked) Subscription end to end.

Test plan

  • cd apps/api && npx jest --runInBand — 27 suites / 119 tests passed
  • cd apps/web && npm test -- --run — 13 files / 116 tests passed
  • tsc --noEmit clean for both apps/api and apps/web
  • npm run lint clean for both apps (0 errors; pre-existing warnings only, none in changed files)
  • Playwright E2E: 9/9 passing, including a real (unmocked) HAPI Subscription flow
    (coordinator-live-assignment.spec.ts) and the S5 Director population flow
  • Two-axis code review (Standards + Spec) run for S6 — 0 blocking findings
    (docs/plans/caresync-ai/review.md)
  • verification-before-completion PASS for both slices
    (docs/plans/caresync-ai/verification.md, verification-s5.md)

Full evidence, spec-drift checks, and documented deviations are in
docs/plans/caresync-ai/verification.md and docs/superpowers/specs/feature-caresync-s6-realtime-assignment/2026-07-05-changelog.md.

🤖 Generated with Claude Code

manjula25and others added 11 commits July 5, 2026 15:27
A1: deterministic ~500-patient cohort (diabetes/CHF/depression) with
per-patient RiskAssessment from a documented heuristic and US Core
race/ethnicity; wired into the existing $batch import (idempotent PUT).
B1: roleHome routes Director -> /population; Director-only /population
route via RoleGuard role prop. Placeholder Population page pending B2.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A2: audited population/ aggregate service over HAPI (RiskAssessment +
Encounter, paginated via _count + link[rel=next]) — getPopulationScatter
and getPopulationSummary, Director-only (explicit role check + denial
audit, since hasScope grants coordinator the same domains), cost-avoidance
from a pure documented formula over real risk counts. Also fixes
import-fhir.ts to chunk the $batch POST (250/request) so the ~2500-entry
cohort import no longer exceeds the client's headers timeout.
B2: W02 Population Dashboard — native Canvas risk x urgency scatter (no
chart library, GD10), KPI tiles computed from the summary API, mockup
fidelity ~83% against reference-materials/caresync-population.html with
deviations documented in Population.tsx/PopulationScatterChart.tsx
(Care Team/HEDIS/Activity as S6+ placeholders, no fabricated numbers).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Clicking a scatter point's risk/urgency quadrant (thresholds >=60/>=60,
reusing lib/patient.ts's amber-risk cutoff) filters the already-fetched
scatter array client-side and navigates to /population/patients with the
filtered ids in router state. pixelToQuadrant/unprojectPoint are exact
inverses of the existing paint projection, so hit-testing can't drift from
what's drawn. The new list page reuses the existing GET /api/patients/:id
per id (useQueries, isolated failures) and links into the unmodified
PatientDetail route — no new backend endpoint. Also hoists the shared
risk-dot Tailwind class map (previously duplicated in PatientPanel) into
lib/patient.ts as RISK_DOT_CLASS.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Login -> W02 dashboard (asserts computed KPI values, not the mockup's
hardcoded 23/$247,400/847) -> click the critical risk/urgency quadrant on
the native Canvas scatter -> filtered "Critical -- Act Now" list ->
PatientDetail. Drives the click via the same padding/threshold constants
the scatter paints with, so the test can't drift from the real projection.
Passes standalone and under the full 5-worker parallel suite (bumped the
KPI-tile assertion timeout to 15s -- the population aggregate bulk-reads
~500 patients from HAPI and is the suite's slowest fetch).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Rotates the prior slice's verification.md/review.md to verification-s4.md/
review-s4.md (this repo's established per-slice naming convention) and adds
a fresh verification.md documenting S5's evidence: apps/api 23/23 suites
(106/106 tests, serial) against the live-imported ~500-patient cohort,
apps/web 12/12 files (108/108 tests), and the full Playwright E2E suite
(8/8, including the new Director population flow) both standalone and
under parallel load. Also flips the stale S5 checkboxes in issues.md and
implementation-plan.md now that every acceptance bullet is independently
confirmed against the code and this session's live evidence.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Both axes reviewed as parallel sub-agents over 9e2f01c...HEAD. Standards:
0 hard violations, 4 non-blocking Fowler-smell judgement calls. Spec: 0
missing/wrong acceptance-bullet requirements, 2 low-severity already-
disclosed deviations (procedural cohort vs literal "Synthea"; team KPIs
deferred to S6/S7). No blocking findings — none require a code change
before shipping S5.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Director-scoped task assignment (PATCH /api/tasks/:id/assign, audited),
an idempotent HAPI Subscription bootstrap (rest-hook on Task changes),
a webhook receiver + in-process SSE hub relaying live to /api/events,
and a Coordinator-side toast + panel refresh on assignment — no manual
refresh needed.
Confirmed against the local HAPI (7.2.0) that a rest-hook Subscription
with `channel.payload` set delivers as PUT {endpoint}/Task/{id} (mimicking
the triggering verb+path), not a bare POST to the endpoint as initially
assumed — the webhook route matches that real shape. Also de-dupes the
Coordinator's toast since HAPI delivers a single update's hook twice.
119 API tests, 116 web tests, and the full Playwright E2E suite (9/9,
including a new spec driving the real HAPI Subscription end to end) pass.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Fresh command evidence (119 API tests, 116 web tests, 9/9 E2E incl. a
live real-HAPI Subscription round trip), definition-of-done + spec-drift
checks against issues.md/implementation-plan.md, and a review pass —
most notably documenting the real defect found and fixed this session:
HAPI's actual rest-hook delivery shape (PUT {endpoint}/Task/{id}, body
IS the resource) differs from the plan's POST-to-bare-endpoint
assumption. S6 acceptance checkboxes were stale; corrected to [x].
Prior slice's verification.md preserved as verification-s5.md.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@manjula25
manjula25 merged commit a1f1dc1 into mainJul 5, 2026
manjula25 added a commit that referenced this pull request Jul 8, 2026
Caught by live smoke test (this session, after the PR was open):
the smoke test of gap #4 hit a regression that 281/281 unit tests
couldn't catch — every test file builds its own Express app without
mounting smartAuth, so the mount-order bug in index.ts was invisible
to the suite.
The regression: smartAuth was mounted via
app.use('/api/patients', smartAuth, createPatientsRouter(fhirService))
which meant smartAuth ran BEFORE the route's inner `requireAuth`. A
login JWT (signed with auth/jwt.ts's JWT_SECRET) would hit smartAuth
first, fail signature verification (smartAuth expects SMART tokens
signed with serverSecret), and 401 with `invalid_signature` before
requireAuth ever saw it. Net effect: every legitimate API caller
with a login JWT — the UI, scripts, the test-via-server path — was
locked out of every HAPI-touching route.
Two-part fix:
1. smartAuth no-double-auth pass-through. If `req.auth` is already
set by an upstream middleware (requireAuth), smartAuth calls
next() without validating the SMART shape. This is a 4-line
additive check at the top of the middleware function. Preserves
all 5 existing unit tests (none of them pre-set req.auth) and
adds a 6th test "passes through when req.auth is already set by
an upstream middleware (no double-auth)" that pins the new
behavior with a fake upstream.
2. Mount order: smartAuth is now wrapped INSIDE each router, AFTER
requireAuth. New helper `wrapRouterWithSmartAuth(router, smartAuth)`
in smartAuth.ts does `router.use(smartAuth)` post-construction.
index.ts mounts become
app.use('/api/patients', wrapRouterWithSmartAuth(createPatientsRouter(fhirService), smartAuth))
— same shape for all 10 HAPI-touching prefixes. The error
handler remains mounted globally at the bottom of index.ts.
Net result: login JWT → requireAuth passes → smartAuth no-double-auth
passes → route handler runs. SMART-shape tokens still hit smartAuth's
full validation (when requireAuth is taught to accept them too —
tracked as follow-up #7 in verification-s14.md).
Live smoke test (npm run dev):
- Login JWT → GET /api/patients/maria-chen → 200 ✓ (was 401)
- No token → 401 {"error":"Missing bearer token"} (caught by requireAuth)
- Garbage SMART-shape token → 401 {"error":"Invalid or expired token"} (caught by requireAuth; smartAuth never runs)
Test suite: 282/282 (was 281; the new pass-through test is the +1).
tsc clean.
Note: this is a real regression introduced by Commit 4 (5e73c68).
The Commit 4 self-review claim that "tests are unchanged because each
test file builds its own Express app without mounting smartAuth" was
true at face value but missed the production mount order. The unit
tests for the middleware itself were always green; the regression
lived in the integration glue (index.ts). This is a useful lesson
for S15: mount-order bugs don't surface from per-route test apps.
Follow-up #7 (verification-s14.md): requireAuth should also learn to
accept SMART-shape tokens so the two tiers fully interoperate. Out
of scope for this fix — the immediate regression is closed.
manjula25 added a commit that referenced this pull request Jul 8, 2026
Three doc updates to match the now-published reality (vs the
pre-smoke-test state when verification-s14.md + the changelog were
first written):
1. verification-s14.md §6 — two new follow-ups:
- #7 (FIXED in f8d0862): the mount-order regression the live smoke
test caught. Records the methodology lesson (per-route unit tests
are blind to mount-order bugs; future slices need an integration
smoke test against npm run dev).
- #8 (out of scope): requireAuth itself still rejects SMART-shape
tokens, so SMART-token-only callers would 401 at requireAuth
before smartAuth ever runs. Asymmetry left by the f8d0862 fix.
2. review-s14.md — added a "Post-review update (commit f8d0862)"
paragraph that discloses the regression caught after the PR was
open and the fix.
3. changelog — added commits 9-11 (changelog + regression fix), the
"Live smoke tests" section, follow-up #7 + #8, and the verification
count update (281/281 → 282/282 with the new pass-through test).
PR description was also updated (separately, via gh pr edit) to
include a "🚨 Regression caught post-PR (and fixed in f8d0862)"
section + the new follow-ups + the live smoke-test evidence.
Unrelated pre-existing uncommitted changes in apps/web/ (PatientDetail.tsx
+ MyPatients.test.tsx) are NOT part of this commit; they were left
in the working tree from an earlier session.
Sign up for freeto 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.

1 participant

@manjula25