Skip to content

1D Admin UI — Backend → UI Surface Inventory

Status: audit output, not a plan. Produced 2026-05-07 immediately after foundation 1C closed. Author: Claude (Opus 4.7) audit pass. Scope: every shipped foundation backend (1A–1C) mapped to the UI surface it needs in 1D, plus the 1D primitives that don't exist yet, plus the OpenAPI / api-client gaps that block UI work.

This document does not propose new features, redesign existing pages, sequence work, or estimate effort. It catalogs what exists in the backend, what UI it needs, what UI is already built, and where the gaps are. The follow-on 1D execution plan consumes this inventory; producing that plan is a separate exercise.

How to read this doc

  • The four 1D sub-phases (1D.1 Console, 1D.2 Clinic admin, 1D.3 Portal, 1D.5 Cross-org Account) get one section each. Each surface entry lists its backend endpoints, required permission, OpenAPI status, api-client status, and UI status (built / stub / not started).
  • 1D.4 (Shared UI Patterns) is its own section — primitives the per-app surfaces consume.
  • A "Gaps" section flags every place where a UI surface is blocked by missing OpenAPI or missing api-client wrapper.
  • A "System-only — no UI needed" section enumerates backend functionality that exists for cron/internal purposes; future engineers shouldn't re-ask whether it needs UI.
  • A "Decisions / ambiguities" section flags everything that needs human input before 1D starts.

Authoritative sources cited throughout

  • apps/docs/implementation-plan/foundation.md — sub-phase status and 1D.1–1D.5 scope (line numbers cited inline)
  • services/api/internal/core/server/routes.go — every mounted route
  • services/api/internal/core/domain/{name}/handler.go — endpoint behavior + permission gates
  • apps/docs/openapi.yaml — current spec (incomplete; see Gaps)
  • packages/api-client/src/client.ts — existing client wrappers (hand-written; see Gaps)
  • apps/clinic/app/, apps/portal/app/, apps/console/app/ — existing UI
  • apps/docs/architecture/{glossary,data-model,patterns,decisions}.md — taxonomy, schema, conventions

When a feature spec under apps/docs/features/ describes UI for a backend slice that doesn't exist yet, that surface is in the "Out of 1D scope" section. Per CLAUDE.md → Foundation Discipline, feature specs predate the holistic schema audit and lose to architecture docs on schema details.


Top-line counts

Counts below are pre-decision snapshots from the 2026-05-07 audit. Effective post-decision counts (after the 12 decisions resolved 2026-05-07) are in the Appendix — surface counts by app; the deltas come from D-1 / D-3 / D-6 / D-7 (six rows removed) and D-12 (31 F-tier sidebar entries pruned).

ItemCount
Distinct UI surfaces catalogued (across 1D.1 + 1D.2 + 1D.3 + 1D.5)121
Surfaces already built (full or partial)22
Surfaces with nav stub but no implementation41
Surfaces with no nav entry yet58
Backend endpoints (foundation 1A–1C)105 across 24 domain handlers
Endpoints with OpenAPI entry~53 (~50% coverage)
Endpoints with api-client wrapper~38 (~36% coverage)
Distinct permission codes the UI needs to honor~30
Shared UI primitives needed (1D.4) but not yet built4 (toast, RequirePermission, server-validation renderer, empty/loading/error states)
Backend tables introduced in 1A–1C~50
Backend domain handlers with zero OpenAPI coverage12 (locations, impersonation, webhooks, integrations, integration-services catalog, platform-providers, ai-models, break-glass, audit, invitations, consents, legal-documents)
Decisions resolved 2026-05-0712 of 12 (D-1 through D-12) — see Decisions
Surfaces removed by decisions6 (C19, C21, C24, C25, C33, C34)
Surfaces moved to 1E1 (C30 system health)
Net 1D-scoped surfaces post-decision73 (was 79)

The OpenAPI gap is the single biggest blocker — see § Gaps to flag.


1D.4 — Shared UI Patterns (cross-cutting)

packages/ui/ already ships the building blocks for tables, sidebars, dialogs, etc. The 1D.4 work is the conventions every per-app surface depends on. From foundation.md:1300–1316:

PrimitiveStatusWhere it lives / will liveConsumer surfaces (every app)
DataTable (TanStack-backed; server-driven sort / filter / pagination + detail Sheet)Builtpackages/ui/src/components/data-table.tsxEvery list page
MultiSelectFilter, AsyncMultiSelectFilter, DateRangeFilterBuiltpackages/ui/src/components/{multi-select-filter,async-multi-select-filter,date-range-filter}.tsxAudit, lists, search
App shell + brand theme (sidebar, OKLCH tokens, Poppins, light/dark)Builtpackages/ui/src/components/sidebar.tsx; per-app components/app-sidebar.tsxEvery dashboard
Listing-page pattern (fill mode, sticky toolbar, edge-to-edge tables)BuiltPattern in apps/console/app/(dashboard)/audit-logs/page.tsxEvery list page
Branded 404 pages with i18nBuiltEach app's not-found.tsxAll apps
<RequirePermission /> helperNot builtBelongs in packages/ui/src/patterns/Every gated control across all 3 apps
Toast / notification systemNot builtBelongs in packages/ui/src/components/Every mutation feedback (publish, save, delete, copy, etc.)
Server-validation rendering (422 field errors → form field error states)Not builtBelongs in packages/ui/src/patterns/Every form (org settings, billing, consent, share-link mint, etc.)
Empty / loading / error states standardisedNot builtBelongs in packages/ui/src/patterns/Every list and detail page
AsyncMultiSelectFilter for entity pickers (P-required: GIN trigram + unaccent)BuiltAlready used by audit-logsAudit, members, patients, specialists, segments

Existing packages/ui primitives a 1D engineer can reuse without thinking: button, input, form-field, textarea, input-group, checkbox, switch, select, card, dialog, dropdown-menu, popover, tabs, collapsible, badge, skeleton, tooltip, breadcrumb, separator, table, calendar, avatar. Patterns: field, section-card-header, stat-card, status-pill, user-avatar. Hooks: use-mobile, use-server-synced-state.

Resolved per D-4: the four un-built primitives ship as ONE packages/ui PR BEFORE any per-app 1D.1/1D.2/1D.3/1D.5 surface starts. Every per-app consumer composes against the same versioned primitives.


1D.1 — Console (platform superadmin)

Hostname: console.restartix.pro. Auth: Clerk. Access gate: is_superadmin (membership in platform_memberships, human-only by CHECK).

Existing UI today: 17 pages (most are nav stubs). See apps/console/app/(dashboard)/ for what exists.

The Console layout, sidebar nav, and the foundational pages (clinics list, clinic detail tabs, audit logs, legal-document templates, platform legal documents) are already wired and consume real backend endpoints. The gap is the depth — most clinic-detail tabs and most catalog/registry pages are mock or stub.

1D.1.A — Identities & access (catalog scope = platform-global)

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
C1Users — list humans across platform; view memberships across orgs; block / unblockGET /v1/users (handler human/handler.go:255)admin_superadminlistUsersSidebar nav stub /users; no pageOpenAPI
C2Agents — list AI agent principals (none yet)none yet — agents table exists, no handlerSidebar nav stub /agents; no pageDefer until first agent backend ships
C3Service accounts — list non-human principals (Cat F integrations)none yet — service_accounts table exists, no handlerSidebar nav stub /service-accounts; no pageDefer until Cat F backend ships
C4Platform memberships — grant / revoke superadmin (+ future support_engineer)POST/GET/DELETE /v1/admin/platform-memberships[/{principalId}] (1D.0)superadminNo nav entryBackend ready — OpenAPI + api-client bundle with this UI
C5Permission catalog viewer (read-only — code / resource / action / description per row)GET /v1/admin/permissions (1D.0)superadminNo nav entryBackend ready — OpenAPI + api-client bundle with this UI. "Migration introduced" column dropped from the original surface
C6System role templates viewer (read-only per D-2) — list system role templates (admin, specialist, customer_support) with permissions per template. Editor deferred post-1D until cross-tenant propagation semantics are designed.GET /v1/admin/role-templates (1D.0)superadminNo nav entryBackend ready — read-only viewer; mutation endpoints remain deferred per D-2

1D.1.B — Tenants

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
C7Clinics list — table of orgs; create new clinic dialogGET /v1/organizations; POST /v1/organizationssuperadmin_onlylistOrganizations; ❌ createOrganization (missing)Builtclinics/page.tsx (list) + NewOrganizationDialogapi-client wrapper for create
C8Clinic overview (clinic detail tab) — stat row, snapshot card, attention nudges, last 5 audit events, quick actionsGET /v1/organizations/{id} (cached orgResource(id, "summary"))any-member RLS or superadmingetOrganizationPartial — page renders; stats stay mock at 1D closeAggregate-stats endpoint deferred per 1D.0 decision (storage tracker doesn't exist; MRR shape unsettled). Lands later in 1E observability / production-launch-readiness
C9Clinic profile tab — edit name / slug / language; branding & white-label preview (logo, brand color, email-from-name)PATCH /v1/organizations/{id}organizations.updateupdateOrganizationPartial — name/slug/language editable; branding card is mockBackend gap — branding columns + asset-upload endpoints not yet defined (universal across shared and dedicated tenancy modes; ships post-foundation)
C10Clinic members tab — staff directory (no PII gating); add member dialogGET /v1/organizations/{id}/members; POST /v1/organizations/{id}/members; DELETE /v1/organizations/{id}/members/{principalId}; GET /v1/organizations/{id}/rolesorganizations.manage_members✅ all fourBuiltclinics/[id]/members/page.tsxNone
C11Clinic patients tab (privacy boundary) — aggregate stats default; break-glass elevated read-only patient tablePOST /v1/break-glass/sessions; GET /v1/organizations/{id}/patients (under elevation); GET /v1/break-glass/sessions/{id}superadmin (open break-glass), then RLS via active session❌ break-glass; ✅ patients list❌ break-glass wrappers; ❌ patients list wrapperPartial — privacy panel + aggregate stats present; ?bg=active shows mock elevation; real elevation modal not wiredOpenAPI for break-glass, api-client wrappers for both
C12Clinic plan tab — current plan, active sales overrides, billing form, patient tiers (read-only echo of clinic admin), entitlements togglesGET /v1/organizations/{id}/subscriptions; GET .../{subId}/overrides; POST .../{subId}/overrides; POST .../{subId}/overrides/{ovId}/revoke; GET /v1/organizations/{id}/billing; PATCH /v1/organizations/{id}/billing; GET /v1/organizations/{id}/entitlements; PATCH /v1/organizations/{id}/entitlements; GET /v1/organizations/{id}/patient-tiersmix: subscriptions.view_org, subscriptions.manage, superadmin, organizations.manage_billing, RLS for entitlements GET, superadmin for entitlements PATCH✅ subs / overrides / billing / entitlements / tiers❌ allPartial — page is mostly mock; no real dataapi-client wrappers for all 9 endpoints
C13Clinic audit tab — per-org audit log slice (metadata always-on; diffs/IPs/bodies gated on break-glass)GET /v1/audit-logs?organization_id={id} (currently superadmin platform-wide; per-org scoping uses query filter)superadmin (and break-glass for content)listAuditLogsStub — page exists with mock content; "Open full audit log" button worksOpenAPI for audit-logs
C14Clinic settings tab — operations + compliance / locale form; domains; legal documents (read-only echo); close clinic (danger). Per D-6: feature_flags JSONB is engineering-internal and NOT exposed in this form.GET /v1/organizations/{id}/settings; PATCH .../settings; GET .../domains; POST .../domains; DELETE .../domains/{domainId}; POST .../domains/{domainId}/verify; GET .../legal-documentsorganizations.update_settings, organizations.manage_domains, organizations.manage_privacy_noticesettings ✅; domains ✅; legal-documents ❌settings ✅; domains ✅ (different naming); legal-documents ✅Partial — settings + domains wired; legal docs read-only echo; "Close clinic" disabled (no soft-delete endpoint yet)"Close clinic" → backend gap (no DELETE /v1/organizations/{id} exists; not in 1D scope per glossary)
C15Onboarding — list orgs in onboarding state, stale-template listGET /v1/admin/legal-document-templates/stale-orgssuperadminlistLegalDocumentStaleOrgsSidebar nav stub /onboarding; partially shown on legal-document-templates pageOpenAPI for stale-orgs; full onboarding dashboard is in scope but content is sparse

1D.1.C — Subscriptions & catalog (Console-driven)

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
C16Subscriptions (cross-org view) — list all org subscriptions; filter by tier, statusGET /v1/admin/subscriptions[?org_id=&status=&tier_id=&page=&limit=] (1D.0)superadminSidebar nav stub /subscriptions; no pageBackend ready — each row enriched with organization_name, organization_slug, active_overrides_count. Per-org mutations on existing per-org routes
C17Sales overrides (cross-org view) — list active overrides; grant / revokeGET /v1/admin/subscriptions (1D.0) + per-org override routes (existing)superadminNo nav entryBackend ready — list is the same aggregator as C16; per-row drill-down via existing per-org POST .../subscriptions/{subId}/overrides + POST .../overrides/{ovId}/revoke
C18Tiers catalog — list tiers, view detail, see entitlements / limits / version historyGET /v1/tiers; GET /v1/tiers/{id}; GET /v1/entitlements; GET /v1/limit-definitionsauth_only (any signed-in user; superadmin pages just consume)Sidebar nav stub /tiers; no pageapi-client wrappers for all 4
C19Patient tiers (cross-tenant)REMOVED per D-1. Patient tiers stay clinic-side only (L17). Console /patient-tiers sidebar stub deleted at 1D.1.Sidebar nav stub /patient-tiersSidebar entry removed
C20Specialties / Services / Exercises / Forms catalogs (Console superadmin views of per-org content)none yet — these are clinical domain backends not built in 1A–1CSidebar nav stubs /specialties, /services, /exercises, /forms; no pagesOut of 1D scope — backends not built. See Out of 1D scope

1D.1.D — Communications & integrations

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
C21Notification templatesREMOVED per D-3. Foundation templates stay migration-managed; F-tier features add per-template editing if needed. Console /notifications sidebar stub deleted at 1D.1.Sidebar nav stub /notificationsSidebar entry removed
C22SES suppression list (1A.18 — closes in 1E)none yet — gated by 1E AWS stagingSidebar nav stub /suppression; no pageDefer to 1E
C23Announcementsnone yet — no backendSidebar nav stub /announcements; no pageOut of 1D scope — no backend
C24Webhooks (cross-tenant)REMOVED per D-1. Cat C subscriptions stay clinic-side (L18). Console /webhooks sidebar stub deleted at 1D.1.Sidebar nav stub /webhooksSidebar entry removed
C25Connectors (cross-tenant)REMOVED per D-1. Cat B integrations stay clinic-side (L19). Console /connectors sidebar stub deleted at 1D.1.Sidebar nav stub /connectorsSidebar entry removed
C26Platform service providers (Cat A — 1C.2 superadmin write surface) — list / create / update / delete platform-default + per-org-override providers (email/ses, storage/aws_s3, auth/clerk)GET /v1/admin/platform-service-providers; POST /v1/admin/platform-service-providers; GET /v1/admin/platform-service-providers/{id}; PATCH /v1/admin/platform-service-providers/{id}; DELETE /v1/admin/platform-service-providers/{id} (mounted at routes.go:250)platform providers.manage or superadminNo nav entry — backend shipped 1C.2 but Console UI not yet plannedCONFIRMED IN 1D.1 SCOPE per D-5. OpenAPI + api-client + new sidebar entry. Load-bearing for dedicated-tier rollout (per-org Cat A overrides)
C27AI models registry (1C.8) — list / create / get / update / pricingGET /v1/admin/ai-models; POST /v1/admin/ai-models; GET /v1/admin/ai-models/{id}; PATCH /v1/admin/ai-models/{id}; POST /v1/admin/ai-models/{id}/pricing (mounted at routes.go:261)superadmin or platform ai_models.manageNo nav entry — backend shipped 1C.8CONFIRMED IN 1D.1 SCOPE per D-5. OpenAPI + api-client + new sidebar entry. Load-bearing for AI cost configuration

1D.1.E — Observability

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
C28Audit logs (cross-tenant) — search, paginate, sort, filter by action / entity_type / action_context / status_class / org / actor / actor_type / date rangeGET /v1/audit-logs; GET /v1/organizations?ids=...; GET /v1/users?ids=... (for chip resolution)superadminlistAuditLogs, listOrganizations, listUsersBuiltaudit-logs/page.tsx with full filter chip UIOpenAPI for audit-logs
C29Break-glass sessions — list active + recent sessions; close session; view session detail (with reason, scope, opened_at, expires_at)GET /v1/break-glass/sessions; GET /v1/break-glass/sessions/{id}; POST /v1/break-glass/sessions; POST /v1/break-glass/sessions/{id}/closeRLS (self / break_glass.manage / audit_log.view_org)Sidebar nav stub /break-glass; no pageOpenAPI + api-client. Foundational — required before C11 can wire real elevation flow
C30System health — operational platform dashboard (concurrent users, request rate, latency, error rate, dependencies, partition runway)none — page is fully mockBuilt (mock)system-health/page.tsxMOVED TO 1E per D-11. Page stays mock at 1D close; real wiring lands in 1E observability scope (consumes staging KMS/S3/RDS/SES artifacts that exist after 1E.3)

1D.1.F — Platform configuration

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
C31Legal-document templates (platform-wide) — list latest per (document_type, locale); publish new version (one row per locale at version MAX+1); see stale-orgsGET /v1/admin/legal-document-templates; POST /v1/admin/legal-document-templates; GET /v1/admin/legal-document-templates/stale-orgssuperadmin✅ all threeBuiltlegal-document-templates/page.tsx + /publishOpenAPI
C32Platform legal documents (consent purposes — platform_terms, platform_privacy_notice) — list versions; edit + publish new version (triggers re-consent platform-wide)GET /v1/admin/platform-consent-purpose-versions; POST /v1/admin/platform-consent-purpose-versionssuperadmin✅ bothBuiltplatform-legal-documents/page.tsx + [code] editorOpenAPI
C33Feature flagsREMOVED per D-6. organization_settings.feature_flags is engineering-internal (staged-rollout JSONB), distinct from entitlements. Not a UI surface — Console /feature-flags sidebar stub deleted at 1D.1; field also dropped from C14/L2 settings forms.Sidebar nav stub /feature-flagsSidebar entry removed
C34LocalesREMOVED per D-7. i18n config is system-only (next-intl messages/{en,ro}.json); no locales DB table. Console /locales sidebar stub deleted at 1D.1.Sidebar nav stub /localesSidebar entry removed
C35Settings (Console superadmin's own preferences)GET /v1/me; PATCH /v1/menoneSidebar nav stub /settings; no pageNone — straightforward to wire
C36Sales / Marketing / Compliance misc dashboardsnone yetNoneOut of 1D scope

1D.2 — Clinic admin UI (org self-service)

Hostname: {slug}.clinic.restartix.pro. Auth: Clerk + org-scoped via hostname → org-id cookie. Access gate: structural (membership row in organization_memberships for the resolved org).

Per foundation.md:1255–1280, all 1D.2 surfaces are deferred to a clinic-app refresh that has not started; the backend contracts are frozen and stable, and "no Go changes needed when this lands."

Existing UI today: 4 wired pages + 13 nav stubs. Wired pages are (dashboard)/page.tsx (legal-docs onboarding card), (dashboard)/legal-documents/page.tsx, (dashboard)/legal-documents/[type]/page.tsx, sign-in. Everything else is a sidebar entry without a page.

1D.2.A — Tenant configuration

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
L1Org profile editor — name, slug (read-only), language, default timezoneGET /v1/organizations/{id}; PATCH /v1/organizations/{id}organizations.updateSidebar nav stub /organizations; no pageNone
L2Organization settings — marketing prefs, retention override, support locale, telerehab toggle (entitlement-mirrored read-only). Per D-6: feature_flags JSONB is engineering-internal and NOT exposed in this form.GET /v1/organizations/{id}/settings; PATCH .../settingsorganizations.update_settingsSidebar nav stub /settings; no pageNone
L3Custom domains — list, add, verify (DNS TXT record), removeGET /v1/organizations/{id}/domains; POST .../domains; POST .../domains/{domainId}/verify; DELETE .../domains/{domainId}organizations.manage_domains✅ (naming differs: addDomain etc.)No nav entry; tab on Console clinic-detail page existsNone
L4Billing (read-only on Clinic) — current plan, period, contact, renewalGET /v1/organizations/{id}/billingorganizations.manage_billingNo nav entryapi-client wrappers
L5Locations (1B.5) — list, create, update, close (status), deleteGET /v1/organizations/{id}/locations; POST .../locations; GET .../locations/{locationId}; PATCH .../locations/{locationId}; DELETE .../locations/{locationId}locations.manage (mutations); RLS for readSidebar nav stub /locations; no pageOpenAPI gap (full domain) + api-client gap (full domain)

1D.2.B — Members, roles, invitations

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
L6Members — staff directory list, role-change dropdown, removeGET /v1/organizations/{id}/members; POST .../members; DELETE .../members/{principalId}; GET .../rolesorganizations.manage_membersSidebar nav stub /users; no page (Console clinic-detail tab is built)None
L7Roles editor — edit cloned system roles + create custom roles + permissions checklistnone yet — roles + role_permissions write endpoints not exposedorganizations.manage_roles (TBD)No nav entryBackend gap — full domain missing. See Gaps § G-3
L8Staff invitations (1B.12) — list (pending / accepted / revoked); invite form (email + role); revoke; resendPOST /v1/organizations/{id}/staff-invitations; GET .../staff-invitations; GET .../invitations/{inviteId}; POST .../invitations/{inviteId}/revoke; POST .../invitations/{inviteId}/resendorganizations.manage_membersNo nav entryOpenAPI gap (full domain) + api-client gap (full domain)
L9Patient invitations (1B.12) — list; invite form (email + tier); revoke; resendPOST /v1/organizations/{id}/patient-invitations; GET .../patient-invitations; (shared revoke / resend with L8)patients.manageNo nav entrySame as L8
L10Patient share-links (1B.12) — mint form (tier, max_uses, expires_at, note); list with copy / QR; revokePOST /v1/organizations/{id}/share-links; GET .../share-links; GET .../share-links/{shareLinkId}; POST .../share-links/{shareLinkId}/revokeorganizations.manage_share_linksNo nav entryOpenAPI gap + api-client gap (resolveShareLink exists but admin endpoints don't)

1D.2.C — Patients & consents

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
L11Patients list — search, paginate, sort, archiveGET /v1/organizations/{id}/patients; POST .../patients; GET .../patients/{patientId}; DELETE .../patients/{patientId}patients.view, patients.manageSidebar nav stub /patients; no pageapi-client wrappers (whole domain)
L12Patient detail — profile (mask if profile_shared = false); consents trail; subscription; impersonation history; appointments (post-foundation); forms (post-foundation)combination of L11 + L13 + L14 + L15per surfacemixedmixedNo pagecomposes L11 / L13 / L14 / L15
L13Per-patient consents (staff view + staff action) — history; grant on patient's behalf; withdraw on patient's behalfGET /v1/organizations/{id}/patients/{patientId}/consents; POST .../consents; POST .../consents/{consentId}/withdrawconsents.view_org, consents.manageNo nav entryOpenAPI gap (full consent domain) — see Gaps § G-1
L14Per-patient subscription — view active tier; cancelGET /v1/organizations/{id}/patient-subscriptions; GET .../patient-subscriptions/{subId}; POST .../patient-subscriptions/{subId}/cancelpatient_subscriptions.view_org, patient_subscriptions.manageNo nav entryapi-client wrappers
L15Per-patient impersonation history — staff sessions on this patient (sidebar of patient detail)GET /v1/organizations/{id}/patient-impersonation-sessions?patient_id=...RLS (patients.manage)No nav entryOpenAPI + api-client (whole domain)
L16Patient impersonation modal (open session with reason)POST /v1/organizations/{id}/patient-impersonation-sessions; POST .../patient-impersonation-sessions/{sessionId}/close; GET .../patient-impersonation-sessionspatients.impersonate, patients.manageNo nav entrySame as L15

1D.2.D — Patient tier configuration

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
L17Patient tiers (1B.4) — list, add, edit (entitlements + limits picker), archive (is_active=false)GET /v1/organizations/{id}/patient-tiers; POST .../patient-tiers; GET .../patient-tiers/{tierId}; PATCH .../patient-tiers/{tierId}patient_tiers.manage; RLS for readSidebar nav stub doesn't exist on Clinic; Console has stub /patient-tiers (cross-tenant)api-client wrappers; nav entry on Clinic

1D.2.E — Integrations

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
L18Outbound webhook subscriptions (Cat C — 1C.4) — create (target URL + event filters); list (filterable by status); update (URL, events, status); revoke (soft-delete); rotate signing secret (dual-secret); list deliveries; fire test deliveryPOST /v1/organizations/{id}/outbound-webhook-subscriptions; GET .../outbound-webhook-subscriptions; GET .../outbound-webhook-subscriptions/{subscriptionId}; PATCH .../{subscriptionId}; DELETE .../{subscriptionId}; POST .../{subscriptionId}/regenerate-secret; GET .../{subscriptionId}/deliveries; POST .../{subscriptionId}/testorganizations.manage_webhooks + EnforceLimit(max_webhook_subscriptions)No nav entryOpenAPI gap (full domain — 8 ops) + api-client gap (full domain)
L19Connected accounts (Cat B — 1C.5; framework-only, no consumers yet) — public catalog (GET /v1/integration-services); per-org create / list / get / update / delete / testGET /v1/integration-services; POST /v1/organizations/{id}/integrations; GET /v1/organizations/{id}/integrations; GET /v1/organizations/{id}/integrations/{integrationId}; PATCH .../{integrationId}; DELETE .../{integrationId}; POST .../{integrationId}/testorganizations.manage_integrations (per-org); rate-limited public catalogNo nav entryOpenAPI gap (full domain — 7 ops) + api-client gap. First Cat B catalog row + connector impl + OAuth callback handler is deferred to first F-tier consumer per foundation.md:1C.5, so the UI may be stub-only at 1D close

1D.2.F — Compliance

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
L20Legal documents (1B.10) — list (terms + privacy_notice cards); editor with placeholders + toggleable sections; preview; publish (creates consent_purpose_version row → triggers re-consent); refresh source templateGET /v1/organizations/{id}/legal-documents; GET .../legal-documents/{type}; PUT .../legal-documents/{type}; POST .../legal-documents/{type}/preview; POST .../legal-documents/{type}/publish; POST .../legal-documents/{type}/refresh-source-templateorganizations.manage_privacy_notice✅ allBuiltlegal-documents/page.tsx + legal-documents/[type]/page.tsxOpenAPI gap (api-client wraps via reverse-engineered shape)
L21Per-org audit log viewer (1A.1 backend; 1B equivalent of C13) — search, filter, paginate, detail SheetGET /v1/audit-logs?organization_id={current_org_id}audit_log.view_orglistAuditLogsSidebar nav stub /audit-log; no pageOpenAPI; nav-stub → page
L22Break-glass active-session banner (1B.11) — show when platform staff is currently elevated against this orgGET /v1/break-glass/sessions?org_id={current_org_id}RLS via audit_log.view_orgNo surfaceOpenAPI + api-client; persistent banner pattern (1D.4)

1D.2.G — Clinical (post-foundation; not in 1D scope)

The following surfaces are listed for completeness but their backends are F-tier features, not foundation:

SurfaceFoundation status
Calendar (month / week views)F-tier (appointments domain not built in 1A–1C)
Patients clinical detail (medical record, allergies, episodes)F-tier
Forms designer + fillerF-tier
Treatment plans (templates + assignment)F-tier
Exercise library editorF-tier
Specialists (list + license tracking + schedule)F-tier
Services / Offerings catalogF-tier (also subject to deferred servicesofferings rename per glossary)
SpecialtiesF-tier
SegmentsF-tier
Custom fieldsF-tier
PDF templatesF-tier
AutomationsF-tier
ReportsF-tier
Daily.co video callsF-tier

These appear as nav stubs (/calendar, /treatment-plans, etc.) in the existing Clinic sidebar; they should remain stubs at 1D close.


1D.3 — Patient Portal (self-service)

Hostname: {slug}.portal.restartix.pro. Auth: Clerk. Access gate: structural (patients row in resolved org). Re-consent gate (RequireCurrentConsents) blocks all per-org pages until missing consent versions are accepted.

Existing UI today: 7 wired pages, 8 nav stubs. Wired: sign-in, sign-up, landing (page.tsx), /join/[code], /onboard, (patient)/dashboard (placeholder), (patient)/consents.

1D.3.A — Identity & onboarding

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
P1Sign-up + sign-in (Clerk-hosted)none — Clerknonen/an/aBuiltsign-in/[[...sign-in]]/page.tsx, sign-up/[[...sign-up]]/page.tsxNone
P2Public share-link landing /join/[code] (anonymous, no Clerk yet) — branded clinic name + tier; CTA → Clerk sign-up; stores code in httpOnly cookieGET /v1/public/share-links/{code}nonepartial (resolveShareLink in OpenAPI)resolveShareLinkBuiltjoin/[code]/page.tsxNone
P3Onboard /onboard (two-step) — Step 1: profile + platform consents (POST /v1/me/patient-profile); Step 2: per-clinic membership + org consents (POST /v1/portal/onboard); patient-invite banner if pendingPOST /v1/me/patient-profile; POST /v1/portal/onboard; GET /v1/me; GET /v1/me/pending-invitations; GET /v1/consent-purposesnone (auth_only)partial (portalOnboard, setupMyPatientProfile)setupPatientProfile, onboardPatient, listMyPendingInvitations, listConsentPurposes, getMeBuiltonboard/page.tsx (multi-step form)OpenAPI for pending-invitations + consent-purposes
P4Re-consent modal (blocking) — required-consents discovery + acceptGET /v1/me/required-consents; POST /v1/me/consentsnone (auth_only)listMyRequiredConsents, grantMyConsentBuilt — wired in (patient)/layout.tsxOpenAPI
P5Sign-out + locale selector + themenone — Clerk + i18nnonen/an/aBuiltNone

1D.3.B — Account self-service

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
P6My profile (portable patient profile — DOB, blood type, allergies, etc.; recipient-timezone IANA picker per 1A.18's 1D.3 close)GET /v1/me/patient-profile; PATCH /v1/me/patient-profileRequirePrincipalRLS✅ existing wrappers (need verification)Sidebar nav stub /profile; no pageOpenAPI; recipient-timezone picker is the close-out of 1A.18 per foundation.md:199
P7My consents (per-org + platform history; grant + withdraw self-toggleable purposes; "delete account to revoke" copy for non-withdrawable)GET /v1/consent-purposes; GET /v1/me/consents; POST /v1/me/consents; POST /v1/me/consents/{consentId}/withdrawnone (auth_only)✅ all fourBuilt(patient)/consents/page.tsxOpenAPI
P8My subscription — view active tier, included features, limits, period datesGET /v1/me/patient-subscriptionnone (auth_only) + RequireCurrentConsentsSidebar nav stub /subscription; no pageOpenAPI gap + api-client gap
P9My documents (foundation-scope: legal docs signed at signup; clinical docs are F-tier)GET /v1/organizations/{id}/legal-documents (read-only echo) — or per-clinic privacy notice viewerorganizations.manage_privacy_notice (or RLS on consent_purpose_versions)listOrganizationLegalDocumentsSidebar nav stub /documents; no pageOpenAPI; clarify scope
P10Privacy — view clinic + platform privacy notices signed at signupderived from P7 + P9nonen/an/aSidebar nav stub /privacy; no pageUI work only
P11Access history (1B.13 — backend shipped) — per-clinic list of staff impersonation sessions on me; who, when, reason, durationGET /v1/me/patient-impersonation-sessions[?organization_id=&only_active=&limit=&offset=]RequirePrincipalRLSNo nav entryOpenAPI + api-client
P12Pending invitations banner (on /onboard) — show "you've been invited to Acme Clinic" when a pending patient invite exists for (principalID, currentOrgID)GET /v1/me/pending-invitationsnone (auth_only)listMyPendingInvitationsBuilt in /onboardOpenAPI

1D.3.C — Clinical (post-foundation; out of 1D scope)

Sidebar entries /appointments, /exercises, /treatment-plan, /forms are F-tier consumer surfaces. They remain stubs at 1D close.


1D.5 — Cross-Org Account Surface (new app)

Hostname: account.restartix.pro (final name TBD per foundation.md:1326). Auth: Clerk. No org-resolver, no X-Organization-ID header — runs on the platform-portable session (set_app_principal with no org), so existing RLS policies (consents_select_self, patients_select_self, patient_subscriptions_select_self) return cross-org unions via the current_app_org_id() IS NULL branch.

No app exists todayapps/account/ is not yet scaffolded. Per foundation.md:1323, this surface must ship before staging cuts over (1E.3 dependency).

#SurfaceBackend endpointsPermissionOpenAPIapi-clientExisting UIGap
A1Hostname provisioning — DNS, ACM cert, Route53 entry; same shape as console.restartix.pron/a — infran/an/an/aNoneInfra work; close in 1E
A2App scaffold — new apps/account/ Next.js app or extend an existing one with a no-org-resolver layoutn/a — code scaffoldn/an/an/aNoneScaffold work
A3My profile (cross-org portable, read + edit)GET /v1/me/patient-profile; PATCH /v1/me/patient-profileRequirePrincipalRLSNoneSame as P6
A4My clinics (list patient_org_ids with each clinic's name, primary contact, DPO email for DSAR routing)GET /v1/me/clinics (new — ships in 1D OpenAPI catch-up pass per D-8) — returns {org_id, name, slug, primary_contact, dpo_email, ...} per clinic the patient is at; field-filtered subset of organization_billing (no other billing data leaks)RequirePrincipalRLS (no org-context required)NoneResolved — endpoint to ship in OpenAPI catch-up; A4 unblocked. Required by GDPR DSAR-routing hard rule
A5My consents (cross-org view; per-clinic toggles preserved; withdraw at clinic A leaves clinic B intact)GET /v1/me/consents; POST /v1/me/consents; POST /v1/me/consents/{consentId}/withdraw (cross-org via no org-id header)RequirePrincipalRLSNoneOpenAPI; verify cross-org behavior of listMyConsents when no X-Organization-ID
A6Cross-org data export request (GDPR Art. 15 / 20)none yet — F11 implementationNoneOut of 1D scope — F11 backend not built
A7Account deletion request (full GDPR erasure)none yet — F11 implementationNoneOut of 1D scope — F11 backend not built
A8Access history (cross-org impersonation sessions)GET /v1/me/patient-impersonation-sessions (no org filter)RequirePrincipalRLSNoneSame as P11
A9Locale + theme + sign-outn/an/an/an/aNoneUI work

Acceptance test required at 1D.5 close (foundation.md:1336): a patient enrolled at two clinics signs in to account.restartix.pro, sees both clinics, withdraws marketing at clinic A, verifies clinic B still granted, triggers cross-org export, signs out. Add to 1E.2's setup-a-clinic acceptance before staging cutover.


Gaps to flag

G-1 OpenAPI coverage

OpenAPI is the contract every UI engineer (and every external Cat F integrator) consumes. Current state: apps/docs/openapi.yaml covers about half the routes mounted in routes.go. Domains entirely missing from OpenAPI:

DomainEndpointsSurface impact
Locations5L5
Patient impersonation4C11 (modal), L15, L16, P11, A8
Outbound webhooks8L18
Connected accounts (Cat B)7L19
Integration services catalog1L19
Platform service providers5C26
AI models5C27
Break-glass4C29, C11, L22
Audit logs1 (with rich filters)C13, C28, L21
Invitations (staff + patient + share-link admin)8L8, L9, L10
Consents (mine + platform + per-patient + purposes catalog)9C32, L13, P3, P4, P7
Legal documents (per-org + admin templates)9C31, L20
Patient profile (/v1/me/patient-profile)2P6, A3
Patient subscriptions (mine)1 (/v1/me/patient-subscription)P8
/v1/me/required-consents, /v1/me/consents mutations, /v1/me/pending-invitations, /v1/me/patient-impersonation-sessions7 self-context opsP3, P4, P7, P11, P12

Every "❌ OpenAPI" cell in the per-app tables traces to one of these. OpenAPI sync is the single highest-leverage unblock — it removes ambiguity for ~71 operations at once.

G-2 api-client coverage

packages/api-client/src/client.ts is hand-written, not generated. Current state: ~38 wrappers for ~105 backend endpoints (~36% coverage). The wrappers that exist are mostly the ones consumed by the already-built Console pages (audit, legal-documents, organizations, members, consent self-toggles).

Domains with zero api-client wrappers (and therefore every consuming UI surface needs the wrapper before any UI work can start):

  • Patients (4 ops)
  • Patient subscriptions (3 ops)
  • Patient tiers (4 ops)
  • Locations (5 ops)
  • Webhooks (8 ops)
  • Integrations (7 ops)
  • Platform service providers (5 ops)
  • AI models (5 ops)
  • Break-glass (4 ops)
  • Patient impersonation (4 ops)
  • Invitations (5 ops, both staff + patient)
  • Org subscriptions / overrides (8 ops)
  • Org billing + entitlements (4 ops)
  • Tiers / entitlements / limit-definitions catalog (4 ops)

Naming-convention drift — even covered endpoints have inconsistent ops IDs between OpenAPI (getCurrentUser, addOrganizationDomain) and client (getMe, addDomain). Resolved per D-9: the OpenAPI spec is renamed to client-style names (concise, already in use across all built UI) during the catch-up pass. Convention is documented in apps/docs/reference/api-conventions.md.

G-3 Missing endpoints

Backend gaps surfaced by this audit (UI needs them, they don't exist yet). Items resolved by the 2026-05-07 decisions are noted inline; remaining items need explicit human review before any UI consumer can ship — none of these are silently added in the OpenAPI catch-up pass.

  1. List endpoint for permission catalog (C5)RESOLVED in 1D.0: GET /v1/admin/permissions shipped (superadmin-only, returns code/resource/action/description/created_at sorted by code with COLLATE "C"). "Migration introduced" column dropped from the original surface — not on the table. OpenAPI entry bundles with the C5 UI consumer.
  2. System role templates list endpoint (C6 read-only viewer)RESOLVED in 1D.0: GET /v1/admin/role-templates shipped (superadmin-only, returns each system template with its permissions array resolved server-side).
  3. System role template propagation editor (C6 editor) — deferred post-1D per D-2.
  4. Platform memberships management (C4)RESOLVED in 1D.0: POST/GET/DELETE /v1/admin/platform-memberships[/{principalId}] shipped (superadmin-only). Role enum validated against {superadmin, support_engineer}. Every grant + revoke audit-logged with action_context = 'platform_membership_change'.
  5. Cross-org subscriptions aggregator (C16, C17)RESOLVED in 1D.0: GET /v1/admin/subscriptions[?org_id=&status=&tier_id=&page=&limit=] shipped (superadmin-only). Each row enriched with organization_name, organization_slug, active_overrides_count. Paginated via the standard apiquery envelope. Per-org override mutations remain on existing per-org routes.
  6. Per-org aggregate stats (C8 — Clinic overview) — DEFERRED per 1D.0 decision: storage tracker doesn't exist; MRR shape unsettled; no production-blocking gap. Cards ship mock at 1D close; the endpoint lands later in 1E observability or production-launch-readiness. System-health page (C30) deferred to 1E per D-11.
  7. Custom roles editor (L7) — same as item 3 but org-scoped; no roles mutation surface in any handler. Status: still a backend gap.
  8. Notification templates editor (C21)resolved per D-3: stay migration-managed; no admin endpoints, sidebar stub removed.
  9. DPO contact for cross-org "my clinics" (A4) — RESOLVED per D-8: ship GET /v1/me/clinics returning {org_id, name, slug, primary_contact, dpo_email, ...} per clinic; field-filtered subset of organization_billing (no other billing data leaks). Lands in the OpenAPI catch-up pass.
  10. GDPR Art. 15 / 20 export + Art. 17 deletion (A6, A7) — F11 scope, not foundation. Listed as Out of 1D scope.

G-4 UI primitive gaps

Already covered in § 1D.4. Restated for visibility:

PrimitiveUsed by surfacesStatus
<RequirePermission /> helperevery gated control across all 3 apps (~50+ uses)Not built
Toast / notification systemevery mutation feedback (publish, save, delete, copy, etc.)Not built
Server-validation rendering (422 → form field errors)every form (~25 across apps)Not built
Empty / loading / error states standardisedevery list / detail pageNot built
Persistent banner pattern (for break-glass, re-consent prompts)C11, L22, P4Built (re-consent only) — generalise the pattern
QR-code component (for share-link mint UI)L10Not built
Block-based editor (for legal-document placeholders / sections)L20 (built via custom form), C31 / C32Built (form-based) — no general block editor

System-only — no UI needed (record so future engineers don't re-ask)

Backend functionality that exists for cron / internal / observability purposes; explicitly out of UI scope:

  • cmd/audit-partition-roll (1A.1, P41) — monthly partition pre-creation cron. System-only.
  • cmd/check-capabilities (1C.1) — CI guard verifying every external call goes through a Capability. System-only.
  • cmd/check-providers (1C.2) — provider healthcheck cron. System-only.
  • cmd/check-cata-resolution (1C.2) — CI guard enforcing platform-providers resolution. System-only.
  • cmd/check-events-registry (1C.3) — CI guard for events registry. System-only.
  • cmd/dump-events-registry (1C.3) — generates apps/docs/architecture/_generated/events-catalog.md. System-only.
  • cmd/check-inbound-webhooks (1C.6) — AST-based CI guard. System-only.
  • cmd/check-classification — classification registry CI guard (1A.13). System-only.
  • cmd/check-soup — SOUP inventory CI guard. System-only.
  • internal/core/inboundwebhooks/dedup/ (1C.6 — Cat D framework) — monthly-partitioned dedup table; partition roll + WasProcessed / MarkProcessed repo helpers. System-only at framework layer; per-provider handlers (Stripe, Daily.co, Clerk) ship with their consumers and may add provider-specific surfaces, none of which are foundation.
  • internal/core/events/registry.go + events.Bus (1A.9, 1C.3 — Cat E) — internal event bus. System-only; no UI for "fire event" or "list events."
  • internal/core/notify/ dispatcher + EmailChannel + FakeChannel + outbox cron — internal email dispatcher. System-only at the dispatcher layer; the templates seeded for MemberInvite and BreakGlassOpened may surface in C21 if templates become editable.
  • internal/core/webhooks/ outbox dispatcher (1C.4) — event-bus subscriber that fans events to clinic-configured webhook subscriptions. System-only at the dispatcher layer; subscription management UI is L18.
  • internal/core/metering/ + crons (1C.7) — metering counters + quota enforcement. System-only at counter layer; usage display surfaces in patient subscription (P8) and clinic billing (L4).
  • internal/core/cache/ Redis cache-aside (P45) + unstable_cache (P42) — caching layers. System-only.
  • set_app_principal_id / current_app_principal_id() / current_app_has_permission() RLS GUCs and helpers — RLS plumbing. System-only.
  • set_app_impersonation_session_id (1B.13) — RLS impersonation context GUC. System-only.
  • Seeded migration data: every system role template, every seeded permission, every consent purpose catalog row, every entitlement / limit definition, every notification template, every classification entry, every events registry entry. Mutated only by future migrations. None of these need a CRUD UI; some get read-only viewers (C5 permission catalog, C18 tiers / entitlements / limits catalog, the consent purposes side-panel on L13/P7).
  • telemetry package — pseudonymised forwarding to observability backend. System-only.
  • cmd/check-soup + apps/docs/reference/soup.md — SOUP inventory tracker. System-only.

Out of 1D scope

UI surfaces whose backend isn't built yet, or whose backend is explicitly deferred to a feature consumer:

Backends not built (F-tier features)

These appear as stub nav entries today and remain stubs at 1D close:

  • Appointments (Clinic calendar, public booking, patient view, video calls) — F2, F3, F5
  • Forms (template designer, filler, signature capture) — F-tier
  • Treatment plans (templates, assignment, library browse, session execution) — F-tier
  • Exercise library (editor, instructions, contraindications, video upload) — F-tier
  • Specialists (list, license tracking, schedule editor, weekly hours, overrides) — F-tier
  • Services / Offerings (catalog editor, service-specialist matrix) — F-tier; subject to deferred servicesofferings rename
  • Specialties (list, editor) — F-tier
  • Segments (cohort builder, rules from forms / profiles / appointments) — F-tier
  • Custom fields (definitions, versioning, profile sync) — F-tier
  • PDF templates (block-based editor, components library) — F-tier
  • Automations (rule builder, execution log) — F-tier
  • Reports — F-tier
  • Billing / invoicing (clinic-side billing dashboard, F12) — F12
  • Patient marketplace payments (Stripe Connect, KYB / KYC, PSD2) — strategic feature, not foundation
  • GDPR Art. 15 / 17 / 20 export & deletion flows — F11 (A6, A7 in 1D.5 inventory)

Backends deferred to first consumer

  • Cat B Connected Account first catalog row + Connector impl + OAuth callback handler — deferred per foundation.md:1C.5. The framework ships at 1C.5; the first F-tier consumer (e.g. Google Calendar for F2 appointments) brings the first catalog row. L19 may be stub-only at 1D close.
  • Cat D Inbound Webhook per-provider handlers (Stripe, Daily.co, Clerk webhooks) — deferred per foundation.md:1C.6. Framework ships; per-provider handlers ship with their F-tier consumers.
  • AI capability provider impls (Anthropic, OpenAI text generation, vision, embeddings) — 1C.8 ships seams with no provider impls. C27 (AI models registry) UI is in scope; actually-using-an-AI-model is post-foundation.

Decisions (resolved 2026-05-07)

Settled the day after the audit landed. Each entry locks the outcome that the per-app plans now consume.

D-1 Console cross-tenant nav stubs /webhooks, /connectors, /patient-tiersREMOVE

Per CLAUDE.md → Foundation Discipline, cross-tenant aggregators built before a stated platform-ops use-case are speculation. Webhooks (Cat C) and Connectors (Cat B) stay clinic-side; patient tiers stay clinic-side. The three Console sidebar entries are removed at 1D.1 — no aggregator endpoints, no Console pages. If a real platform-ops need surfaces later, it lands as an explicit ADR-worthy request.

Affects rows: C19 (removed), C24 (removed), C25 (removed).

D-2 System role template editor — READ-ONLY VIEWER ONLY at 1D close

C6 ships as a read-only viewer of system role templates (mirroring C5's permission-catalog viewer). The full editor — which requires designing cross-tenant propagation semantics (grants propagate to all org clones; revocations do not) — is deferred post-1D. C5 is unchanged. Custom-roles-per-org (L7) stays as a flagged backend gap.

Affects rows: C6 (scope reduced to read-only).

D-3 Notification templates — MIGRATION-MANAGED, remove /notifications stub

Foundation templates stay seeded by migration. F-tier features that introduce new notification categories add per-template editing if their UX requires it. The Console /notifications sidebar stub is removed at 1D.1.

Affects rows: C21 (removed); section "System-only" already records the dispatcher / outbox as system-only.

D-4 1D.4 primitive ordering — SHIP packages/ui PR FIRST

The four un-built primitives (<RequirePermission />, toast, server-validation renderer, standardised empty/loading/error states) ship as ONE packages/ui PR before any per-app 1D.1/1D.2/1D.3/1D.5 surface starts. Every per-app consumer composes against the same versioned primitives. This is stricter than the audit's parallel-prototype recommendation; the rationale is that prototypes hardened on one app's first surface diverge from the version a second app starts against.

Affects: 1D.4 sequencing line in foundation.md.

D-5 1C.2 platform-service-providers + 1C.8 ai-models Console pages — IN 1D.1 SCOPE

Both shipped Console superadmin admin endpoints in 1C; neither had a 1D.1 sidebar entry in the original foundation.md:1219–1254 scope. They're load-bearing for dedicated-tier rollout (Cat A per-org overrides) and AI cost configuration; without UI, superadmin can't drive these flows. Add sidebar entries + CRUD pages to 1D.1.

Affects rows: C26 (confirmed in scope), C27 (confirmed in scope). foundation.md 1D.1 list needs an explicit append.

D-6 "Feature flags" — NOT A UI SURFACE

Per CLAUDE.md taxonomy, organization_settings.feature_flags (staged-rollout JSONB) is engineering-internal — distinct from the 1C.9 entitlements rename (which is the user-facing per-tenant capability toggle). It does NOT surface in the Console (C33 removed) AND it does NOT surface in the Org Settings UI on either Console or Clinic (C14 / L2 lose the field from their UI scope; the PATCH .../settings endpoint may still accept it for engineering use, but the form does not expose it).

Affects rows: C33 (removed); C14 (drop feature_flags from form); L2 (drop feature_flags from form).

D-7 "Locales" — REMOVE

i18n config (next-intl messages/en.json, messages/ro.json) is system-only; no locales DB table exists. The Console /locales sidebar stub is removed at 1D.1.

Affects rows: C34 (removed).

D-8 Cross-org "My clinics" DPO contact — SHIP GET /v1/me/clinics

Required by GDPR ("DSAR routing flows through the clinic, never the platform" — CLAUDE.md hard rule). A generic "contact your clinic" CTA defeats the rule. 1D ships a small endpoint that, with no X-Organization-ID header, returns the patient's clinics with {org_id, name, slug, primary_contact, dpo_email, ...} for each patient_org_id. DPO/contact fields are sourced from organization_billing but exposed read-only and field-filtered on this endpoint (no other billing data leaks). Land in the OpenAPI catch-up pass; A4 then consumes it.

Affects rows: A4 (resolved — endpoint to ship); G-3 entry #8 (resolved). New endpoint added to routes.go + handler + OpenAPI + api-client.

D-9 OpenAPI / api-client naming alignment — CLIENT-STYLE NAMES; pass during OpenAPI catch-up

The built UI consumes the concise client-style names (getMe, addDomain, onboardPatient). Renaming the spec is the smaller, less-invasive direction (no consumer code changes vs. ~38 client wrappers + every app call site). The OpenAPI catch-up agent renames getCurrentUser → getMe, addOrganizationDomain → addDomain, etc. as part of the same pass; the convention is added to apps/docs/reference/api-conventions.md.

D-10 servicesofferingsDEFER

Glossary locks the rename target. The actual file/code rename waits until clinical services (F-tier) is built; no 1D action. Console /services sidebar stub stays as an F-tier nav stub today and is removed per D-12 at 1D close.

D-11 System health (C30) — 1E, NOT 1D

The page consumes staging deployment artifacts (KMS, S3, RDS, SES). Those exist after 1E.3. Page stays mock at 1D close; real wiring lands in 1E observability scope.

Affects rows: C30 (moved to 1E section).

D-12 F-tier sidebar stubs — REMOVE, no "coming soon" placeholder

Empty entries that point at unbuilt features are speculation noise. No 404, no "coming soon" placeholder. The foundation sidebar contains foundation surfaces only; F-tier surfaces appear in the sidebar when their features ship.

Affects: every "Sidebar nav stub" row whose backend is F-tier (Clinic: 14, Console: 9, Portal: 8 = 31 entries — see Appendix). Each app's app-sidebar.tsx is pruned at 1D close.


Appendix — surface counts by app

Pre-decision (2026-05-07 audit):

AppTotal surfaces in 1D scopeBuiltPartial / mockNot started
1D.1 Console365 (C7, C10, C28, C31, C32)6 (C8, C9, C11, C12, C13, C14, C30 mock-only)25
1D.2 Clinic admin221 (L20 legal docs)021
1D.3 Patient Portal126 (P1, P2, P3, P4, P5, P7)1 (P12 banner only)5
1D.5 Cross-org Account9 (incl. infra)009
Total7912760

Post-decision (after D-1 / D-3 / D-6 / D-7 remove C19/C21/C24/C25/C33/C34, D-11 moves C30 to 1E):

AppTotal surfaces in 1D scopeBuiltPartial / mockNot started
1D.1 Console29 (–7)5 (unchanged)5 (C30 moved out)19 (–6 removed)
1D.2 Clinic admin22 (unchanged)1021
1D.3 Patient Portal12 (unchanged)615
1D.5 Cross-org Account9 (unchanged)009
Total7212654

Stub-only nav entries that are F-tier (out of 1D scope) — Clinic: 14, Console: 9, Portal: 8 = 31. Per D-12, all 31 are removed from the relevant app-sidebar.tsx at 1D close (no "coming soon" placeholder); they reappear in the sidebar when their backends ship.

If counted with the F-tier stubs: 121 distinct UI surfaces touched by this audit.

Appendix — domain handler → surface map (single-grep reverse-lookup)

HandlerRoutes countTouched surfaces
aimodels5C27
audit1C13, C28, L21
breakglass4C11, C29, L22
consents10C32, L13, P3, P4, P7
human3C1, C7 (login), C35, P5
impersonation5C11, L15, L16, P11, A8
integrations7L19
invites8L8, L9, L10, P3, P12
legaldocument9C31, L20, P9, P10
location5L5
organization13C7, C8, C9, C10, C14, L1, L3, L6
orgbilling2C12, L4
orgentitlements2C12
orgsettings2C14, L2, C33
patientprofiles2P6, A3
patients4C11, L11, L12
patientsubscriptions4C12, L14, P8
patienttiers4L17, C19
plancatalog4C18
platformproviders5C26
portalonboarding2P3
sharelinks5L10, P2
subscriptions8C12, C16, C17
webhooks8L18