Anaya Care Docs

Anaya Care — As-Built Product Wiki

What this is: A reverse-engineered, "as-built" documentation of the Anaya Care platform, generated from the codebase on 2026-06-11. It documents what the code actually does today — not what anyone intended. Inconsistencies, dead code, and missing behavior are recorded as-is and flagged in each page's Open Questions & Gaps section.

How to use it: Read it, correct it, and decide. The Open Questions & Gaps sections are the review agenda — each one is either (a) a verbal decision that was never written down, (b) a bug, or (c) a feature that was never finished. The team should label each item accordingly.

Conventions: Every stated rule cites the file (and often line) that enforces it. Line numbers drift as code changes; paths are the stable anchor. Collections are MongoDB; entities are Mongoose schemas with _id → id normalization.


Product summary

Anaya Care is a multi-tenant home care management platform. A care business (agency) is the tenant: it assesses prospective clients, sends priced care proposals to family representatives, converts accepted proposals into managed clients, plans care (care plans + care provider tasks), schedules shifts worked by care providers, fills coverage gaps via job postings, and keeps families informed through requests, chat, notifications, and shared reports. AI features (care plan/task/meal/activity generation, wound photo analysis, shift summaries, RAG-backed trainings) run throughout.

  • Backend: NestJS + MongoDB (Mongoose) + Redis/BullMQ + Socket.IO — apps/backend
  • Web: Next.js dashboard for agency staff (admins, managers) — apps/web
  • Mobile: Expo React Native app for care providers and family representatives — apps/mobile
  • Shared: enums, types, and all state-machine definitions — packages/shared (@anaya/shared)

Roles & tenancy (the 2-minute version)

Full detail: 11-identity-and-access.md

  • Roles: SuperAdmin (platform), Owner/Admin (agency management), CareProvider (caregiver), Representative (family-side; renamed from Family/FamilyProfile in migration 025), MedicalProfessional, plus per-business Custom roles (the CareManager base role was removed in migration 031 and converted to custom roles).
  • Authorization is permission-based: @RequirePermissions + PermissionsGuard, with per-business overrides (BusinessPermission, baseRolePermissionOverrides).
  • Tenancy: nearly all data carries a business ref, auto-scoped by a CLS-based Mongoose plugin. Known systemic holes are listed below.

Module index

#PageCovers (backend modules)
01Clients (Core)clients core: Client, status lifecycle, care team (CaregiverAssignment), medical record, service info, representative links
02Assessments & Change of Conditionsinitial-assessments, six per-client clinical assessments, health-observations (the "change of conditions" feature), care readiness quiz
03Care Proposalscare-proposals + the shared transition map, review rounds, snapshots, e-sign flow
04Care Plans & Care Provider TasksClientCarePlan, CareProviderTasks, task templates, task submissions & family concerns
05Scheduling & Shiftsclient_schedules (RRULE), shifts, timesheets/clock-in-out, shift-handovers, shift summaries, daily-hour caps
06Job Postings & Applicationsjob-postings: hiring marketplace, shift→posting conversion, applicant review
07MedicationsClientMedication, med-pass virtual tasks, MedicationLog, stock tracking, RxTerms/OpenFDA
08Health Monitoringhealth-metrics (vitals), wound-care (+AI analysis), DNR acknowledgments, doctors' appointments
09Incident Reports & Emergency Alertsincident-reports, emergency-alerts (SOS)
10Daily Livingclient-meals, client-engagements, essential-needs, home-inventories, daily-motivations
11Identity, Access & Tenancyauth, users, profile-setup, business, base-rates, permissions & tenancy machinery
12Communicationchat, websocket, notification (the fan-out hub), livekit calls, announcements, emails
13Requestsrequests: representative ↔ agency service tickets
14AI Featuresai, ai-chat, ai-settings, plate-ai, knowledge-base, generation orchestrators
15Content & Communityposts, blog, trainings (LMS), skills, ratings, feature-requests
16Platform Operationsfiles/assets/avatar, lock-care vault, pdf, google-maps, statistics, platform-*, app-version, bull-board, migrations

How the modules relate

The core care lifecycle is a chain; everything else either feeds it or fans out from it.

Key mechanics behind the arrows (each documented in the linked pages):

  • 02 → 03: InitialAssessment.convertToCareProposal() copies assessment data into the proposal. The reverse rule — "a proposal must originate from an assessment" — is not enforced in the backend (see below).
  • 03 → 01: POST /clients/from-care-proposal/:id materializes a Client (+ service info shells, seeded routine-task inventory) and marks the proposal In Service. It does not create schedules, caregiver assignments, or a ClientCarePlan.
  • 04 ↔ 05: Tasks live per-schedule (CareProviderTasks, DRAFT→PUBLISHED) and are expanded per shift; clock-out is gated on required task submission; medications/meals/engagements are merged into the shift task list at read time.
  • 05 ↔ 06: Shifts convert to job postings; acceptance fires events that assign the caregiver to the client and the originating shift. Daily-hour caps (12h cross-client / 24h same-client) are checked at apply/accept/assign — but not during bulk shift auto-generation.
  • 09/02 → 05: Incident reports and health observations feed a BullMQ queue where an AI decides whether to create a shift handover note for the next caregiver. That is currently their only downstream effect.
  • 12: ~110 registry-driven notification types fan out to in-app/WebSocket, Expo push, Resend email, and Twilio SMS; nearly every module emits into it.

Cross-cutting findings (read this before the per-module gaps)

These patterns recur across many modules. They are the highest-leverage items for team review.

1. Several "known rules" exist verbally but not in code

Rule as stated by the teamWhat the code actually doesWhere documented
"Care provider must not exceed 16 hours/day"No 16h constant exists anywhere. Implemented caps: 12h cross-client / 24h same-client (packages/shared/src/constants/common.ts), enforced at apply/accept/assign/pickup but not during bulk shift auto-generation05, 06
"A care proposal cannot be created unless it originates from an initial assessment"POST /care-proposals accepts direct creation and fabricates a blank linked assessment; the web UI exposes a direct "add proposal" page. Only trace of the rule is a permission label string03
"Change of conditions updates the care plan and care provider tasks"No cascade exists. "Change of conditions" is a UI label on HealthObservation (free-text notes). Its only automated effect is an AI shift-handover note. Plan and task updates are entirely manual, separate steps02, 04
"Incident reports affect schedule/care plan"Incidents trigger notifications + an AI handover note, then dead-end. Nothing touches plan, schedule, or assessments09

2. Permission enforcement is partially decorative

There is no global auth guard. PermissionsGuard, BusinessGuard, and IdleTimeoutGuard are registered globally but run before the per-controller JwtAuthGuard, and all three skip when req.user is absent — which at that point it always is. Result: @RequirePermissions is enforced only on the ~24 controllers that locally re-bind @UseGuards(JwtAuthGuard, PermissionsGuard); on ~22 others (including clients, shifts, care-proposals, chat, posts, lock-care) the decorators do nothing, and the inactive-business block and HIPAA idle-timeout never execute anywhere. Full analysis: 11-identity-and-access.md.

3. Tenancy has systemic holes

The CLS-based Mongoose plugin auto-scopes schemas that have a business field — but: LockCare, Notification, and all profile entities have no business field; aggregation pipelines skip both casting and the plugin (several $matches compare strings to ObjectIds and silently return nothing); and the plugin no-ops outside HTTP request context (cron jobs, WebSockets, migrations). Cross-tenant exposures were found in announcements fan-out, knowledge base CRUD, meals catalog, skills, and LockCare reads. See 11, 12, 14, 16.

4. Event-driven architecture with dead ends

Multiple events are emitted but have zero listeners (health-observation.reviewed, medication.updated/deleted, engagement approve/reject), and some listeners are fire-and-forget where failures silently break invariants (job acceptance side-effects, proposal snapshots). Where the team expected cascades (CoC → plan → tasks), the events simply were never wired. Flagged per-module.

5. Security items needing immediate triage

Collected from module pages, most severe first — see 16-platform-operations.md and 11-identity-and-access.md:

  • DevController endpoints are unauthenticated and registered in production (user enumeration; overwrite client medical data).
  • Bull Board queue dashboard (/admin/queues) has no auth in code.
  • GET /files/file/:key is public; file delete/copy endpoints have no ownership checks; incident photos uploaded isPublic: true.
  • Several PHI-bearing endpoint groups (doctors' appointments, medications, assessments) have no permission decorators and/or missing business filters.
  • Full client PHI narratives are sent to OpenAI/Anthropic/Gemini/Qdrant with no redaction layer (14).

6. Naming history that confuses readers

  • FamilyProfileRepresentativeProfile (migration 025). UI says "Family"; code says "Representative".
  • CareManager base role removed (migration 031) → per-business custom roles.
  • ClientCareProviders is not the care team — it stores external provider organizations. The care team is CaregiverAssignment. (01)
  • "Change of conditions" in the UI = HealthObservation in code. (02)
  • plate-ai is the rich-text editor AI, not meal-photo analysis. (14)

Suggested review order

  1. Product/clinical leads: 01 → 02 → 03 → 04 → 05 → 06 (the lifecycle), focusing on each Open Questions section — most items there are "decide what the rule should be" questions.
  2. Engineering leads: 11 and 16 first (cross-cutting permission/tenancy/security findings), then the lifecycle pages.
  3. Everyone: treat every Open Questions item as a ticket candidate: label it decision needed / bug / unfinished feature.

Generated from the codebase as of commit 4b321bf8 (main). Each page is independently editable Markdown — paste into ClickUp or internal docs as needed.

On this page