Skip to content

Features

What builds on top of the foundation, in dependency order. None of this starts until 1D closes.

Architecture is authoritative for design — these checklists track scope and status, not entity definitions:

Discipline. Layer numbering inside features is Fn. Existing references like "Layer 3" / "Layer 4.5" still resolve via section anchors below. The naming change is editorial, not structural.


F1. Specialists & Specialties

Was old Layer 2.3. The first medical-domain feature: specialist profiles (specialty, signature, scheduling timezone) on top of the foundation's clinic-staff identity.

F1.1 Specialties

  • [ ] specialties table (per-org or global — decide before migration).
  • [ ] Permission seeding: specialties.manage (admin); membership-read.

F1.2 Specialists

  • [ ] specialists table — human_id UNIQUE NULL FK to humans(principal_id), scheduling_timezone, scheduling_active, signature_url, deleted_at (P13).
  • [ ] specialist_specialties junction.
  • [ ] Signature image upload via 1A.8 (S3 surface signatures).
  • [ ] Patient-side SELECT policy expansion (carried from 1B.6): patients can read the clinic's specialist list — staff via organization_memberships OR patient via current_human_patient_profile_ids() joined to patients.organization_id. Without this, the portal's specialist picker returns zero rows for patient sessions. Same pattern likely applies to other patient-facing read surfaces (services, calendars) — flag at each subsequent feature's table.
  • [ ] Permissions: specialists.view_org (membership-read), specialists.manage (admin).

Exit criteria: Clinic admin creates specialists with specialty + signature; portal can list specialists for booking once F4/F5 ships.


F2. Service Catalog

What the org offers. Pure catalog — scheduling lives in F4. See data-model.md Area 3.

F2.1 Services

  • [ ] services table (catalog entity, no scheduling config).
  • [ ] service_specialists junction (capability — who CAN provide).
  • [ ] service_attachments (downloadable files via 1A.8).

F2.2 Service Plans

  • [ ] service_plans (three plan types: session_based, time_based, hybrid; access grants telerehab_access + library_access).
  • [ ] Counter extension for non-session entitlements — add treatment_plan_assignments_total INT NULL (or equivalent) to service_plans so a tier inclusion like "4 specialist treatment plans / year" can be modeled. See tiers-and-subscriptions.md § Counter semantics.
  • [ ] patient_service_plans (enrollment + progress tracking).
  • [ ] patient_service_plans.source_tier_subscription_id UUID NULL FK → patient_subscriptions(id) — tracks which patient_service_plans rows were auto-granted by a tier subscription versus directly purchased.
  • [ ] patient_tier_inclusions table — junction (tier_id FK → patient_tiers, service_plan_id FK → service_plans, grant_period, grant_quantity, carry_over_unused, prorate_on_upgrade). Lands here (rather than in 1B.4) because service_plan_id FKs to service_plans introduced in this feature. RLS gated by patient_tiers.manage.
  • [ ] Tier-inclusion provisioning hook — on patient_subscriptions state change, project patient_tier_inclusions onto patient_service_plans. Provisions on activate / trialing, soft-expires on cancel / expire / past_due, regrants on period rollover (drops or carries unused per carry_over_unused), full grant or prorate per inclusion config on tier change.

F2.3 Products

  • [ ] products (reference catalog; no e-commerce).
  • [ ] service_plan_products (bundle junction).

Note. service_forms junction defers to F3 with form_templates.

Exit criteria: Admin can create services + plans; products are listed for reference; patients can be enrolled in plans.


F3. Forms & Consents

Was old Layer 4 (and old 4.5 consent foundation). Forms must come before Scheduling because services and calendars both reference form_templates. See data-model.md Areas 6-7. Versioning + immutability are core (P14, P18, P19).

F3.1 Custom Fields + Versioning

  • [ ] custom_fields (with version, published, published_at, system_key).
  • [ ] custom_field_versions (append-only history).
  • [ ] custom_field_values (polymorphic entity_type, entity_id).
  • [ ] Auto-fill resolver: at form-instance creation, pre-populate from custom_field_values + patient_profiles.
  • [ ] system_key immutability enforced at app layer.

F3.2 Form Templates

  • [ ] form_templates (with version, published, pdf_template_id — FK populated in F6).
  • [ ] form_template_versions (append-only).
  • [ ] Predefined org templates auto-created on org creation (driven by 1A.9 event).

F3.3 Forms (Instances)

  • [ ] forms (snapshot template at creation; values, files, status).
  • [ ] State machine: pending → in_progress → completed → signed (immutable from signed).
  • [ ] File upload integration via 1A.8 (S3, signed URLs, MIME validation).
  • [ ] Profile sync: fields with profile_field_key write back to patient_profiles.
  • [ ] Publish events: form.created, form.completed, form.signed via 1A.9.
  • [ ] Consent grant on signing a consent-purpose form (writes to consents from F3.5).

F3.4 Service + Calendar Forms Junctions

  • [ ] service_forms (M:M services ↔ form_templates).
  • [ ] calendar_forms defers to F4.

Layers form-driven medical consents on top of the foundation's consents ledger (1B.9). The schema, withdrawal semantics, RLS, trail view, and RequireConsent middleware all ship in foundation; this feature adds the form-as-content path + Tier B purposes + multi-modal signature capture.

What's already there from 1B.9 (do not re-ship):

  • consents ledger table with source discriminator, source_form_id NULL, withdrawal columns, append-on-grant + UPDATE-on-withdraw semantics
  • consent_purposes + consent_purpose_versions catalog with legal_basis and withdrawable columns
  • current_required_consent_versions(principal, org) helper + re-consent middleware
  • RequireConsent(purpose) middleware (stub in foundation; this feature adds real consumers via F5.6 / F9)
  • Self-toggle endpoints (foundation surfaces SaaS-level toggles in patient settings)
  • Staff-action endpoints (foundation surfaces staff grantor path)
  • Trail view (GET /v1/me/consents, GET /v1/organizations/{id}/patients/{patient_id}/consents)

Tier B adds (this feature):

F3.5.1 Tier B purposes register in the catalog

  • [ ] Add to consent_purposes (org-scope, legal_basis='consent', withdrawable):
    • telemedicine — telemedicine consultation acceptance + clinical disclaimers
    • video_recording — recording the video call for clinical review
    • biometric_capture — pose estimation, goniometer, biometric readings
    • treatment_specific_* — clinic-defined treatment-disclosure consents (one purpose per treatment type, registered per clinic)
  • [ ] Per-org override flow for clinic-defined treatment_specific_* purposes — clinic publishes the purpose code + initial version via the form template that captures it (see F3.5.2 below).

F3.5.2 Form-as-canonical-content for medical consents

Clinical-grade consents need the immutability + reproducibility + signature capture that signed forms give. The form template is the canonical legal text; the consents ledger row is the queryable index referencing it.

  • [ ] form_templates.consent_purposes JSONB — array of (purpose_code, purpose_version) declarations on the template. When a form instance is signed, one consents row is inserted per declared purpose, with source='form' and source_form_id pointing at the signed form instance.
  • [ ] Form-signing hook (extends F3.3): on forms.status transition to signed, the consent rows are inserted in the same transaction. Audit-logged via 1A.1.
  • [ ] One signed form can produce multiple consent rows (typical pre-treatment form covers telemedicine + video_recording + treatment_specific_* in one signature) — already supported by the array shape.
  • [ ] Withdrawal of a Tier B consent does NOT mutate the signed form (the form is immutable). It UPDATEs the consents row's withdrawn_at + withdrawn_by_principal_id. The form remains as the audit artefact of the original grant.

F3.5.3 Multi-modal signature capture

The same signed-form path supports three signature modes:

  • [ ] In-portal click — patient signs by typing their name + clicking "I agree". Form template's signature_mode = 'click_typed'. Stored on the form instance: typed name, IP, user agent, timestamp.
  • [ ] Drawn on tablet at clinic — clinic-side kiosk flow. Form rendered on a tablet handed to the patient; signature captured as a drawn path (PNG + SVG path data). Form template's signature_mode = 'drawn_kiosk'. Stored on the form instance: signature image (S3 surface signatures from 1A.8), capture timestamp, kiosk identifier (form token from the staff-initiated session).
  • [ ] Sent to phone — clinic admin generates a one-time signed URL pointing at the form instance; patient receives it via SMS or email; signs from their own phone. Form template's signature_mode = 'remote_link'. Link is single-use, expires (default 24h, configurable per template), tied to the patient's identity (signed JWT carries the form-instance ID + patient hash). On submission, the form moves to signed. Stored on the form instance: signature image OR typed-name (link's signature mode), IP, user agent, geolocation if available.
  • [ ] Signature blobs live on the forms row (not duplicated on consents). The source_form_id FK ties them together. Trail view in patient settings (1C.3) follows the FK to render "signed via in-portal click" / "signed at clinic on tablet" / "signed remotely from phone" with the signature image inline if present.

F3.5.4 Real consumers of RequireConsent

  • [ ] F5.6 (consent gating in appointment flow) — appointment-creation requires telemedicine and any treatment_specific_* consents the service has declared via service_forms.
  • [ ] F5.5 (Daily.co video integration) — video room creation requires video_recording if the appointment is configured with recording enabled.
  • [ ] F9 (telerehab) — pose-estimation features require biometric_capture.
  • [ ] F7 (automations) — actions that send marketing emails / SMS require the corresponding marketing_* purpose; this is enforced in the action handler, not the middleware.

Decisions to resolve before implementation starts:

  • [ ] Cross-purpose withdrawal cascade for Tier B — withdrawing telemedicine independently vs cascading to all dependent video/recording purposes? Default proposal: independent (matches foundation 1B.9 semantics for SaaS toggles). UI offers a "withdraw all medical consents" convenience button.
  • [ ] Signature image format — PNG raster + SVG path stored together (PNG for rendering, SVG for re-rendering at higher fidelity), or PNG only? Storage cost is trivial either way; SVG path lets us re-style for printed PDFs later (F6).
  • [ ] Sent-to-phone link revocation — if a clinic admin sends the link then changes their mind, can they revoke before signing? Default proposal: yes, via POST /v1/forms/{id}/revoke-signature-link. Existing link returns 410 Gone.

Exit criteria. A clinic publishes a telemedicine-consent form template with consent_purposes=[(telemedicine, v1)]; patient signs it during booking via any of the three signature modes; one consents row is inserted with source='form' + source_form_id set; the appointment flow proceeds. Patient withdraws via the trail view; the row's withdrawn_at is set; next telemedicine booking re-prompts. Clinic admin sees the signed form (with signature image) and the consents row in their patient detail view.

Exit criteria for F3 overall: Forms are designable, instantiable per appointment, snapshot template versions, immutable after signing, file uploads work, profile fields sync, and medical consents flow through signed forms into the foundation's consents ledger with multi-modal signature capture.


F4. Scheduling

Calendars are the bookable unit. See data-model.md Area 4. Slot hold pattern from patterns.md P30.

F4.1 Specialist Availability

  • [ ] specialist_weekly_hours.
  • [ ] specialist_schedule_overrides (vacations, extra hours).

F4.2 Calendars

  • [ ] calendars (links to service; horizon, cooldown, modality, assignment_strategy, override hours/duration/price).
  • [ ] calendar_specialists (priority + per-calendar override hours JSONB).
  • [ ] calendar_forms junction (forms specific to this calendar).

F4.3 Specialist Assignment Tracking

  • [ ] specialist_assignment_tracking (round-robin counters per calendar).

F4.4 Slot Hold System (Redis)

  • [ ] Hold key pattern + TTL.
  • [ ] Hold release on timeout, cancellation, or successful booking.
  • [ ] SSE endpoint for live availability updates.
  • [ ] Concurrent hold prevention (same slot).

F4.5 Availability Engine

  • [ ] Compute slots from weekly hours + date overrides + calendar overrides + held slots + booked appointments.
  • [ ] Conflict detection.
  • [ ] Round-robin and priority-based specialist assignment.
  • [ ] Slot duration calculation from service settings.

Exit criteria: All calendar types from needs-on-day-1 work. Slot hold is collision-free. Availability returns correct slots end-to-end.


F5. Appointments

See data-model.md Area 5. State machine pattern P33.

F5.1 Appointments

  • [ ] appointments table with full state machine: booked → upcoming → confirmed → inprogress → done | cancelled | noshow.
  • [ ] Soft delete (1A.4).
  • [ ] FK to patient_profile_id (not human_id) for caregiver compatibility.
  • [ ] Add-on services and products via UUID arrays.
  • [ ] Plan session tracking via patient_service_plan_id + plan_session_number.
  • [ ] contact_email is plaintext (pii_basic). Per the encryption-invariants rule (decisions.md → Why most PII is plaintext) all contact PII outside auth_secret and pii_regulated is plaintext + layered defense. The booking-flow dedupe-by-email path stays simple as a result.

F5.2 Appointment Files

  • [ ] appointment_files via 1A.8.

F5.3 Appointment Reviews

  • [ ] appointment_reviews (rating 1-5, alert on low rating, publishes event for org dashboard).

F5.4 Public Booking API

  • [ ] GET /v1/public/availability — public, no auth.
  • [ ] POST /v1/public/bookings — guest booking, no account required.
  • [ ] Slot hold during form fill (via F4.4).
  • [ ] CS confirmation flow → patient_profiles row → optional account creation → appointment created.

F5.5 Daily.co Video Integration

  • [ ] Auto-create video room on appointment.started.
  • [ ] Auto-delete room on appointment.done.
  • [ ] Per-org Daily.co API key via organization_integrations.api_key_encrypted (encrypted by 1A.3).
  • [ ] Check global org consents (Terms, GDPR) before booking proceeds (uses F3.5).
  • [ ] Aggregate forms from service + calendar.
  • [ ] Block flow until required forms signed.
  • [ ] Digital signature capture for physical clinics.

Exit criteria: End-to-end booking flow per needs-on-day-1 works in staging: guest → CS confirm → account → appointment with consents and forms gated, video call working.


F6. Documents

See data-model.md Area 11. Use pdf_templates (block-based) — drop the legacy document_templates design.

F6.1 PDF Templates

  • [ ] pdf_templates (with versioning, editor_state JSONB, layout_config JSONB).
  • [ ] pdf_template_versions (append-only).
  • [ ] pdf_template_components (reusable blocks: letterhead, footer, signature).

F6.2 Document Generation

  • [ ] Decide: PDF rendering engine — ChromeDP, wkhtmltopdf, Gotenberg.
  • [ ] PDF rendering pipeline: template + form values → PDF → S3.
  • [ ] Digital signature embedding (specialist signature from F1).
  • [ ] Generated PDF caching strategy.

F6.3 Appointment Documents

  • [ ] appointment_documents (reports + prescriptions, unified by type, with pdf_template_version frozen at generation).
  • [ ] appointment_document_files (additional supporting files).

F6.4 Form-to-PDF Wiring

  • [ ] form_templates.pdf_template_id FK populated.
  • [ ] Render-to-PDF API on signed forms.

Exit criteria: Specialist generates report/prescription PDFs from appointment data. Documents stored in S3, downloadable via signed URLs.


F7. Automations & Webhooks

Two consumers of the event bus from 1A.9. See data-model.md Areas 12, 13.

F7.1 Event Bus Consumers

  • [ ] Automation engine: matches events to enabled automation_rules, executes actions.
  • [ ] Webhook dispatcher: matches events to webhook_subscriptions, delivers signed payloads.

F7.2 Automation Rules + Executions

  • [ ] automation_rules table.
  • [ ] automation_executions (append-only audit trail).
  • [ ] Decide: Email/SMS/push transport providers — AWS SES, SendGrid, Postmark; Twilio, AWS SNS.
  • [ ] Action handlers: require_form, suggest_form, send_email, send_sms, send_whatsapp, show_notification, send_push, block_booking, grant_access, require_consent, update_segment, schedule_action, send_session_reminder, book_followup_appointment.

F7.3 Webhook Delivery

  • [ ] webhook_subscriptions (HMAC signing, signing_secret generated server-side as whsec_...).
  • [ ] webhook_events (delivery log, retry with exponential backoff, idempotency).
  • [ ] Delivery worker process.

Exit criteria: Onboarding automation works end-to-end (patient.onboarded → required consents + welcome email). Webhooks deliver signed payloads with retry and idempotency.


F8. Segments

Patient cohorts driven by rules over forms + custom_field_values + appointments. See data-model.md Area 8.

F8.1 Segments

  • [ ] segments (rules JSONB, match_mode, version).
  • [ ] segment_members (materialised cache).
  • [ ] segment_versions (append-only history).

F8.2 Rule Engine

  • [ ] Multi-source rules: forms.values + custom_field_values + appointments.
  • [ ] Tiered evaluation.
  • [ ] Auto-update on data changes (event bus consumer for form.signed, appointment.completed, patient.profile_completed).

Exit criteria: Segments auto-update on relevant data changes; admin UI builds rules visually.


F9. Telerehab

Exercise library + treatment plans + patient execution. First feature inside the regulatory boundary — anticipated Class I MDR posture (IEC 62304 Class A/B), designed for Class IIa-upgrade if/when treatment decisions are driven from the data. The Class I posture means the system displays informational data; the specialist makes clinical decisions. See CLAUDE.md → Medical Device Readiness, data-model.md Areas 9-10, and telemetry/index.md → Aggregation engine.

F9.0 Regulatory Habits (start of F9, maintain throughout)

  • [ ] Add requirement IDs to exercise library spec (REQ-EX-001: System shall display contraindications).
  • [ ] Add requirement IDs to treatment plans spec (REQ-TP-001: System shall display prescribed exercises with correct parameters).
  • [ ] Add "Safety Implications" section to exercise + treatment plan feature docs.
  • [ ] Reference requirement IDs in test comments.

F9.1 Exercise Library

Phase 1 — composer integration + cache (shipped, commit 3d95e38)

  • [x] Decided: Bunny Stream for delivery + AWS S3 for raw primitives. The exercise video composition pipeline is documented in P56 and features/exercise-library/composition.md.
  • [x] exercises table — minimal Phase 1 shape: slug, kind (reps_based | duration_based), status (draft | published | archived), asset_version, catalog_render_id, bunny_collection_id. Platform-curated (no org_id), RLS SELECT for any authenticated principal, AdminPool-only writes. No dual-scope (org-clone) for v1 — the spec line below predates the composer-pipeline architecture and is reconsidered in Phase 2.
  • [x] exercise_renders cache table — keyed by (exercise_id, recipe_hash, language)bunny_video_id. Both kinds use it; duration_based has one row per language with recipe_hash='_imported'.
  • [x] Composer service (services/exercise-composer/) — Go service, S3 bundle → ffmpeg → Bunny upload + per-slug collection auto-create.
  • [x] Admin endpoint POST /v1/admin/exercises/{slug}/renders — Console-only render trigger with cache lookup.
  • [x] Shared-secret bearer-token auth between Core API ↔ composer.

Phase 2 — clinical metadata + Console UI + patient surface (remaining)

  • [ ] Taxonomy: exercise_categories (hierarchical), exercise_body_regions, exercise_equipment.
  • [ ] exercise_tags (polymorphic junction).
  • [ ] exercise_instructions (ordered steps, typed) + exercise_contraindications (severity).
  • [ ] translations JSONB on global rows (P21) — display_name + description per language.
  • [ ] Difficulty rating + AI tracking config columns/tables.
  • [ ] Console exercise CRUD UI (list / detail / edit / publish action).
  • [ ] asset_version bump endpoint + eager catalog-preview re-render on bump.
  • [ ] Patient catalog endpoint GET /v1/portal/exercises — returns published exercises with catalog_render_id-resolved Bunny IDs.
  • [ ] Async / queue wrapper around composer call (when treatment plans fan out N renders).
  • [ ] duration_based import workflow (Console superadmin endpoint instead of manual SQL).
  • [ ] Cat A providers resolver for Bunny credentials (before production launch).
  • [ ] Re-evaluate dual-scope (clone-from-global-to-org). Original F9.1 spec assumed clinics clone-and-customize exercises; the composer pipeline shifts the cost of "customization" toward the prescription recipe rather than asset duplication. Decide if per-org exercise customization is still needed.

The full Phase 2 backlog with implementation detail lives in features/exercise-library/composition.md → Phase 2 backlog.

F9.2 Treatment Plans

  • [ ] treatment_plans (three scopes: global, org, custom-per-patient via created_for_patient_id).
  • [ ] treatment_plan_versions (sessions_snapshot JSONB).
  • [ ] treatment_plan_sessions, treatment_plan_session_exercises.
  • [ ] Approval workflow (configurable per org).

F9.3 Patient Enrollment + Execution

  • [ ] patient_treatment_plans (frozen plan version, schedule, progress).
  • [ ] patient_session_completions (per-session tracking, post-session form, pain levels).
  • [ ] patient_exercise_logs (per-exercise: prescribed vs actual, video watch, optional pose tracking).

F9.4 Telerehab Automations

  • [ ] Triggers: treatment_plan.assigned, treatment_plan.session_completed, treatment_plan.completed, treatment_plan.expired, low-adherence detection.
  • [ ] Actions: send_session_reminder, book_followup_appointment (depends on F7).

Exit criteria: Specialist creates plan, patient assigned, patient does sessions, progress tracked. Physical session tracking works with remaining session count.


F10. Telemetry Service

Separate Go service (services/telemetry/). PG aggregates on the same RDS as Core API + S3 replay blobs. No ClickHouse, no separate compliance Postgres, no audit_log forwarding. Doesn't block any other feature — runs in parallel with F1–F9. Locked design in /telemetry/index.md and /telemetry/api.md. Rationale in decisions.md → Why telemetry is PG + S3, not ClickHouse.

F10.1 Telemetry API setup

  • [ ] services/telemetry/cmd/ entry point.
  • [ ] Cat F service-account principal for callbacks to Core API.
  • [ ] Signed-session-token verifier (HS256). Issuer ships in Core API at exercise-session start.
  • [ ] Three typed ingest endpoints: POST /v1/pose/frames, POST /v1/media/events, POST /v1/sessions/{id}/end.
  • [ ] Health check endpoint.

F10.2 In-flight buffer + aggregator

  • [ ] SessionBuffer interface; default impl: per-batch S3 PUT under s3://restartix-telemetry/{org_id}/{session_id}/inflight/{batch_seq}.bin.gz, concatenate-and-finalize into the canonical replay blob at session_end (S3 multipart's 5 MB minimum part size is incompatible with 1-2 KB compressed batches; see telemetry/index.md → Storage / S3 (replay blobs)).
  • [ ] LandmarkCodec interface; default impl: binary float32 + gzip per batch.
  • [ ] Server-side aggregation at session_end: per Class I scope (rep count + ROM + session completion as informational signals; no form_score at Class I — see telemetry/index.md → Aggregation engine).
  • [ ] algorithm_version recorded on every pose_session_metrics row at session_end; never retroactively rewritten.
  • [ ] Backpressure: pose batches droppable, finalizer non-droppable.

F10.3 Aggregate persistence (Core API side)

  • [ ] PG migrations for pose_session_metrics, pose_rep_metrics (monthly partitioned per P41), media_session_metrics, media_buffering_events (monthly partitioned).
  • [ ] Update patient_exercise_logs columns (video_watch_percentage, pose_accuracy_score, actual_sets, actual_reps).
  • [ ] Events.Bus subscriber that consumes Telemetry's session-aggregated event and writes the rows.
  • [ ] Data classification entries (P39) for every new column + S3 blob class.

F10.4 Reads (Core API)

  • [ ] GET /v1/me/exercise-sessions (Patient Portal).
  • [ ] GET /v1/patients/{id}/exercise-sessions (Clinic app).
  • [ ] GET /v1/exercise-sessions/{id}/replay — mints signed S3 URL.
  • [ ] Materialized views per cohort dashboard, refreshed nightly.
  • [ ] Telemetry API rejects ingest with 403 if analytics (media) or biometric (pose) consent flag is not active.
  • [ ] Withdrawal takes effect immediately.
  • [ ] Replay blob retention via S3 lifecycle (standard → IA → Glacier → expire).
  • [ ] CI guard cmd/check-telemetry-bounds rejects direct PG/S3 access from Telemetry handlers.

Exit criteria: Patient does a pose-tracked exercise session in Portal; landmarks ingest to Telemetry API; aggregates appear in PG via events.Bus; replay blob lives in S3; specialist views the session + replay in Clinic app.


F11. Compliance Hardening + Production Deploy

Scope: technical and regulatory hardening features. Consumes data from F1–F9. Operational launch-readiness items (Sentry org setup, Bunny CDN account, first-clinic onboarding runbook, support escalation, on-call rotation, legacy-data migration execution) live in production-launch-readiness.md — that's the operational gate, distinct from this feature set. F11 ships the features the production environment needs; production-launch-readiness.md is the checklist that flips the switch on real patients.

F11.0 IEC 62304 Preparation

Anticipated Class I MDR posture (IEC 62304 Class A or B safety class), designed for Class IIa-upgrade if/when treatment decisions are driven from clinical pose measurements. See CLAUDE.md → Medical Device Readiness for the regulatory framing. Confirm class with regulatory counsel during F11.0.5 before scope expands.

  • [ ] Risk table for all clinical features (hazard, severity, likelihood, existing controls). Class I scope means risk-management rigor proportional to "informational data, not clinical decisions"; revise upward if Class IIa is confirmed.
  • [ ] SOUP list update (started in 1A.13) — full inventory with risk assessment. MediaPipe + the in-house aggregation engine are the primary additions for F9 telerehab.
  • [ ] Reframe implementation plan as Software Development Plan (SDP) at IEC 62304 Class A/B rigor.
  • [ ] Verify requirement IDs on all clinical feature specs.
  • [ ] Verify traceability: each clinical requirement ID has at least one referencing test.
  • [ ] Document the regulatory boundary in architecture docs.

F11.0.5 Romanian compliance pass (ANSPDCP)

Pre-launch legal review by a Romanian data-protection lawyer. Required before any real patient interacts with the platform on production. Drives revisions to the privacy notice template seeds (1B.10), the consent purpose catalog (1B.9), and any telemedicine/biometric flows.

  • [ ] Engage data-protection counsel — Romanian-specialised firm with healthtech experience.
  • [ ] Law 190/2018 review — Romanian GDPR implementation; quirks on processing the national ID number (CNP) even with consent, and on employment relationships.
  • [ ] Age of digital consent — Romania at 16; below 16 requires parental consent. Verify portal sign-up + telemedicine flows respect this.
  • [ ] Telemedicine framework — Order 1.589/2020 + Order 1.488/2020 from the Romanian Health Ministry set technical and procedural requirements for telemedicine consultations; verify F5 (Daily.co integration) + F9 (telerehab) compliance.
  • [ ] Medical records retention — Law 95/2006 + supporting orders set retention periods (some up to 100 years). Takes precedence over GDPR Art. 17 erasure rights via Art. 17(3)(c) — codify in F11.1's erasure flow.
  • [ ] Biometric data — ANSPDCP guidance on biometric processing; relevant for tablet signature capture (1B.10's signature surface) and pose-estimation features (F9).
  • [ ] Cross-border transfer — RestartiX uses sub-processors in non-EU jurisdictions (Clerk in the US; Daily.co; possibly others). Annex SCCs to the DPA + transfer impact assessment in the privacy notice template's cross_border_transfer toggleable section.
  • [ ] Recent ANSPDCP enforcement actions — quick scan of the last 12 months of fines to identify regulator focus areas; adjust priorities.
  • [ ] Privacy notice template revision — apply the lawyer's findings to the seeded EN + RO templates (1B.10). Mark them as "production-ready" only after this pass.
  • [ ] DPA template revision — apply findings to the platform's standard DPA, including SCCs and the sub-processor list.
  • [ ] Document outcome — write findings + decisions to a follow-up entry in decisions.md.

F11.1 GDPR Implementation

  • [ ] Data subject access request (DSAR) endpoint — export all patient data.
  • [ ] Right to erasure endpoint — anonymise patient data (retain audit trail per Art. 17(3)(c)).
  • [ ] Granular consent UI built on top of F3.5 ledger.
  • [ ] Data processing records.
  • [ ] 72-hour breach notification procedure documented + alerting.
  • [ ] Cookie/tracking consent.

F11.2 Encryption (prod)

  • [ ] Production KMS key + rotation procedure tested at scale.
  • [ ] TLS enforcement on all connections (browser↔app, app↔DB, app↔external).

F11.3 Security Hardening

  • [ ] Rate limiting beyond auth + public (per-endpoint, per-user, per-org).
  • [ ] CORS configuration (prod allow-list).
  • [ ] Input validation + sanitisation audit.
  • [ ] Security headers (HSTS, CSP, X-Frame-Options) in prod.
  • [ ] WAF rules.

F11.4 Production Technical Hardening

The infrastructure topology is fully specified in aws-infrastructure.md, iac-layout.md, scaling-architecture.md, deployment.md, and backup-disaster-recovery.md. This sub-phase is the "provision the production environment + verify it under load" pass — the IaC apply happens here, not the design.

  • [ ] Provision infra/envs/production Terraform — Multi-AZ RDS db.t4g.medium, NAT Gateway, Multi-AZ pgbouncer, Multi-AZ Redis with replica, on-demand Fargate (no Spot), all per iac-layout.md.
  • [ ] Verify backup posture end-to-end — RDS PITR working, daily pg_dump to S3 with checksum, lifecycle to Glacier IA at 90d / Deep Archive at 365d, cross-region replication to a second EU region, restore drill executed (per backup-disaster-recovery.md → Testing & Validation).
  • [ ] Tune Fargate auto-scaling bounds against production-shape traffic — initial bounds per scaling-architecture.md → Auto-scaling parameters; revise after first load test.
  • [ ] Performance benchmarks (k6 load tests) against production environment with synthetic user shape; record baseline p50/p95/p99 latency, error rate, DB connection saturation.
  • [ ] Customer-managed KMS migration if F11.0.5 / regulatory counsel triggers it (otherwise Phase 1 AWS-managed KMS continues per aws-infrastructure.md → Customer-managed KMS migration path).

Exit criteria (for F11 as a feature set): GDPR DSAR + erasure work end-to-end. Production KMS rotation tested. Rate limiting active per route/user/org. Security scan passes. Production environment provisioned and load-tested. Note: "production environment can take real traffic" is the F11 exit; "production environment IS taking real traffic" is the production-launch gate, captured in production-launch-readiness.md.


F12. Billing & Invoicing (platform → clinic)

Engine that turns subscription state + usage data + AI cost line items into invoices charged to the clinic. Foundation declares the capability interfaces (1C.1's payment.Provider + invoicing.Provider); F12 ships the engine + concrete provider impls. Romania-aware via per-org provider resolution (1C.2). Patient → clinic billing stays in the clinic's domain via Option A (clinic-BYO payment provider; webhook integration via 1C.4); marketplace mediation (Option B) is a separate post-launch feature.

Trigger: when manual invoicing of paying clinics becomes operationally painful — likely after 5–10 paying clinics on the platform. Until then, clinics on payment_provider='manual' get hand-cut FGO invoices and the engine waits.

Why dedicated feature, not part of F11: F11 is operational hardening (compliance, GDPR, security, deploy). Billing is product surface (subscription state machine, invoices, dunning, billing UI) — different scope, different risk profile, different team competence. Mixing them would slow F11.

Locked decisions (from foundation design phase, see foundation.md → 1C.1, 1C.2 and glossary → Marketplace mediation):

  • Provider strategy via Cat A abstraction. payment.Provider and invoicing.Provider capability interfaces (declared at foundation 1C.1) are switchable per-org via platform_service_providers (1C.2 resolver). Default impls land in F12; per-org overrides for Romanian clinics or dedicated tier slot in the same way.
  • International default: Stripe (mature, Stripe Tax handles VAT, well-known UX). Stripe also works for many Romanian clinics.
  • Romanian invoicing default: FGO (already in use by the platform). Handles RO VAT + e-Factura submission to ANAF. Per-org override for clinics that prefer SmartBill or e-Factura direct.
  • Romanian payment provider: per-org choice. Stripe works; Netopia / Euplatesc available as override impls if a clinic specifically needs them.
  • Tax always delegated to invoicing provider. Platform NEVER computes tax rates itself. We pass line items + clinic's organization_billing.tax_id_encrypted + currency; invoicing provider returns assembled invoice with tax calculated.
  • Patient → clinic billing stays in clinic's domain. patient_subscriptions (1B.7) is informational; clinic uses their own payment infrastructure. Cat C webhooks (1C.4) sync subscription state from clinic's payment provider into our records when a clinic wants integration. Marketplace mediation (platform-mediated patient payments) is the deferred F-Marketplace feature, not F12.
  • AI cost passthrough with markup. Each AI call's usage_records.cost_cents (from 1C.8 pricing-history) is summed per period; F12 invoice generation applies the platform's markup (TBD in F12 design — likely 30%) and surfaces as a separate invoice line item.

What F12 ships:

  • Subscription state machineorganization_subscriptions.status transitions (pendingactivepast_duecanceled → ...). Driven by payment.Provider webhook events (Cat D) + explicit admin actions.
  • Invoice generation cron — at period end, query organization_subscriptions + usage_summaries (1C.7) + AI cost roll-ups (1C.8) → assemble invoice line items → call invoicing.Provider.IssueInvoice → store invoice metadata locally.
  • Dunning — failed payment → retry schedule → suspend subscription on chronic failure → notify clinic admin via 1A.18 notifications.
  • Refund flow — admin action triggers payment.Provider.Refund → state update → invoice credit note via invoicing.Provider.
  • Billing UI — clinic admin sees plan + usage + invoices; Console superadmin sees billing across orgs.
  • Inbound webhooks (Cat D, per 1C.6): Stripe / Netopia / Euplatesc payment event handlers (payment.succeeded, subscription.updated, etc.). Each provider impl ships its own verifier + handler under the codified Cat D convention.
  • Concrete impls: internal/core/billing/payment/stripe/, internal/core/billing/payment/netopia/ (when needed), internal/core/billing/invoicing/stripe/, internal/core/billing/invoicing/fgo/. Each follows 1C.1 capability convention with Fake doubles for tests.
  • Two invoice cadences per locked decision: monthly invoicing (SaaS norm) + annual-with-monthly-usage-true-up for clinics that prefer annual prepay.
  • Overage billing for soft-meter capabilities (1B.5 Limit semantics) — per-unit overage cost configured on tier_limits.overage_cents_per_unit (new column added in F12 migration); billing engine sums excess units × rate as line items. Hard-block capabilities get no overage; clinic must upgrade tier to continue.

Open design questions (settle when F12 design starts):

  • AI cost markup percentage — likely 30%, but pin against actual provider costs + competitive analysis at design time.
  • Romanian payment provider default — Stripe for everyone, or Netopia for RO clinics by default? Affects setup operations.
  • Trial periods — Free tier perpetual? Pro tier 30-day trial? Consult sales/marketing.
  • Annual invoice format — single big invoice or 12 monthly invoices issued upfront?
  • Billing UI placement — clinic admin app dedicated section vs. Console-only? Likely both with clinic-side restricted to read-own + initiate-card-update.
  • Per-tier pricing (shared vs. dedicated) — surface fees + AI markup + storage rates etc.

Dependencies:

  • F11 ships first (operational hardening; production deploy).
  • 1C.1 capability skeletons for payment.Provider + invoicing.Provider (foundation, ships before F1).
  • 1C.2 provider resolver (foundation).
  • 1C.7 metering + usage_summaries (foundation).
  • 1C.8 AI cost capture + ai_model_pricing_history (foundation).
  • 1A.18 notifications (foundation, shipped).
  • 1C.4 outbound webhooks (foundation, for clinic-side billing-event integration when clinic uses Option A).
  • 1C.6 inbound webhooks (foundation, for Stripe / Netopia provider webhooks).

Exit criteria: clinic onboards → automatic monthly invoice cycle works (subscription + usage + AI cost rolled up; invoice generated; clinic charged; receipt email sent); failed payment dunning + suspension works; refund admin action works; clinic-facing billing UI shows current plan + usage + invoice history; Romanian clinic with provider_name='fgo' override successfully receives e-Factura-submitted invoice.


Post-launch (deferred until first paying dedicated-mode clinic contract)

F13. Tenant Isolation Mode (Dedicated Tier)

Trigger: first paying clinic contract that demands a per-tenant identity namespace + data-sovereignty story and can fund the operational setup. Not part of the regular feature track. Designed and documented now; built when a paying contract funds the work.

Status: settled (architectural commitment), deferred (build). Default platform mode is tenancy_mode = 'shared'; this is the premium tenancy_mode = 'dedicated' tier. Both modes target SMB clinics; hospital networks and dedicated-infrastructure tiers are permanently out of scope (see CLAUDE.md → Project Overview). The marketing label "White-Label" maps to dedicated mode in sales conversations but is decoupled from the architectural name — a shared-mode clinic with a custom domain is also visually white-labeled. See the full spec at features/platform/tenant-isolation.md and decisions.md → Why tenancy_mode is a single enum, not multi-axis.

Foundation-cheap pre-work (shipped in 1B + 1E):

  • [x] Add humans.provider_org_id TEXT NULL column to migrations/core/000002_tenancy_rbac.up.sql (no FK initially — FK target lands with the dedicated-mode runtime feature).
  • [x] Replace humans.email NOT NULL UNIQUE with UNIQUE (email, provider_org_id) NULLS NOT DISTINCT in the same migration. Functionally identical for shared-mode tenants today (all have NULL provider_org_id); future-proofs for dedicated mode where the same email can exist once per auth-provider tenant.
  • [x] Add organizations.tenancy_mode TEXT NOT NULL DEFAULT 'shared' CHECK (tenancy_mode IN ('shared', 'dedicated')) to the same migration. Single-enum discriminator; no creation flow accepts 'dedicated' yet.
  • [x] Add organizations.activated_at TIMESTAMPTZ NULL + idx_organizations_draft partial index. NULL = draft; every creation path today sets NOW() in the same transaction. Public-resolve and owner-welcome gate on activated_at IS NOT NULL.

These schema lines are the entirety of the now-work. Pre-prod cost: a handful of lines edited. Post-prod cost: real migrations touching every humans and organizations row plus deployment-coordinated downtime. Land now.

Deferred build (when first paying dedicated-mode contract closes):

  • Per-tenant Clerk org provisioner (auth-provider Backend API integration; writes the dedicated provider_org_id per tenant).
  • Re-introduced finalize-provisioning endpoint with proper preconditions (Clerk org exists, platform_service_providers overrides written) that flips activated_at = NOW() and queues the welcome email.
  • Addons via the entitlements catalog: own_s3_bucket (ships with the exit / portability tool) and own_cmk (ships with the documented crypto-shred runbook). Available on either tenancy mode; not coupled to tenancy_mode = 'dedicated'.
  • Terraform module for per-tenant infrastructure (S3 bucket + CMK + IAM bindings + Clerk org wiring).
  • Operational templating (DNS, ACM cert, SES sender, SMS sender ID, Daily.co domain) — bulk of the cost; the universal branding pieces work on shared mode too.
  • Dedicated-mode DPA template (legal counsel work, can run in parallel).

Pricing model: one-time setup fee + premium MRR uplift (industry comparable: 5–10x default tier) + termination service fee. See spec for breakdown.


Open decisions (features)

DecisionWhereWhen to resolve
Cross-purpose withdrawal cascade for Tier B medical consents — independent vs cascadingF3.5Before F3.5 ships
Signature image format — PNG + SVG vs PNG onlyF3.5.3Before F3.5 ships
Sent-to-phone link revocation behaviourF3.5.3Before F3.5 ships
PDF rendering engine — ChromeDP, wkhtmltopdf, or Gotenberg?F6Before F6.2
SMS/push providers — confirm Twilio for both, or alternatives (MessageBird, push provider TBD)? Email is locked: AWS SES (foundation 1A.18).F7Before action handlers ship
AI cost markup percentage for clinic invoices (likely 30%, pin against actual provider costs + competitive analysis)F12Before F12 design starts
Romanian payment provider default — Stripe for everyone, or Netopia for RO clinics by defaultF12Before F12 design starts
Mobile app approach — React Native, Flutter, responsive web only?F11+Before mobile work

Resolved this cycle:

  • Exercise video CDN — Bunny CDN (Bunny Stream library + their CDN) is the locked direction for exercise video hosting + delivery. Confirmed in the AWS-infrastructure consolidation chat. The video_provider flexibility column on exercises stays (preserves the swap-point if a clinic-specific override is ever needed), but the platform default is Bunny. Pre-launch wiring task: account setup + DPA review + admin upload workflow lights up with F9.
  • Email transport — AWS SES locked at foundation 1A.18 (production identity verification + suppression list close in 1E.3). Per-org override path via Cat A provider resolution preserved. SES is the only adapter the foundation ships; Twilio (SMS) and other channels slot in via the same Channel interface when their first consumer ships in F7.
  • Aggregation engine for telemetry — in-house heuristic (rep count + ROM + session completion as informational signals at Class I MDR posture). Vendor (Sency / Kemtai / Physitrack) rejected: competitors for parts of the platform; biometric data + algorithm leaves the trust boundary; per-user/month cost dominates at scale. Build path: a Go engineer + MediaPipe + per-exercise reference videos + heuristic rep segmentation. See telemetry/index.md → Aggregation engine and CLAUDE.md → Medical Device Readiness.
  • Payment + invoicing provider strategy — Cat A capability abstraction (payment.Provider + invoicing.Provider) declared at foundation 1C.1; per-org resolution via 1C.2's platform_service_providers table. Stripe international default; FGO + e-Factura Romanian default. White-label tier per-org overrides supported. Billing engine itself ships in F12, not F11. See foundation.md → 1C.1 / 1C.2 and F12 above.
  • Marketplace mediation (Option B — patient → clinic via platform with fee + payout) — strategic future feature, not foundation, not F12. Foundation accommodates via four Cat A capability skeletons (payment.Provider, invoicing.Provider, patient_payment.Provider, clinic_payout.Provider); no implementations yet. See foundation.md → Deferred Foundation Extensions → Marketplace Mediation.
  • Lawful-basis discriminator — yes, in schema. consent_purposes.legal_basis enum lands in 1B.9. See decisions.md → Why clinic is controller, platform is processor.
  • Withdrawal mechanics for SaaS-level consents — independent withdrawal per purpose; withdrawal of platform-scope platform_terms triggers the GDPR erasure flow (F11.1). Withdrawal of org_terms triggers per-org patients.deleted_at cascade. See foundation.md → 1B.9.
  • PII storage — humans.email, organizations.phone, patient_profiles.phone, patient_profiles.emergency_contact_phone, appointments.contact_email are all plaintext (pii_basic). Column-level encryption is reserved for auth_secret and pii_regulated (e.g., organization_billing.tax_id_encrypted). Mechanically enforced by cmd/check-classification. See decisions.md → Why most PII is plaintext (and what isn't).

Cross-feature concerns (ongoing)

Testing

  • Unit tests, table-driven, throughout every feature.
  • Integration tests for repos, handlers (RLS harness from 1A.2).
  • E2E tests for full flows (booking, consent → form → call, plan assignment → session execution, GDPR DSAR).
  • Performance tests in F11.

Frontend integration contract

  • API contract conventions in 1A.7.
  • OpenAPI spec-first (Go + frontend types regenerated on schema change).
  • Real-time integration (SSE) lands per-feature (F4 for slot availability).
  • File upload pattern (1A.8) used by F3 forms.

Parallel work streams

Some features can run concurrently once 1D closes:

Foundation:   [1A done] [1B in progress] [1C in progress] [1D pending]
                                                              |
Features:                                                     |--F1--|
                                                                     |--F2--|
                                                                     |--F3--|  (F2 + F3 in parallel)
                                                                            |--F4--|
                                                                                   |--F5--|
                                                                                          |--F6--|
                                                                                          |--F7--|
                                                                                          |--F8--|
                                                                                          |--F9----|
                                                              |--F10 (parallel) -----------------|
                                                                                                  |--F11--|

Key parallelism:

  • F2 (Catalog) and F3 (Forms) can be built in parallel after F1.
  • F10 (Telemetry) can begin as soon as 1A.1 audit writes are real (already true) — runs concurrent with all other features.