Uh oh!
There was an error while loading. Please reload this page.
feat(S5+S6): Population Dashboard (Director) + Task assignment/real-time FHIR Subscription - #7
Merged
Merged
Conversation
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 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.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for freeto join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
maindoes not yet contain S5 (PR #6 merged S5 into the S4 branch instead ofmain, apparently bymistake, breaking the usual one-PR-per-slice pattern used by PRs #3-#5). This PR targets
maindirectly per the repo's documented convention and brings in S5 and S6 together in one combined
diff, catching
mainup to the current tip.S5 — Population Dashboard + drill-in (Director):
critical-zone count, projected cost-avoidance, team KPIs — from a new audited population aggregate
API over HAPI.
Math.random()) with per-patientRiskAssessment, replacing the S1-deferred Synthea load.S6 — Task assignment + real-time FHIR Subscription (GD7):
PATCH /api/tasks/:id/assign— Director-scoped, auditedTask.ownerupdate (logical reference, nota literal
Practitioner/{id}— this POC seeds noPractitionerresources).Patients" panel live and shows a toast — no manual refresh.
PUT {endpoint}/Task/{id}, occasionaldouble-delivery) was discovered via live investigation and is now documented in code +
verification.md.Test plan
cd apps/api && npx jest --runInBand— 27 suites / 119 tests passedcd apps/web && npm test -- --run— 13 files / 116 tests passedtsc --noEmitclean for bothapps/apiandapps/webnpm run lintclean for both apps (0 errors; pre-existing warnings only, none in changed files)(
coordinator-live-assignment.spec.ts) and the S5 Director population flow(
docs/plans/caresync-ai/review.md)verification-before-completionPASS 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.mdanddocs/superpowers/specs/feature-caresync-s6-realtime-assignment/2026-07-05-changelog.md.🤖 Generated with Claude Code