Skip to content

feat(S7): Role-filtered task queue + task actions - #8

Merged
manjula25 merged 11 commits into
mainfrom
feature/caresync-s7-task-queue
Jul 6, 2026
Merged

feat(S7): Role-filtered task queue + task actions#8
manjula25 merged 11 commits into
mainfrom
feature/caresync-s7-task-queue

Conversation

@manjula25

Copy link
Copy Markdown
Collaborator

Summary

  • Role-filtered task queue (M02) and task detail (M03): a Social Worker sees only SDOH-domain tasks, a Coordinator sees all; opening a task shows justifying patient context + citations, with Complete/Defer/Escalate and a tel: Call action.
  • W13 built as an honest nav-only shell per plan.md's locked GD9 scope tier (not one of the 6 demo-critical screens); the "mobile completion syncs to web" requirement is instead satisfied on the already-demo-critical PatientDetail.tsx via a new broadcast relay event.
  • Two plan-review findings caught and resolved during implementation via empirical verification, not discovered after the fact: citation persistence (FHIR Task.reasonReference silently truncates multi-value arrays on this HAPI version; switched to Task.input) and the GD9/W13 scope conflict noted above.

Test plan

  • cd apps/api && npx jest --runInBand — 147/147
  • cd apps/web && npx vitest run — 122/122
  • cd apps/web && npx playwright test --workers=1 — 13/13
  • npx tsc --noEmit clean in both apps/api and apps/web
  • npm run lint clean in both apps (pre-existing warnings only, none in touched files)
  • Full docs/plans/caresync-ai/verification-s7.md (Phase C gate: PASS) and review-s7.md (Standards + Spec axes, both findings fixed pre-merge)

🤖 Generated with Claude Code

manjula25and others added 11 commits July 5, 2026 23:43
Record PWA/responsive web as the locked GD4 decision in plan.md, prd.md,
issues.md, and implementation-plan.md — the pre-work gate for Iteration 7
required this before any S7 code lands.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Plan review found Task carries no field distinguishing SDOH from clinical
work today (category/code/reasonCode/businessStatus all unset), so A1's
role filter had nothing to key off. Adds A0: Action Planner self-reports
domain per task (reusing the existing ResourceDomain vocabulary), threaded
through Task.category on write and mapTaskResource/getTasks on read.
Legacy Tasks without category fail open (visible to every role).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds a per-task care domain ('clinical' | 'sdoh') so S7 A1 can role-filter
the task queue (Social Worker → SDOH-only). The Action Planner self-reports
domain in its plan_tasks structured output (it knows which upstream finding
each task synthesizes); it is threaded through ActionPlannerTaskInput and
written on the FHIR Task, read back via a shared extractTaskDomain helper
in mapTaskResource/getTasks, exposed as TaskSummary.domain, and carried on
the analysis SSE task event.
Storage: a second meta.tag coding (system .../fhir/task-domain), mirroring
CARESYNC_TASK_TAG — NOT Task.category, which the plan originally named but
which FHIR R4 Task has no element for (HAPI 7.2.0 silently drops it,
verified). External contract is unchanged: domain surfaces as
TaskSummary.domain.
Fail-open: Tasks predating this field carry no domain tag and map to
domain: undefined (never a default), so A1 can treat an uncategorized Task
as visible to every role.
Tests (Jest + real HAPI): createTask writes the domain tag (read-back by
id); mapTaskResource extracts it and returns undefined when absent; getTasks
maps the untagged seed Task to undefined. All existing ActionPlannerOutput
fixtures updated for the now-required domain field. 123/123 API tests pass
on a serial run (jest --runInBand).
Note: `npm run test:api` (parallel workers) intermittently times out
integration tests due to pre-existing shared-HAPI contention at the default
5s timeout — reproducible on suites A0 never touched, and green when run
serially. Flagged for a maxWorkers/timeout decision in a later task.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Adds GET /api/tasks: Social Worker sees only sdoh-domain + uncategorized
Tasks (fail-open per A0), Coordinator/Director see everything. FhirReadService
.listTasks reads the fixed coordinator-demo-panel group (same panel
getAssignedPanel uses) via Patient/{id}/$everything rather than
Task?subject=... search — verified directly against the local HAPI that
search lags behind a just-written Task while $everything doesn't, which
matters here since this is a live read path, not just test cleanup.
TDD: tests written first (red on missing route, then red again on the HAPI
search-lag race before switching to $everything), green after. 126/126 API
tests pass serially (npx jest --runInBand).
Follow-up flagged, not fixed here: getAssignedPanel's taskCount still reads
via Task?subject=... search and has the same latent lag risk.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Adds PATCH /api/tasks/:id/status handling complete/defer/escalate. FHIR R4
Task.status has no native "deferred"/"escalated" value, so: complete sets
status=completed; defer sets status=on-hold + businessStatus="Deferred";
escalate leaves status as-is + businessStatus="Escalated" + priority=urgent.
Read side (mapTaskResource/getTasks/listTasks) now prefers businessStatus.text
over the generic status map so the queue shows the distinct labels.
Authorization generalizes A1's per-task domain filter to a write: fetch the
Task, check its domain against the actor's scope, fail-open on undefined
domain — not a guard(actor, 'clinical', ...) gate, since that would wrongly
block a Social Worker from their own sdoh tasks.
TDD: tests written first (red on missing route), green after. 136/136 API
tests pass serially (npx jest --runInBand); tsc --noEmit clean.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New /tasks route (TaskQueue.tsx), built against
reference-materials/caresync-mobile.html's phone-shell mockup. Social
Worker's roleHome now points here instead of the /coming-soon placeholder;
Coordinator reaches it via a new header nav link.
Backend: listTasks (A1) extended with patientId/patientName/conditionTag per
task — free, since the $everything bundle it already fetches contains the
Patient and Condition resources too. Scoped to listTasks only; TaskSummary
and getTasks/mapTaskResource are unchanged.
Chrome scope (html-mockup-fidelity): phone-frame + decorative status bar
kept for GD4 visual identity; segment tabs, bottom tab bar, bell/badge, back
button, and the pinned risk-summary sheet omitted (no backing data/screens
yet). "Done" (-> A2 complete) is the only wired card action — Call and
Defer/Escalate stay scoped to B2's task-detail screen per the plan's own
architecture note.
New Playwright spec covers Social Worker (sdoh/uncategorized only, completes
one via Done) and Coordinator (full unfiltered set via the nav link).
social-worker-denied.spec.ts and coordinator-panel.spec.ts updated for the
new route and nav link. API 137/137, web unit 116/116, Playwright 11/11
(serial), tsc clean in both apps.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
New /tasks/:id route (TaskDetail.tsx) showing justifying patient context,
resolved citations, Complete/Defer/Escalate (A2), and a tel: Call link.
Reachable from a task card in the queue (Done button uses stopPropagation
so it keeps completing in place instead of also navigating).
Citation persistence revisited (plan-review finding): S3's createTask
deliberately did not persist citations onto the FHIR Task (SSE-only). B2
needs them readable later, so this reverses that call. task.fhirResources
now writes as Task.input entries (type.text: 'citation', valueReference per
citation) instead. Task.reasonReference was tried first and rejected: FHIR
R4 defines it 0..1, and HAPI 7.2.0 silently keeps only the first entry of a
multi-value array on write (verified by direct probe against local HAPI) --
exactly the silent data loss GD11 exists to prevent. Task.input (0..*,
native valueReference support) was verified the same way to round-trip
every entry intact.
New GET /api/tasks/:id (getTaskDetail) resolves citations to display
strings and returns the patient's phone (new Patient.telecom seed data --
fabricated demo numbers, all seed data here is synthetic) for the Call
link. Reuses the same fail-open domain-scope rule as A1/A2 (factored into
a shared guardTaskDomain helper).
No mockup exists for M03; built to the design tokens/patterns already
established by TaskQueue.tsx/PatientDetail.tsx.
New Playwright spec opens a probe task with real citations from the queue,
confirms they render, defers the task, and confirms a working tel: link.
API 144/144, web unit 116/116, Playwright 12/12 (serial), tsc clean in both
apps.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Scope correction found and resolved: plan.md's GD9 (locked) classifies
screen W13 as a nav-only shell, not one of the 6 demo-critical
fully-functional screens -- this B3 plan line's own text implied full
functionality, contradicting that. Resolved by honoring GD9: /task-center
(TaskCenter.tsx) is an honest placeholder matching ComingSoon.tsx's
pattern, reachable via a Coordinator nav link. No task table or status
actions were built into W13 itself.
Cross-surface sync moved to PatientDetail.tsx instead (already
demo-critical, already shows a patient's task list), since S7's own
acceptance criteria still require it regardless of W13's tier. EventHub
gained publishAll (broadcast to every connection); the Subscription webhook
now fires a task-updated broadcast on every Task change, assigned or not,
alongside the unchanged owner-scoped assignment event from S6.
PatientDetail subscribes and invalidates its own query when the event's
patientId matches, live-updating with no reload.
New Playwright spec proves a direct status-transition PATCH (simulating a
mobile-queue completion) live-updates an already-open patient tab. API
147/147, web unit 122/122, Playwright 13/13 (serial), tsc clean in both
apps.
This completes S7 Phase B (M02, M03, W13 shell + sync).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Final consolidated verification: fresh API 147/147 (serial), web unit
122/122, Playwright 13/13, tsc clean in both apps. Closes out stale C1/C2
and Definition-of-done checkboxes in implementation-plan.md Iteration 7.
Gate outcome: PASS. See verification-s7.md for full evidence, the
definition-of-done check against issues.md, and the two plan-review
findings from this slice (Task.input citation persistence, GD9/W13 scope
correction).
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Standards: extracted the duplicated patient-context lookup in listTasks/
getTaskDetail into a shared patientContextFromBundle helper. Spec: fixed a
stale doc comment in agent.ts still referencing Task.category, which A0
established doesn't exist in FHIR R4. Both fixed and re-verified (147/147
API tests, tsc clean) before this commit. No open findings on either axis.
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@manjula25
manjula25 merged commit 0194164 into mainJul 6, 2026
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