Anaya Care Docs

Care Plans & Care Provider Tasks

Part of the Anaya Care product wiki. See 00-overview.md.

Purpose

This module covers two related but separate "care plan" concepts and the task execution pipeline built on top of them:

  1. ClientCarePlan — the narrative/clinical care plan for a client (ADL/IADL approaches, goals, safety adaptations, memory care, health monitoring, escalation protocol). Authored manually by admins or generated by an AI agent pipeline. Versioned, exportable as PDF.
  2. CareProviderTasks — the operational task list ("care plan document" in much of the code/UI) attached to one client schedule. Holds embedded recurring tasks (rrule-based) that caregivers must perform during shifts. Has a DRAFT → PUBLISHED → ARCHIVED lifecycle with version groups.
  3. Task execution — per-shift expansion of published tasks, caregiver submissions on mobile (TaskSubmission), threshold warnings, geofence validation, and family/representative review with comments and escalatable concerns (TaskSubmissionComment).

Backend code lives in apps/backend/src/clients/ (entities, services, controllers); shared AI-generation constants live in packages/shared/src/care-plan/care-plan-generation.ts.

Entities & Data Model

ClientCarePlan — collection client_care_plans

Defined in apps/backend/src/clients/entities/client-care-plan.entity.ts.

FieldTypeNotes
businessObjectId → BusinessRequired, indexed (line 238–244)
clientObjectId → ClientRequired (line 246)
planCarePlanPlan (embedded)escalation, emergencyProtocol, memoryCareSupport?, adl, iadl, adaptationsSafety, healthMonitoring? (lines 196–218). Each domain item carries assessmentScore?, assessmentNotes?, approach, instructions[], goals[], clientNotes[] (lines 19–38)
authorObjectId → UserRequired (line 252)
versionnumberRequired; monotonically increasing per client (line 255)
scheduleIdsObjectId[]Schedules the plan was generated against (line 258)
lastUpdatedDate | null(line 261)
isCompletedbooleanDefault false; AI generation saves with true (ai/agents/care-plan/care-plan-orchestrator.service.ts:353–359)

There is no status field; "the latest care plan (highest version) is considered the approved/published version" (client-care-plans.service.ts:173–176). Updates never mutate — every update/revert creates a new version document (client-care-plans.service.ts:268–329, 357–439).

CareProviderTasks — collection care_provider_tasks

Defined in apps/backend/src/clients/entities/care-provider-tasks.entity.ts (lines 188–259).

FieldTypeNotes
businessObjectId → BusinessRequired, indexed
clientObjectId → ClientRequired
scheduleObjectId → ClientScheduleRequired — one document per client schedule
tasksCareProviderTask[] (embedded)See below
authorObjectId → UserRequired
statusCareProviderTasksStatusDRAFT | PUBLISHED | ARCHIVED (packages/shared/src/enums/domain-status.ts:416–420)
versionnumberDefault 1
publishedAtDate?Set on publish
previousVersionIdObjectId → CareProviderTasksLinks draft to its source
versionGroupIdObjectId → CareProviderTasksGroups all versions of a plan; set to own _id on first create
isLatestPublishedbooleanExactly one per version group should be true
metadataobject?AI generation metadata

Indexes: {business, createdAt}, {business, client, status}, {client, status} (lines 256–258).

CareProviderTask (embedded in tasks[])

Same file, lines 44–152.

FieldTypeNotes
taskIdstringUUID-style app-level ID (not _id; subdocs have _id: false)
templateTypestringJoins to TaskTemplate.type via virtual template (lines 145–150)
taskModeTaskModescheduled (once per occurrence per shift) or tracking (multiple submissions) (domain-status.ts:425–428)
tzidstringRequired IANA timezone
dtstart / rrule / exdateDate / string / Date[]RFC 5545 recurrence; null for tracking tasks
instructionSteps{id, text, isOptional?}[]Checklist shown to caregivers
isRequiredbooleanUsed by shift completion check (shifts/shifts.service.ts:4187–4195)
prioritynumbermin 1; AI generation clamps 1–5 (client-care-provider-tasks.service.ts:235)
metadataobject?e.g. aiGenerated, mealId, populated meal/engagement at read time
filesObjectId[] → LockCareAttachments
excusedOccurrences{occurrenceDate, reason, appointmentId?, excusedBy?, excusedAt}[]Written when a doctor appointment collides with a task occurrence (client-doctors-appointments.service.ts:1276–1330; reason enum TaskExcusalReason.DoctorAppointment, packages/shared/src/types/appointment.ts:195–197)

TaskTemplate — collection task_templates

Defined in apps/backend/src/clients/entities/task-template.entity.ts (lines 167–210).

FieldTypeNotes
categorystringe.g. "Vital Signs"
typestringUnique; referenced by CareProviderTask.templateType and computed medication tasks (MEDICATION_REMINDER)
label, descriptionstringDisplay
taskMode'scheduled' | 'tracking'Default mode for tasks of this type
fieldsMap<string, TemplateField>Dynamic form schema: number/text/textarea/select/radio/multiselect/time/date/file, with min/max/required/warningThreshold (low/high/critical), conditional display (conditionalOn/showWhen) (lines 76–131)
validationRules{rule, message}[]Cross-field rules, e.g. diastolic < systolic (lines 18–25)

Templates are global (no business field) and seeded from apps/backend/src/clients/data/task-templates.json via TaskTemplatesService.populateTaskTemplates() (task-templates.service.ts:68–103); rePopulateTaskTemplates() wipes and reseeds (lines 122–126).

TaskSubmission — collection task_submissions

Defined in apps/backend/src/clients/entities/task-submission.entity.ts.

FieldTypeNotes
business, clientObjectIdRequired
careProviderTasksObjectId → CareProviderTasksParent task document
shiftAssignmentObjectId → ShiftAssignmentRequired — submissions are scoped to a shift (lines 67–72)
taskIdstringEmbedded task's taskId, or virtual medication ID medgrp_<taskGroupId>_<scheduleId> (common/utils/compute-medication-tasks.util.ts:16)
templateObjectId → TaskTemplateRequired
caregiverObjectId → UserRequired
taskModeTaskModescheduled/tracking
submissionNumbernumber?Auto-incremented for tracking tasks (client-task-submissions.service.ts:334–342)
responsesTaskResponseField[]Field answers
statusTaskResponseStatusDRAFT | IN_PROGRESS | SUBMITTED (domain-status.ts:398–402)
startedAt / completedAtDate?Two-step flow timestamps
wasAutoCompletedbooleanSet by shift clock-out / stale cron (see workflows)
location + isLocationValidatedGeoLocation / booleanGeofence check at submit (client-task-submissions.service.ts:82–94)
filesObjectId[] → LockCare
stepCompletions{stepId, completed, completedAt?}[]Schema exists (lines 128–138); written only by the test-data script scripts/complete-shifts-with-ai.ts, not the production services
thresholdWarnings{fieldName, level, value, threshold, message}[]Non-blocking warnings (lines 140–147)

Unique partial index unique_scheduled_task_submission_per_shift on {careProviderTasks, taskId, caregiver, shiftAssignment, taskMode} filtered to taskMode: SCHEDULED — enforces one submission per scheduled task per shift per caregiver (lines 160–173).

TaskSubmissionComment — collection task_submission_comments

Defined in apps/backend/src/clients/entities/task-submission-comment.entity.ts.

FieldTypeNotes
business, taskSubmission, client, authorObjectIdRequired
contentstringmax 2000 chars
isConcernbooleanFlags comment as a concern
concernStatusConcernStatus | nullopenacknowledgedresolved (domain-status.ts:407–411)
acknowledgedBy/At, resolvedBy/At, resolutionNoteaudit fields
attachmentsObjectId[] → LockCare
isDeleted, deletedAt, deletedBysoft deleteSchema only — no delete endpoint exists in task-submission-comments.controller.ts

Workflows & State Machines

How a ClientCarePlan comes into existence

Two paths (both via ClientCarePlansService, routes in clients.controller.ts:1026–1170):

  1. ManualPOST /clients/:clientId/care-plans with a CarePlanPlanDto and scheduleIds; the service normalizes (utils/care-plan-normalization.ts) and saves version = latest+1 (client-care-plans.service.ts:212–266). PUT .../:carePlanId does not edit in place — it creates a new version copying scheduleIds from the original (lines 268–329).
  2. AI generationgenerateCarePlan() enqueues a care-plan-generation BullMQ job (one active job per client; lines 441–546). CarePlanProcessor (processors/care-plan.processor.ts:35–83) delegates to CarePlanOrchestratorService (ai/agents/care-plan/), an agentic loop (validation → context builder → content generator → quality evaluator). The context builder pulls the client narrative, medications, and completed assessments (ai/agents/care-plan/agents/context-builder.agent.ts:111–120). Generation modes fast|standard|thorough and focus areas come from packages/shared/src/care-plan/care-plan-generation.ts (GENERATION_MODE_PRESETS, CarePlanFocusArea, provider map for OpenAI/Anthropic). On success it calls createCarePlan(..., isCompleted=true) (care-plan-orchestrator.service.ts:353–359) and notifies the requesting user over the notification WebSocket (CARE_PLAN_GENERATION_COMPLETED).

Revert: revertCarePlanToVersion hard-deletes all newer versions and re-saves the target content as a new highest version (client-care-plans.service.ts:357–439).

There is no approval state machine on ClientCarePlan — version order is the only lifecycle.

How CareProviderTasks (the task document) is created and published

  • Empty drafts: POST /clients/:clientId/care-provider-tasks/draft creates one DRAFT document per scheduleId, all-or-nothing, rejecting schedules that already have a document (client-care-provider-tasks.service.ts:1031–1104). Emits care-provider-tasks.created.
  • AI generation: POST .../generate requires the client's latest ClientCarePlan to exist and to have scheduleIds (client-care-provider-tasks.service.ts:1404–1434). It auto-targets schedules that don't yet have task documents (or user-selected ones), passes summaries of existing tasks for context, and enqueues a care-provider-tasks-generation job (lines 1452–1586). The processor (processors/care-provider-tasks.processor.ts + ai/agents/care-provider-tasks/) generates tasks, which are saved as DRAFT v1 documents — tracking duplicates deduped, scheduled tasks sorted chronologically, versionGroupId = own _id, metadata.aiGenerated: true (saveCareProviderTasksFromGeneration, lines 182–261).
  • Manual task CRUD (admin web): add task (addTaskToCareProviderTasks, lines 331–385 — validates templateType exists), update task (lines 904–956), delete task with modes all / this (adds exdate) / thisAndFollowing (rewrites UNTIL= in the rrule) (lines 387–545).
  • Publish: only DRAFT documents can publish; previously-published versions in the group are atomically archived and the new one gets isLatestPublished: true + publishedAt (lines 1192–1254). Emits care-provider-tasks.published, which ClientsService.onCareProviderTasksPublished listens to and auto-activates the client on first publish (clients.service.ts:721–743).
  • New draft from published: copies tasks, increments version, links previousVersionId (lines 1256–1300).
  • Revert to a published version: sets it isLatestPublished and hard-deletes all newer non-draft versions (lines 1308–1374).
  • Archive: sets ARCHIVED + isLatestPublished: false (lines 1379–1402).

How tasks reach a caregiver's shift (per-shift expansion)

Tasks are not stored per shift or per day — they are stored once per schedule with rrule recurrence, and expanded at read time:

GET /clients/:clientId/care-provider-tasks/document/shift/:shiftAssignmentId (client-care-provider-tasks.service.ts:755–891):

  1. Loads the shift assignment, computes the shift window from dtstart + durationMinutes in tzid.
  2. Finds the single PUBLISHED + isLatestPublished document for the shift's schedule. If none exists, returns an empty task list (no 404) (lines 798–813).
  3. Filters embedded tasks into the shift window using filterTasksForShiftWindow (common/utils/shift-task-filter.util.ts), which expands dtstart/rrule/exdate with a "fake UTC" workaround for the rrule library's timezone/DST bug.
  4. Excludes any stored legacy MEDICATION_REMINDER tasks; medication tasks are instead computed on the fly from active ClientMedication schedules (computeMedicationTasks, virtual taskId prefix medgrp_) (lines 826–862).
  5. Injects per-shift meal data (ShiftMealAssignmentmetadata.meal for MEAL_PREPARATION tasks) and engagement data (ShiftEngagementAssignmentmetadata.engagement for ENGAGEMENT tasks) (lines 2152–2358).
  6. Attaches this shift's existing TaskSubmissions per task with signed file URLs (lines 2384–2483).

Task submission state machine

  • Scheduled tasks: startTask creates an IN_PROGRESS submission (duplicate blocked by the unique index → 400, client-task-submissions.service.ts:559–568); completeTask validates responses against the template (TaskFieldValidator), checks warning thresholds against client health baselines (TaskThresholdValidator, validators/task-threshold.validator.ts — client-specific baseline overrides template default), validates files, sets SUBMITTED (lines 611–838).
  • Tracking tasks: submitTrackingTask does everything in one step with auto-incremented submissionNumber (lines 157–464).
  • Virtual medication tasks resolve via resolveTask() — a medgrp_-prefixed taskId maps to a ClientMedication task group and uses the MEDICATION_REMINDER template (lines 127–150). Completing one with a medicationChecklist emits MEDICATION_TASK_COMPLETED_EVENT for stock decrementing/notifications (lines 1368–1397).
  • Threshold breaches at high/critical emit THRESHOLD_BREACH_EVENT to the notification module (lines 96–120). Warnings never block submission.
  • Geofence: isLocationValidated is computed against the client's address within GEOFENCE.TASK_SUBMISSION_RADIUS; failure does not block submission (lines 82–94).
  • Auto-completion: at shift clock-out, all IN_PROGRESS submissions for that shift are force-SUBMITTED with wasAutoCompleted: true (shifts/shifts.service.ts:4203–4231); a cron also force-submits any DRAFT/IN_PROGRESS submissions older than the stale threshold every 30 minutes across all tenants (shifts/services/auto-complete-tasks.service.ts:25–53).

Review: comments and concerns

There is no approve/reject status on submissions. Instead, post-hoc review happens via comments (TaskSubmissionCommentsService):

  • Anyone with TASK_COMMENTS_CREATE can comment on a submission; setting isConcern: true opens a concern (concernStatus: open) and notifies all active Admin/Owner users of the business (task-submission-comments.service.ts:55–97, 249–279). Plain comments notify the submitting caregiver and the client's active representatives (lines 281–319).
  • Concerns are acknowledged (open → acknowledged) and resolved (open|acknowledged → resolved with optional note) by users with TASK_CONCERNS_MANAGE; the author is notified at each step (lines 135–205).
  • GET /task-submission-concerns lists/filter concerns business-wide (lines 207–247).

Family/representative read model

RepresentativeTasksService (representative-tasks.service.ts) gates everything on an active RepresentativeProfile link to the client (lines 58–75) and provides: linked clients, daily tasks (all tasks of all latest-published documents + that day's submissions, marked not_submitted/status, lines 90–185), submission history, submission detail, and stats. Stats use a rough estimate expectedTasks = scheduledTaskCount × dayCount — rrule expansion is explicitly skipped (lines 310–323).

Business Rules & Constraints

  • One CareProviderTasks document per schedule — enforced on draft create, draft update, and schedule reassignment (client-care-provider-tasks.service.ts:997–1008, 1051–1063, 1151–1163).
  • Only DRAFT documents can be edited or published; only PUBLISHED + isLatestPublished can spawn a new draft (client-care-provider-tasks.service.ts:1128–1130, 1205–1207, 1271–1281).
  • Exactly one scheduled-task submission per (task, caregiver, shift) — DB-level partial unique index (task-submission.entity.ts:160–173); tracking tasks allow unlimited submissions with submissionNumber.
  • Caregivers may only complete/update/delete their own submissions (client-task-submissions.service.ts:625–629, 966–971, 1069–1073); the shift assignment must belong to the submitting caregiver, client, and business (lines 204–211).
  • AI task generation requires an existing latest ClientCarePlan with scheduleIds (client-care-provider-tasks.service.ts:1413–1434).
  • Adding a task validates the templateType against task_templates (client-care-provider-tasks.service.ts:354–360, 1933–1964).
  • Multi-tenancy: most queries filter business; SuperAdmin (businessId null) bypasses the filter (getBusinessFilter, client-care-provider-tasks.service.ts:134–140). Exception: ClientCarePlan reads/writes filter only by clientId, not business (client-care-plans.service.ts:149–210).
  • Deleting a schedule cascades: deletes all ClientCarePlans referencing it in scheduleIds and all CareProviderTasks documents for it (client-schedules.service.ts:766–769deleteCarePlansByScheduleId / deleteCareProviderTasksByScheduleId).
  • Per-route permissions (BusinessPermission): CARE_TASKS_VIEW_ASSIGNED (reads), CARE_TASKS_CREATE/EDIT/DELETE, CARE_TASKS_GENERATE, CARE_TASKS_PUBLISH, CARE_TASKS_SUBMIT (start/complete/submit), TASK_COMMENTS_CREATE, TASK_CONCERNS_MANAGE (client-care-provider-tasks.controller.ts, client-task-submissions.controller.ts, task-submission-comments.controller.ts).
  • One in-flight generation job per client for both queues; cancellation via CancellationRegistry + job-data flag (client-care-plans.service.ts:459–488, 740–898; client-care-provider-tasks.service.ts:1521–1546, 1671–1802).
  • Threshold warnings prefer client-specific healthBaselines[templateType][fieldName] over template defaults (validators/task-threshold.validator.ts:39–46).
  • Care-plan create/update/delete emits platform activity logs and bumps the client's updatedAt (client-care-plans.service.ts:58–80).

The change-of-conditions cascade (critical pain point)

What exists today, traced from this module's side:

  • APIs that can update an existing care plan: only the manual endpoints — PUT /clients/:clientId/care-plans/:carePlanId (creates a new version), POST .../revert, and re-running AI generation (POST .../generate-care-plan flow via generateCarePlan). All are user-initiated (clients.controller.ts:1050–1105, client-care-plans.service.ts).
  • Nothing updates tasks when the plan changes. There is no listener, hook, or job that reacts to a new ClientCarePlan version by updating CareProviderTasks. createCarePlan/updateCarePlan emit only platform-log events (client-care-plans.service.ts:252–257, 315–320). The only event consumers touching this module are: care-provider-tasks.published → client auto-activation (clients.service.ts:721), and client-meal.rejected/client-meal.unassigned → clearing metadata.mealId from MEAL_PREPARATION tasks (client-care-provider-tasks.service.ts:2526–2561).
  • Change-of-conditions observations don't reach the plan either. The health-observations module emits health-observation.submitted (sole listener: shift-handovers/listeners/handover-trigger.listener.ts:31, which triggers a handover — not a plan update) and health-observation.reviewed, which has no listener anywhere in the backend (health-observations.service.ts:192, 225; repo-wide grep finds no @OnEvent('health-observation.reviewed')). The web "Change of Conditions" page (apps/web/app/(app)/(admin)/dashboard/clients/[id]/edit/change-of-conditions/) is review-only; its review dialog contains no care-plan action.
  • Assessments don't cascade. ClientAssessmentsService emits only platform-log events (client-assessments.service.ts:70–91). Assessments influence care plans only when an admin manually triggers AI regeneration (the context builder reads completed assessments at generation time, ai/agents/care-plan/agents/context-builder.agent.ts:111–120).
  • Net effect — the sync is fully manual: condition changes → someone must (1) regenerate or edit the ClientCarePlan (new version), then (2) separately create a new CareProviderTasks draft, edit tasks, and publish. Worse, generateCareProviderTasks by default skips schedules that already have task documents ("Care provider tasks already exist for all schedules in this care plan", client-care-provider-tasks.service.ts:1480–1489), so post-change regeneration requires explicitly selecting schedules, and the generated output is a brand-new version group rather than an update of the existing one.
  • Exceptions that do auto-propagate (because they're resolved at read time, not stored): medication tasks are computed per shift from current ClientMedication data (client-care-provider-tasks.service.ts:843–861), and meal/engagement details are populated per shift. Medication changes therefore flow to caregivers without touching the task document.

Flagged: plan→task synchronization is absent; see Open Questions #1–3.

Relationship to the approved care proposal

The care proposal (care-proposals/entities/care-proposal.entity.ts:181–203) embeds its own carePlan object with a completely different shape (cover letter, memoryCare[], physicalHealthAndSafety[], etc. as flat id/value lists). Repo-wide search finds no code in the care-proposals module that reads or writes ClientCarePlan — proposal approval does not snapshot into a ClientCarePlan. The clinical care plan is created independently from client data/assessments/schedules. The naming collision is historical. See 03-care-proposals.md.

Relationship to shifts/schedules

Tasks are per-schedule, expanded per shift: the published CareProviderTasks document for a shift's ClientSchedule is filtered into the shift's time window via rrule at read time, and submissions are keyed to the ShiftAssignment (task-submission.entity.ts:67–72). Shift completion checks that all required tasks in the window have submissions (shifts/shifts.service.ts:4187–4195), and clock-out auto-submits in-progress tasks. Doctor-appointment collisions write excusedOccurrences onto tasks. See 05-scheduling-and-shifts.md.

Surfaces (Web & Mobile)

Web (admins / care managers)

  • Care plan: apps/web/app/(app)/(admin)/dashboard/clients/[id]/edit/care-plan/page.tsx — loads the latest plan, edits via a form whose save calls the update endpoint (creating a new version), GenerateCarePlanDialog (mode/focus/provider options from @anaya/shared), generation status indicator (WebSocket + polling), version history at care-plan/[version], compare dialog, and PDF view (_components/view-care-plan-pdf.tsx → backend pdf-stream route, clients.controller.ts:1107–1170).
  • Care provider tasks: apps/web/app/(app)/(admin)/dashboard/clients/[id]/edit/care-provider-tasks/page.tsx — document list per schedule, GenerateAITasksDialog, CreateManualTasksDialog (empty drafts), per-document page [careProviderTasksId]/page.tsx with calendar/timeline views, add/edit/view task dialogs with rrule recurrence fields and instruction-step editor, version management (versions/, _components/care-provider-tasks-versions.tsx).
  • Change of conditions (read-only review of caregiver health observations): .../edit/change-of-conditions/page.tsx.
  • Frontend-only rule: the care-plan page form validates against carePlanPlanSchema client-side before save (care-plan/page.tsx:448–456).

Mobile (care providers)

  • Daily tasks per shift: apps/mobile/app/(client)/[id]/daily-tasks/index.tsx uses useCareProviderTasksByShift (hooks/use-care-provider-tasks.ts:40–44) → GET /clients/:clientId/care-provider-tasks/document/shift/:shiftAssignmentId (lib/care-provider-tasks-api.ts:45); also daily-tasks/shift-calendar.tsx.
  • Task completion: components/care-provider-tasks/task-details-bottom-sheet.tsx drives the full flow — startTask (line 288), completeTask (line 328, shows threshold warnings in the success toast, lines 401–405), and submitTrackingTask (line 454, "tracking tasks can always be submitted again", line 656). File fields upload via POST .../tasks/:taskId/upload-file.
  • Care plan (read-only): apps/mobile/app/(client)/[id]/care-plan/index.tsx renders the latest ClientCarePlan via useLatestCarePlan.

Mobile (family / representatives)

  • Care tab: apps/mobile/app/(app)/(care)/index.tsx + components/care/care-tasks-tab.tsx use useRepresentativeDailyTasksGET /representative/clients/:clientId/tasks/daily (representative-tasks.controller.ts:21); submission detail in components/care/task-submission-detail-sheet.tsx with task-comment-section.tsx rendering comments and concern status badges (open/acknowledged/resolved).

Cross-Module Dependencies

  • Scheduling & shifts (05-scheduling-and-shifts.md): ShiftAssignment scopes per-shift task expansion and every submission; shift completion/clock-out reads and auto-submits task submissions (shifts/shifts.service.ts:4187–4231); schedule deletion cascades into both plan collections (client-schedules.service.ts:766–769).
  • Care proposals (03-care-proposals.md): naming overlap only — no data flow into ClientCarePlan (see above).
  • Medications: virtual medgrp_ tasks computed from ClientMedication (compute-medication-tasks.util.ts); completion emits MEDICATION_TASK_COMPLETED_EVENT consumed by notification/listeners/medication-stock-notification.listener.ts:50.
  • Meals & engagement: ShiftMealAssignment / ShiftEngagementAssignment populate task metadata per shift; client-meal.rejected|unassigned events clear task meal refs (client-care-provider-tasks.service.ts:2526–2561).
  • AI module: care-plan-generation and care-provider-tasks-generation BullMQ queues → orchestrators under ai/agents/care-plan/ and ai/agents/care-provider-tasks/; shared presets in packages/shared/src/care-plan/care-plan-generation.ts.
  • Appointments: doctor-appointment collisions write excusedOccurrences (client-doctors-appointments.service.ts:1276–1330).
  • Notifications: threshold breaches (THRESHOLD_BREACH_EVENT), comment/concern notifications (NotificationTaskCommentType.*), care-plan generation completion via NotificationGateway.
  • Files (LockCare/S3): task attachments and submission files with presigned URLs (lock-care.service, files.service).
  • Clients lifecycle: first task publish auto-activates the client (clients.service.ts:721–743); plan activity feeds platform logs (platform-logs).
  • PDF: PdfService.generateCarePlanPdf (client-care-plans.service.ts:551–623).
  • Shift summaries / reports: shift summary and tracking-summary services read submissions for change-of-condition narratives (shift-summary.service.ts, client-tracking-summary.service.ts) — read-only consumers.

Open Questions & Gaps

  1. No change-of-conditions cascade. A reviewed health observation (health-observation.reviewed) has no listener anywhere; nothing connects observations, assessments, or client condition changes to ClientCarePlan or CareProviderTasks updates. The entire plan-and-task refresh after a condition change is manual, multi-step, and easy to skip. Whether automation was intended cannot be determined from code (the event is emitted with an approved flag that nothing consumes — health-observations.service.ts:225–228).
  2. No plan→task sync. Publishing a new ClientCarePlan version does not invalidate, flag, or regenerate the CareProviderTasks documents that were generated from an older plan version. CareProviderTasks doesn't even record which care-plan version it was generated from (only generic AI metadata), so staleness is undetectable.
  3. Regeneration friction after changes: generateCareProviderTasks defaults to schedules without existing task documents and errors with "Care provider tasks already exist for all schedules" otherwise (client-care-provider-tasks.service.ts:1484–1488); regenerating after a condition change requires explicitly passing scheduleIds, and produces a new version group disconnected from the old one.
  4. Phantom isActive field: createDraft and saveCareProviderTasksFromGeneration set isActive: true (client-care-provider-tasks.service.ts:245, 1074) and generateCareProviderTasks queries { isActive: true } (line 1456), but CareProviderTasks has no isActive schema prop — the value is never persisted and the query filter matches on a nonexistent field. Effect depends on Mongoose strictQuery config; cannot determine intended behavior from code.
  5. TaskResponseStatus.DRAFT is never set by any production code path (only the stale-task cron queries for it, auto-complete-tasks.service.ts:35); stepCompletions is written only by the dev script scripts/complete-shifts-with-ai.ts. Dead-or-future schema.
  6. ClientCarePlan reads are not business-scopedgetCarePlansList/getCarePlanById/getLatestCarePlan filter only by clientId (client-care-plans.service.ts:149–210), and the :clientId/care-plans* routes in clients.controller.ts rely on the surrounding guard stack; whether cross-tenant clientId probing is blocked upstream cannot be determined from this module.
  7. Destructive version reverts: both revertCarePlanToVersion and revertToPublishedVersion permanently deleteMany newer versions (client-care-plans.service.ts:380–394; client-care-provider-tasks.service.ts:1344–1354) — no audit trail of the deleted versions remains.
  8. updateTask silently drops fields: it always overwrites metadata with data.metadata (even undefined) and cannot change taskMode, priority, isRequired, or templateType (client-care-provider-tasks.service.ts:933–945).
  9. Representative daily tasks ignore recurrence: getDailyTasks returns every task in every latest-published document regardless of whether it occurs on the requested date, and getStats admits its expected-task count is a rough tasks × days estimate (representative-tasks.service.ts:100–185, 317–319). Family-facing completion numbers can be misleading.
  10. Naming collision: code and UI call both ClientCarePlan and CareProviderTasks "care plan" (e.g. error string "Care plan already exists for schedule" refers to CareProviderTasks, client-care-provider-tasks.service.ts:1006), which is a standing source of confusion.
  11. No review state on submissions: submissions terminate at SUBMITTED; supervisor review exists only informally via comments/concerns. updateTaskSubmission has a TODO for supervisor privileges that was never implemented (client-task-submissions.service.ts:965–971).
  12. Caregivers can delete their own submissions with no audit trail or time limit (client-task-submissions.service.ts:1054–1078) — compliance-sensitive.
  13. medicationTemplateCache is cached forever (per process) after first read (client-care-provider-tasks.service.ts:118–132); template repopulation won't be reflected until restart.
  14. deleteCarePlansByScheduleId deletes any plan whose scheduleIds array contains the schedule (client-care-plans.service.ts:715–735) — a plan spanning multiple schedules is deleted entirely when only one of its schedules is removed.
  15. GET .../care-plans/calendar is identical to the list endpoint (clients.controller.ts:1032–1035) — purpose unclear from code.
  16. Comment soft delete is schema-only: isDeleted/deletedAt/deletedBy exist on TaskSubmissionComment but no controller/service path deletes comments.

On this page