Skip to content

CareMatch — a full SaaS: marketplace core, 3 web apps, billing, ops, compliance - #1

Open
maxmurphySF wants to merge 13 commits into
mainfrom
claude/carematch-platform-blueprint-8y7orf
Open

CareMatch — a full SaaS: marketplace core, 3 web apps, billing, ops, compliance#1
maxmurphySF wants to merge 13 commits into
mainfrom
claude/carematch-platform-blueprint-8y7orf

Conversation

@maxmurphySF

@maxmurphySF maxmurphySF commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Overview

Builds CareMatch from the blueprint into a running SaaS: the verified, budget-aware in-home care marketplace, plus the company machinery around it — auth, a real database, billing, three web front-ends, an ops console, observability, CI/CD, provider verification, and data-subject compliance. No in-memory shortcuts, no runtime mock data, no vendor stubs on the critical path (external processors sit behind swappable boundaries).

Node ≥ 22.6, TypeScript run directly via type-stripping. 104 tests + two browser end-to-ends, strict typecheck, CI, Docker. 13 commits.

Product core (blueprint §3–§5)

  • Matching engine — explainable, weighted, hard-filtered ranking (active/verified providers only).
  • Budget optimizer — Full coverage / Smart mix / Essentials, priced against real providers.
  • Funding Navigator — VA/Medicaid/LTC/MA screener that re-plans at the funded budget.
  • Booking lifecycle — versioned plans (senior-approved), scheduling, and a strict visit state machine whose check-in is gated on a selfie-match + GPS geofence.
  • Notifications — visit events → family reassurance and urgent ops escalation.

Clients (three web apps, served by the same server)

  • Senior PWA (/app) — accessibility-first (WCAG 2.2 AA floor): booking, match card-stack, "here is your care plan," and a "Verified helper" badge showing each provider's real, dated verification stages.
  • Family dashboard (/family) — reassurance strip, visit timeline, shared care plan, budget — scoped to senior-granted consent.
  • Ops console (/console) — platform metrics, incident queue, audit trail.

SaaS platform

  • Auth & consent — role-based accounts (senior/family/provider/ops), scrypt + HS256 JWT, and granular senior-granted family consent (view/book/financial).
  • Provider verification & trust — a verification-record pipeline (identity → background → credential → continuous monitoring) that derives provider status and tier; adverse results suspend and de-list instantly. Provider profiles are claimed safely (one account per unclaimed profile).
  • Database — PostgreSQL via a Db interface satisfied by PGlite (local/tests) and node-postgres (prod) against identical migrations.
  • Billing & payments — subscriptions, invoices, marketplace take-rate, provider payouts, over a PaymentGateway boundary (in-house ledger → Stripe Connect).
  • API gateway hardening — security headers/CSP, CORS, per-client rate limiting, request IDs, structured JSON logs, /health /ready /metrics, graceful shutdown.
  • Audit log & admin — append-only trail of sensitive actions; ops-only console and API.
  • Account lifecycle & compliance — password reset & email verification (hashed, single-use, expiring tokens over an email boundary); CCPA/GDPR data export and account deletion (scrubs PII/health data, de-identifies retained financial records).
  • Ops/CD — GitHub Actions CI (typecheck + test), Dockerfile with healthcheck, docs/openapi.yaml.

Verification

  • npm run typecheck clean (strict); npm test104 pass over a real in-memory Postgres with real auth.
  • npm run test:e2e / test:e2e:family drive the senior flow and the senior→family handoff in a real browser under the production CSP, zero console errors.
  • Billing/take-rate settlement, rate limiting, audit trail, verification gating, and the compliance flows are all covered by tests.

Deliberately deferred

Provider native app; live vendor webhooks for identity/verification (Persona/Checkr) and live payments (Stripe); messaging (Twilio/Sendbird); the tokenized PII vault. Each sits behind an interface this PR defines. See docs/ARCHITECTURE.md for the built-vs-deferred component map.

🤖 Generated with Claude Code

https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d

maxmurphySF and others added 5 commits July 13, 2026 01:21
Bootstraps the CareMatch platform from the product & build blueprint,
implementing the defensible core the blueprint calls out as the moat and
as independently testable: the Matching Engine and Budget Optimizer (§4).

- domain: strictly-ordered service tiers, ADL/IADL task catalog with
  minimum-tier mapping and safety-critical flags, core entity types, geo
- matching: rules-based v1 engine — hard filters then transparent
  weighted-linear scoring with plain-language "why this match" reasons;
  weights are config, not code
- optimizer: the signature "here is your care plan" feature — Full
  coverage / Smart mix / Essentials plans priced against real providers,
  with lower-tier hours billed at that tier's market rate
- api: dependency-free POST /match HTTP surface with defensive input
  validation
- sample data + console demo, 21 tests (Node built-in runner), strict
  tsconfig, docs (blueprint, architecture, data model)

Runs on Node >=22.6 with no runtime dependencies. Deferred components
(identity/verification, payments, booking, PII vault, client apps) are
documented in docs/ARCHITECTURE.md.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Extends the core downstream of matching, turning an optimized plan into
approved, scheduled, verifiable care.

- domain/booking: CarePlan, Visit, VisitEvent stream, visit-time
  verification types, and the transition table
- booking/carePlanService: promote an optimizer option into a versioned
  CarePlan; senior-only approval activates it (senior sovereignty)
- booking/schedulingService: expand an active plan into concrete visits,
  co-locating same-day/same-provider blocks and spreading weekly hours
- booking/visitLifecycle: strict VisitEvent state machine; check-in is a
  hard gate requiring both selfie match and GPS geofence (blueprint stage
  5), and a "not good" review flags a same-day follow-up
- booking/repository + api/bookingService: in-memory store and
  orchestration wiring the tested services together
- api/server: care-plan and visit REST endpoints with typed error codes
  (409 invalid transition, 422 failed verification); bookingValidate
  guards untrusted input
- optimizer: plan blocks now carry their scheduled days, enabling the
  weekly calendar and scheduling
- bookingDemo + 24 new tests (care plan, scheduling, visit lifecycle,
  full HTTP flow); 45 tests total, strict typecheck clean

Also updates README and docs to mark booking/scheduling/visit
verification as built.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Implements the blueprint's §4.4 Funding Navigator as a content + decision
-tree screener (no payer integrations), which the optimizer re-runs
against an adjusted effective budget.

- domain/funding: screener input, source results, indicative program
  parameters (VA MAPR, Medicaid HCBS limits, MA supplemental) as config,
  and a standing "estimate, not a determination" disclaimer
- funding/fundingNavigator: rates VA Aid & Attendance, Medicaid HCBS,
  LTC insurance and Medicare Advantage as likely/possible/unlikely with
  an estimated monthly benefit and concrete next steps; only likely
  sources count toward the budget; withAdjustedBudget raises the ceiling
- api/fundingService: assessAndReplan returns the care plans at both the
  out-of-pocket and funded budgets so the funding's effect is visible in
  real plans
- api/server: POST /funding/plan endpoint; screener input validation
- 10 new tests (55 total); README and architecture docs updated

Example: a $1,500 firm budget that only affords "essentials" becomes a
$5,600 effective budget affording "full coverage" once ~$4,100/mo of
likely VA + Medicaid funding is applied.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Turns visit lifecycle transitions into audience-targeted notifications,
connecting the event stream to the family reassurance strip (§5.2) and
the incident-escalation path (§5.1).

- domain/notifications: Notification model with audience (senior/family/
  ops), priority (info/reassurance/urgent), channels and status
- notifications/notificationRules: pure mapping from a visit's latest
  event to notifications — en-route reassurance, the verified-arrival
  trust alert, checkout, cancellation and no-show; a "not good" review
  escalates to an urgent ops phone call; a failed identity check raises
  an urgent ops alert even though it is not a visit event
- notifications/dispatcher: channel-agnostic sink abstraction with
  console, null, and recording sinks; dispatch stamps delivery status
- repository: persist and list notifications per seeker
- api/bookingService: fan out and persist notifications on each visit
  transition (and on failed verification); listNotifications
- api/server: GET /seekers/:id/notifications; console sink wired for the
  running server
- bookingDemo shows notifications firing per step; 8 new tests (63 total)
- README and architecture docs updated

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Turns the in-memory core into a running application with a real database
and real authentication — no in-memory shortcuts, no runtime mock data.

Database (src/db):
- A small Db interface satisfied by both PGlite (real Postgres in WASM,
  in-process, used locally and in tests) and node-postgres in production
  against the same schema (DATABASE_URL).
- Idempotent migrations creating users, providers, care_plans, visits and
  notifications; aggregate value objects stored as JSONB, ids/status/dates
  as indexed columns.
- Async DB-backed repositories replace the in-memory store; provider data
  is seeded into the DB and read from it (never a runtime array).

Auth (src/auth):
- scrypt password hashing and HS256 JWTs, both Node built-ins (the DB
  driver is the only new runtime dependency).
- register/login/authenticate against the users table; login is
  timing-safe against account enumeration.

API gateway (src/api):
- Every route except /health and /auth/* requires a bearer token.
- Authorization enforces senior sovereignty: only the senior (or ops)
  approves a plan or schedules visits; only the assigned provider runs a
  visit's provider actions; only the senior reviews it. seekerId is taken
  from the token, never the body.
- createApp wires DB + services; main.ts is the entrypoint (migrate +
  seed + serve); db:setup CLI; .env.example + src/config.ts.

Tests now run the whole stack over a fresh in-memory Postgres with real
auth (65 total, incl. a full authenticated booking flow and cross-request
persistence). README and architecture docs updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
@maxmurphySF maxmurphySF changed the title CareMatch core: matching engine, budget optimizer, and verified booking lifecycle CareMatch backend: matching, optimizer, funding, verified booking — on real Postgres + auth Jul 13, 2026
maxmurphySF and others added 7 commits July 13, 2026 10:59
Encodes the sovereignty principle (blueprint §5.2): a family account has
no access to a senior until the senior grants it, at a granular level.

- domain/consent: ordered levels view < book < financial, with a
  levelPermits capability check
- db: 0002_consents migration (one active grant per senior+grantee) and
  a consent repository (grant/upsert, revoke, findActive, lists)
- auth/consentService: grant (family accounts only, not self), revoke by
  the granting senior, and permits() used by authorization
- api: POST/GET/DELETE /consents; authorization is now consent-aware —
  family reads require 'view', propose/schedule require 'book', the
  funding navigator requires 'financial'; family can never approve a plan
  (senior/ops only); seekerId still comes from the token/grant, not the body
- 9 new tests (73 total) covering the full grant → act → revoke flow and
  the sovereignty guarantees; README + architecture + data-model updated

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Turns the backend into an actual product. An accessibility-first PWA
(blueprint §5.1) is served by the same Node server at /app, driving the
real authenticated API through the full senior loop.

Backend:
- static file handler (src/api/static.ts) serving web/ under / and /app,
  with content types and path-traversal guards, ahead of the API routes
- GET /seekers/:id/visits so the home screen can show the next visit
  (repo.listVisitsForSeeker + BookingService.listSeekerVisits), reusing
  the consent-aware view authorization

Senior app (web/, no build step, no framework, zero new runtime deps):
- design system (styles.css): 20px base, >=56px targets, >=7:1 contrast,
  visible focus, no gesture-only/timed interactions, reduced-motion, and a
  persistent "Call us" button in the same corner everywhere
- app.js: dependency-free router + API client (JWT) and screens — login/
  register, home (next-visit card + verification badge + "my people"),
  one-question-per-screen booking, match card-stack, and the "here is your
  care plan" confirm that proposes + approves + schedules 4 weeks
- the verification badge opens a "how we verify every helper" dialog —
  trust made visible

Verified with a committed Playwright e2e (scripts/e2eSenior.ts,
npm run test:e2e): register -> book -> matches -> confirm -> the visit
appears on home, with zero console errors. README + architecture updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
The second client (blueprint §5.2) and the payoff for the consent model:
a family member monitors a senior's care, scoped strictly to what the
senior granted.

Backend:
- consent grant-by-email (the invite flow) and enriched GET /consents
  that names the counterpart (senior/grantee) for dashboards
- family registration no longer needs a seniorId up front (access is
  granted later by the senior)
- GET /seekers/:id/care-plans (listCarePlansForSeeker); /visits already
  added — both behind the consent-aware view check
- static handler generalised to serve /family alongside /app

Family dashboard (web/family/, responsive, no build step):
- left-rail nav; only shows seniors who granted access, at their level
- Overview reassurance strip (last visit + verified-arrival time, next
  visit, recent activity) — "is Mom okay and is this working"
- Visits list + detail with the VisitEvent timeline, verification event,
  structured note and the senior's review
- Care plan (the shared weekly artifact) and Budget vs. plan

Senior app: a "Share with family" screen grants consent by email + level.

Verified with a cross-app Playwright e2e (scripts/e2eFamily.ts,
npm run test:e2e:family): family account -> senior books + shares ->
family dashboard shows the visit and plan, zero console errors. One new
backend test locks the invite-by-email + seeker-reads contract (74 total).
README + architecture updated.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
The monetization core that makes this a SaaS, over a payment-gateway
boundary so the in-house ledger swaps for Stripe Connect in production.

- domain/billing: cents-based money, subscription plans, invoices with
  line items, and a take-rate settlement helper that reconciles the
  platform fee to the provider payouts exactly
- payments/gateway: PaymentGateway interface + LedgerPaymentGateway
  (deterministic in-house processor; charge/refund/payout)
- db 0003_billing: subscription_plans, subscriptions, invoices,
  invoice_line_items, payments, payouts; plans seeded (Free, Plus)
- billing/billingService: subscribe (charges paid plans), idempotent
  invoice generation from a care plan's blocks, pay-invoice that charges
  the senior and settles per-provider payouts with the take-rate deducted
- api: /billing routes (plans, subscription, invoices, pay, payouts) with
  financial-level authorization; BillingError -> 402
- 7 new tests (81 total): settlement math + full API flow
  (subscribe -> generate -> pay -> payout) incl. idempotency and access

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Makes the service production-shaped, not just functional.

- observability/logger: structured JSON logs (level, time, fields); every
  request logged with method/path/status/duration/request-id
- observability/metrics: in-process counters + /metrics Prometheus text
- api/rateLimit: fixed-window per-client limiter (tighter on /auth) -> 429
  with Retry-After
- api/httpMiddleware: security headers (CSP, nosniff, DENY, Referrer-
  Policy, Permissions-Policy, HSTS in prod), configurable CORS allowlist,
  and X-Request-Id echo; client key from XFF/socket
- server: /health, /ready (DB-checked), /metrics; middleware applied to
  every request; graceful shutdown in main via the logger
- CSP-safe: senior app skip-link moved from inline JS to CSS
- CI (.github/workflows/ci.yml: typecheck + test on Node 22), Dockerfile
  (+ HEALTHCHECK, .dockerignore), docs/openapi.yaml (OpenAPI 3.1)
- config + .env.example: CORS_ORIGINS, RATE_LIMIT_*, LOG_LEVEL
- 5 new tests (86 total); both browser e2e suites still pass under CSP

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Compliance and operations: an append-only audit trail and an ops console
to run the platform.

- domain/audit + 0004_audit table; auditRepository (append/list) and a
  best-effort AuditService that never fails the user's request
- server records sensitive actions with actor + request id: register,
  login, consent grant/revoke, plan approve, subscribe, invoice pay, and
  failed visit verifications
- admin/AdminService: platform metrics (users, providers, plan/visit
  status, GMV, platform revenue, payouts), the incident queue (bad
  reviews + failed identity checks), and user lookup
- /admin API (ops only) + web/console ops dashboard at /console
- security: ops accounts can no longer be self-registered; a bootstrap
  ops account is provisioned from ADMIN_* config (dev defaults provided)
- 6 new tests (92 total): ops guard, no-self-register, metrics, audit
  trail contents, and the verification incident path

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Rounds out the SaaS: self-service account management and the CCPA/GDPR
data-subject rights.

- 0005 migration: users.email_verified + auth_tokens (hashed, single-use,
  expiring); tokenRepository issues/consumes tokens (only the SHA-256 hash
  is stored, raw token travels by email)
- email/EmailService over an EmailTransport boundary (console locally;
  swap SES/SendGrid in prod) for reset & verification links
- account/AccountService: password reset (request never reveals whether an
  email exists; confirm rotates the password and invalidates other tokens),
  email verification, data export (all of a user's records), and account
  deletion (erases PII + health-adjacent care data, de-identifies retained
  invoices/payments)
- routes: public /auth/password-reset/* and /auth/verify-email/confirm;
  authed /account/verify-email/request, GET /account/export, DELETE /account
- config + .env.example: APP_URL, ADMIN_EMAIL/PASSWORD
- 5 new tests (97 total): reset happy-path + no-enumeration + single-use,
  verification, export, and deletion; both browser e2e suites still pass

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
@maxmurphySF maxmurphySF changed the title CareMatch backend: matching, optimizer, funding, verified booking — on real Postgres + auth CareMatch — a full SaaS: marketplace core, 3 web apps, billing, ops, compliance Jul 13, 2026
Makes the headline moat real: a provider's bookability and tier are
derived from verification records, never self-declared.

- domain/verification: 4-stage pipeline (identity, background, credential,
  monitoring) with deriveStatus/deriveTier and a customer-facing badge
  summary; visit-time confirmation stays on the visit event stream
- 0006 migration: providers.status + requested_tier; verification_records
- verificationRepository + VerificationService: recording a stage result
  re-derives status (active needs identity+background; any adverse
  background/monitoring result suspends) and verified_tier =
  min(requested tier, credential ceiling)
- matching now lists only active providers, so a suspended provider drops
  out of results immediately
- security: provider registration can only claim a pre-created, UNclaimed
  provider profile (no more arbitrary providerId self-claim)
- endpoints: GET /providers/:id/verification (any signed-in user) and
  ops-only POST /admin/providers/:id/verification (stands in for a
  Persona/Checkr webhook)
- seed gives sample providers a real, dated verification history; the
  senior app's "Verified helper" badge now shows that real data
- 8 new tests (104 total): status/tier derivation, seeded badge, adverse
  -> suspended -> unmatched, ops-only recording, and claim-once semantics

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01QN287Pq3hEgDNJmDzKnH8d
Sign up for free to 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