Single source of truth for what is and isn't built. Last reviewed: 2026-06-24 (ops cost/leverage analytics refresh: SA-E4 Expert leverage shipped via #3199; SA-B5 per-org cost now partial — LLM cost + Expert workload in PRs #3195/#3200, dollarized margin pending #3196). Prior audit pass 2026-06-04: refreshed §15/§16 for the agent-runtime decommission per ADR-028 / #1584; aligned dogfood Specialist name with scripts/seed-dev.sh (Eleanora Voss, not Bob); added "Past Week" stanza below. Previous review 2026-05-25.).
Cross-referenced against: api/src/ controllers + entities, frontend/src/ components + pages.
Past Week — Shipped 2026-05-27 → 2026-06-04
Major merges since the 2026-05-25 review that are NOT yet expanded into the per-section tables below. Each is filed against the section it touches so future re-syncs can promote the row in-place.
| PR | Section | What |
|---|
| #1604 | §15 / Learning loop | New correction_embeddings table — Phase 1 of #1360 learning-loop closure. Uses OpenAI directly for embedding generation, not OpenRouter — flagged as a possible exception to ADR-026; pending Eusden confirmation whether deliberate or drift. |
| #1602 / #1613 / #1199 | §5 Onboarding briefs | environment_profiles → client_briefs rename fully shipped (all merged 2026-06-04 under #632). DB-table rename + client_brief_revisions audit table via #1602 (migration 1785500000001-RenameEnvironmentProfilesToClientBriefs.ts, closes #1172); API module / entity / controller / route + permission guards via #1613 (closes #1173) — module is now api/src/client-briefs/, entity ClientBrief, route /client-briefs/*, old /environment-profiles/* paths kept as 308 redirects (slated for removal ~30 days after the FE path swap); frontend copy + API client via #1199 (closes #1174). |
| #1599 | §15 / Resilience | OpenRouter CircuitBreaker moved to Redis (last in-process CB caller removed) so the multi-pod active-active deployment converges on a single shared circuit state. |
| #1584 / #1450 | §15, §16 | Decommission RailwayPerOrgProvisioner — only shared_railway and ecs_per_org agent runtimes remain. Implements ADR-028. Supersedes the 2026-05-22 "soak ongoing" claim from #589. |
| #1559 | §17 / CI | dev → staging → main promotion flow is now CI-enforced — guard workflow is a required status check that rejects PRs to main not originating from staging (and to staging not from dev). Companion doc: docs/promotion-flow.md. |
| #1537 | §1 / domains | Domain canonicalization: h852.work = dev, h853.work = staging, h.work = prod. Sweep any older docs using stale subdomain names. |
| #1583 / #1506 / #1493 | §1 / auth | Cross-org IDOR fix on conversations + fail-closed auth + restore @Public() on routes broken by the global guard (webhook ingress, bootstrap). Public-auth surface audit landed simultaneously. |
| #1502 / #1598 | §15 / tool gateway | HERMES_TOOLSETS is now wired from the toolsManifest + tool-gateway client — Specialist-scoped tool surfaces no longer rely on env-var allowlists. |
| #1432 | §6 / channels | Gmail Pub/Sub webhook for inbound email (closes #686). |
| #1526 + #1528 | §13 / KB | KB search endpoint for Expert workspace (backend) + KB search panel UI. |
| #1551 / #1433 | §10 / billing | Grace-period gating + /ops/billing surfacing for payment-failure states. |
| #1475 | §10 / billing | In-app notifications on payment failure. |
| #1508 | §11 / SLA | Per-org SLA config + Slack breach alerting. |
| #1530 / #1533 | §8 / Expert workspace | Customer history timeline (backend + FE). |
| #1549 / #1474 | §3 / client portal | Dedicated client notifications drawer. |
| #1083 | §14 / Tavus | Tavus expert-application interview integration fixes. |
| #1442 | §6 / channels | Email cutover rollback runbook. |
| #1498 | §6 / Slack | Slack OAuth install runbook. |
| #1577 | §17 / infra | terraform-apply is now workflow_dispatch-only — no infra change applies on push. |
| #1510 | §1 / DEMO_MODE | Audit + CI guard preventing DEMO_MODE=true from leaking into a production deploy. |
| #1567 / #1570 | §15 / /agentic/tasks | Proper 401/403 + role-aware scoping for /agentic/tasks/* endpoints. |
| #1522 | §15 / RuntimeToolExecutor | Added order_lookup tool to the RuntimeToolExecutor surface. |
#1604 / ADR-026 caveat: The correction_embeddings table generates embeddings via the OpenAI SDK directly, not OpenRouter. ADR-026 says "embed, chat, rerank" should route through OpenRouter. Until Eusden confirms this is a deliberate per-pipeline exception (vs. a drift), do not promote ADR-026 to "live for all embedding paths".
1. Authentication & Roles
| Feature | Status | Notes |
|---|
| Email + password login | ✅ Complete | POST /auth/login → JWT; auth.service.ts |
| Email OTP (passwordless) | ✅ Complete | POST /auth/request-otp + POST /auth/verify-otp |
| Onboarding OTP (invite flow) | ✅ Complete | POST /auth/send-invite-otp + POST /auth/verify-invite-otp; single-use OTP per invite token |
| Demo login (DEMO_MODE) | ✅ Complete | POST /auth/demo-login; supports superadmin, account_manager, expert, client; non-prod only. Maps to canonical seed users: amy@acmefinancial.com (client), expert1@humanwork.dev (expert), am1@humanwork.dev (AM), e@humanity.org (superadmin). CORS auto-allows Vercel dev/staging when DEMO_MODE=true. |
| JWT generation + validation | ✅ Complete | RS256 JWT with sub, email, name, platformRole, orgMemberships[]; 7-day expiry |
| 2FA (TOTP) | ✅ Complete | TOTP via otplib; POST /auth/2fa/setup + POST /auth/2fa/verify; qrcode generation |
| Role-based routing (frontend) | ✅ Complete | getRoleRedirect() in AuthContext; superadmin → /ops/clients, expert → /workspace/queue, account_manager → /ops/clients, client → /client/chat. Legacy /am/* and /superadmin/* redirect to matching /ops/* routes. |
| Platform roles | ✅ Complete | superadmin, account_manager, expert, ai_operator, org_admin, org_member, client_admin, client_member |
| Org memberships (owner/admin/member/billing) | ✅ Complete | OrgMembership entity; encoded in JWT |
| Row-level security (RLS) | ✅ Complete | PostgreSQL RLS policies in 6 migrations (009, 013, 017, 027, 033, + WhatsApp whitelist); two-tier internal/client domain policy; SQLite test environments skip via guard |
2. Client Onboarding
2026-05-10 rebuild (PRs #267–271): Client onboarding rebuilt as a 3-step SPA at single URL /onboarding/start?token=… — no page reloads between steps. Channel Setup, Teammates, and Billing steps removed from the wizard (Channels and Billing are still reachable post-onboard from /client/settings/*).
| Feature | Status | Notes |
|---|
| Phase 0 — validation gate (AM-side) | ✅ Complete | GET /am/orgs/:orgId/phase0-status; hard-blocks until company info, specialist, AM profile, trial date all pass |
| Phase 1.1 — company info (AM creates org) | ✅ Complete | Name, slug, industry, domains, email, website, country, timezone; real-time slug validation |
| Phase 1.2 — assign specialists | ✅ Complete | Multi-specialist assignment; confidence threshold per specialist; primary designation; OrgSpecialistAssignment entity |
| Phase 1.3 — AM profile | ✅ Complete | Global AM profile (shared across orgs) |
| Phase 1.4 — send client invite | ✅ Complete | ClientInvite token (7-day expiry); resend extends window; stale org auto-archives after 90 days |
| Step 1.4 wizard — send button | ✅ Complete | PR #243: was permanently disabled due to trialEndDateSet pre-check; fixed with client-side override so button enables when other gates pass |
| Step 1.4 wizard — Copy Onboarding Link | ✅ Complete | PR #243: "Copy Onboarding Link" button now available before invitation is sent |
| Step 1.4 wizard — Complete Setup CTA | ✅ Complete | PR #243: "Complete Setup" CTA added for orgs already in pending_client_confirmation status |
| Client onboarding SPA — 3 steps | ✅ Complete | PRs #267–271 (2026-05-10): Workspace Setup → Your h.work Team → Done; single URL /onboarding/start?token=…; no page reloads between steps |
| Step 1: Workspace Setup | ✅ Complete | Email field as disabled input (not plain text); inline [input].h852.work slug picker; flat sorted timezone list with (GMT +X:00) City format; submit button surfaces specific validation errors on click |
| Step 2: Your h.work Team | ✅ Complete | Read-only specialist preview + AM email + AM name surfaced; Specialist monthly rate displayed with override/catalog source caption (PR #518 / #498 closed) |
| Step 3: Done | ✅ Complete | "Continue to your Workspace" → /client/chat (auto-authenticated); welcome email sent |
| Onboarding progress bar | ✅ Complete | Full-bleed, equal-width segments, correct active/inactive colors |
| Idempotent re-visits | ✅ Complete | Invitation accepted status still allows org/specialist/slug/profile fetches; validateInvitationToken no longer 410s when org is active |
| Phase 2.1 — OTP + password + profile | ✅ Complete | Email OTP verification; password set; name/title/phone fields (entry point preserved at /accept-invite) |
| Phase 2.2 — confirm company info + slug | ✅ Complete | Slug reserved atomically; immutable after confirmation; slug alias system for rebrands |
| Phase 2.3 — meet your specialist | ✅ Complete | Read-only specialist preview; team composition hidden from client |
| Phase 2.4 — channels setup (in onboarding) | ❌ Removed | Channels setup moved out of onboarding to post-onboard /client/settings/channels. Email + corporate-domain whitelist seeded automatically at org creation. |
| Phase 2.5 — invite teammates / billing (in onboarding) | ❌ Removed | Both moved out of onboarding to /client/settings/team and /client/settings/billing post-onboard. |
| Phase 2.6 — completion | ✅ Complete | Status → active; trial starts; welcome emails sent (no longer references "Invite your team" or "Set up billing"); invite token invalidated |
| Slug validation rules | ✅ Complete | Format ^[a-z0-9][a-z0-9-]{1,61}[a-z0-9]$; reserved words blocked; personal domains blocked; never recycled |
| Stale org auto-archive | ✅ Complete | StaleOrgService; 90-day window |
| Business context import (onboarding wizard) | ❌ Not built | USER_STORIES C-O6 (P1 gap) — no upload or config UI for initial knowledge base |
2a. Client Settings & Team Management
Routes: /client/settings/*. Old /portal/admin/* and /client/admin/* routes redirect here.
| Feature | Status | Notes |
|---|
Client settings route (/client/settings) | ✅ Complete | Redirects to /client/settings/profile; replaces /portal/admin/* |
Profile settings (/client/settings/profile) | ✅ Complete | Name, avatar, phone, title, bio; all roles |
Team view (/client/settings/team) | ✅ Complete | All roles can view member list; GET /admin/members |
| Self-service team invites | ✅ Complete | POST /admin/members/invite; accessible to org_admin (admin/owner orgRole) |
| Remove team member | ✅ Complete | DELETE /orgs/:orgId/members/:userId; Admin/Owner only via OrgRolesGuard("admin") |
| Role management (change member roles) | ✅ Complete | PATCH /admin/members/:id/role; Admin/Owner can change roles up to Admin level |
| Workspace roles (Owner/Admin/Billing/Member) | ✅ Complete | OrgMembership.orgRole; encoded in JWT; 4-tier model fully wired in UI and API |
| Transfer ownership | ✅ Complete | POST /orgs/:orgId/transfer-ownership; Owner-only; guard enforced server-side |
| Leave workspace (self-service) | ⚠️ Partial | UI stub exists in /client/settings/team; self-removal API pending — issue #148 |
Channel settings (/client/settings/channels) | ✅ Complete | Admin/Owner only; channel connect/manage UI |
Billing settings (/client/settings/billing) | ✅ Complete | Admin, Owner, Billing roles; plan, usage, payment method |
3. Client Portal Chat
| Feature | Status | Notes |
|---|
| Portal chat UI | ✅ Complete | PortalChat.tsx (1,473 lines); conversation list sidebar + message thread |
| Create conversation | ✅ Complete | POST /conversations; agent name auto-assigned from curated name pool |
| Send message | ✅ Complete | POST /conversations/:id/messages; validated at org boundary |
| Receive agent response | ✅ Complete | Real Hermes runtime wired behind /v1/chat (PR #194); SSE endpoint exists; single-event response acceptable for MVP. Token-by-token streaming is P2 (issue #248). Socket.io gateway wired; client-side polling fallback implemented. |
| Conversation history | ✅ Complete | GET /conversations (list) + GET /conversations/:id (thread with paginated messages) |
| Keyword search | ✅ Complete | GET /conversations/search?q=&orgId=; full-text via pg_trgm |
| Channel badge | ✅ Complete | ChannelBadge.tsx; shows web / email / WhatsApp / Slack / Telegram with icon + color |
| Specialist persona display | ✅ Complete | Client sees Specialist name, avatar, and full profile in the /client/chat dashboard right-panel carousel; expert identity never exposed. GET /client/specialists returns assigned Specialist personas. |
| Trial banner | ✅ Complete | TrialBanner shown to client users; GET /client/trial-info |
| Internal notes hidden from client | ✅ Complete | isInternal filter on message list; client role excluded via EXPERT_ROLES check |
| Read-only status badge (client) | ✅ Complete | Client sees status as read-only badge; cannot change status |
| Client-facing approval UI (high-stakes actions) | ❌ Not built (P2 — deferred) | USER_STORIES C-W8. Demoted from P0 → P2 on 2026-05-23: MVP handles approval manually via the conversation channel since most work is done through communication. |
| AI vs Expert handling label | ✅ Complete | Inline badge in client message view: "AI" (blue) for agent messages, "Human review" (purple) for expert-reviewed messages; experts retain their existing "Expert" label |
| Agentic task status tracking | ⚠️ Partial | AgenticTask entity + API exist; client-facing task progress view not confirmed |
4. Expert Workspace
| Feature | Status | Notes |
|---|
| Expert queue list | ✅ Complete | /workspace/queue; ExpertQueueService; sorted by risk; QueueItem component |
| Filter by status | ✅ Complete | Pending / in_progress / resolved filter; listPending() service method |
| Filter by org | ✅ Complete | Org filter in queue service |
| Full conversation thread view | ✅ Complete | TicketDetail.tsx; CustomerThread component; all messages shown |
| AI suggestion + confidence score | ✅ Complete | getSuggestion() in expert-queue.service; ConfidenceBar.tsx renders 0–100 bar with color coding |
| Risk badges | ✅ Complete | Color-coded risk level: low (green), medium (yellow), high (orange), critical (red) |
| Accept draft (approve & send) | ✅ Complete | respond() in expert-queue.service; marks resolved |
| Edit draft before sending | ✅ Complete | Editable textarea in TicketDetail; edit + send action |
| Write from scratch | ✅ Complete | Expert can clear draft and write manually |
| Send as specialist persona | ✅ Complete | Response attributed to specialist; expert identity not exposed to client |
| Resolve ticket | ✅ Complete | resolve() endpoint; status → resolved |
| Internal notes | ✅ Complete | Note toggle in PortalChat (expert-only); is_internal + sender_id on messages; addInternalNote() API call |
| Conversation status dropdown | ✅ Complete | Status <select> (pending/awaiting_client/resolved/snoozed) in PortalChat for expert role; PATCH /conversations/:id/status. No archived state — it was collapsed into resolved (#2002). See conversation-status-lifecycle. |
| Assign to self | ✅ Complete | "Assign to me" button in PortalChat; PATCH /conversations/:id/assign; assigned_expert_id on Conversation |
| Defer ticket | ✅ Complete (backend) | defer() sets deferred_until; frontend defer UI not yet confirmed |
| Reassign queue item | ⚠️ Disabled | reassign() backend exists; pool model removed 2026-05-03; frontend reassign UI disabled pending new direct-assignment model |
| Queue auto-refresh | ✅ Complete | Polling implemented in workspace queue page |
| Agentic task plan review | ✅ Complete | AgenticTask entity; GET /agentic/tasks; approve/reject per step |
| Agentic task step approve | ✅ Complete | POST /agentic/tasks/:id/steps/:stepId/approve |
| Agentic task step reject | ✅ Complete | POST /agentic/tasks/:id/steps/:stepId/reject with feedback |
| Tool execution (agentic) | ⚠️ Partial | Plan/checkpoint/approval loop landed via #696 (closed #172). Tool dispatch flows through api/src/agent-api/tool-registry.ts (PR #399); some tools (Shopify, Amazon) execute live, Nango-backed tools (QuickBooks, Xero) are registered but stubs; Jumio is skeleton-only (#230). |
| Expert profile + assignment UI | ❌ Not built | USER_STORIES E-O2, E-O3 (P1 gap) — skill tags in User entity but no profile UI. Pool-based assignment is gone (ADR-007); assignment now via expert_access. |
| Correction categorization UI | ⚠️ Partial | Correction entity + POST /learning/corrections exist; UI for selecting error type not confirmed |
| Push notifications (critical escalations) | ❌ Not built | USER_STORIES E-P3 (P0 gap) |
| Native mobile app | ❌ Not built | USER_STORIES E-P4 (P1 gap) |
5. Conversation Management
| Feature | Status | Notes |
|---|
| Conversation status | ✅ Complete | PATCH /conversations/:id/status; pending / awaiting_client / resolved / snoozed (no archived — collapsed into resolved, #2002). See conversation-status-lifecycle. |
| Snooze with resurface time | ✅ Complete | snooze_until column on Conversation (migration 034); snoozeUntil in entity and frontend interface |
| Specialist assignment | ✅ Complete | PATCH /conversations/:id/assign; assigned_expert_id on Conversation |
| Internal notes | ✅ Complete | is_internal + sender_id on Message (migration 034); note toggle; filtered server-side in conversations.service.ts — canSeeInternal role check in query builder excludes is_internal=true rows for client roles |
| Full-text search | ✅ Complete | pg_trgm index; GET /conversations/search |
| Multi-tenant scoping | ✅ Complete | All queries org-scoped; RLS enforced at DB layer |
| Audit trail | ✅ Complete | AuditLog entity; AuditService records every significant action |
| SLA deadline tracking | ✅ Complete | sla_deadline on ExpertQueueItem (migration 012-SlaDeadline) |
| SLA performance dashboard (client) | ❌ Not built | USER_STORIES C-V5 (P1 gap) |
6. Channel Adapters
| Channel | Status | Notes |
|---|
| Web / portal | ✅ Complete | Direct portal chat; POST /conversations/:id/messages |
| CORS fix (Vercel preview) | ✅ Complete | PR #244: Vercel preview deployments (dynamic *-humanityprotocol.vercel.app URLs) now allowed in non-production environments via regex pattern |
| Email (Gmail API inbound polling) | ✅ Complete 2026-06-05 | Primary inbound path. Gmail API polling via cron (*/30 * * * * * on Railway dev) shipped via PR #1722 (closes #1060). Uses ONE Google Workspace service account with domain-wide delegation to read each Specialist's mailbox (bob@h.work); no per-Specialist OAuth tokens. |
| Email (Gmail API outbound via DWD) | ✅ Complete 2026-06-05 | Primary outbound path. Replies sent from the Specialist's gsuite mailbox via DWD impersonation (PR #1722). Specialist mailboxes are provisioned by organizations.service.ts enqueuing WORKSPACE_JOB_CREATE on Specialist↔Org assignment; the processor calls Admin SDK users.insert. |
| Email (Cloudflare inbound) | ⚠️ Fallback during 2-week soak | Cloudflare email worker (deployed 2026-05-10) is fallback while we soak the Gmail polling path. Routes @dev.h852.work → dev API and @h852.work → staging API. Worker sends base64 RFC822; API parses with mailparser at POST /channels/email/inbound; HMAC-SHA256 verify via CLOUDFLARE_EMAIL_WEBHOOK_SECRET. Decommission tracked in #1440/#1441 (still open). |
| Email (Resend outbound) | ✅ Transactional only | No longer the Specialist conversation channel. Resend is reserved for invites/onboarding/transactional email via email/email.service.ts. Auto-replies use verified email domain (not dev.* subdomain). Templates redesigned 2026-05-10. |
| WhatsApp (Twilio) | ✅ Complete | HMAC-SHA1 signature verification; number pool (≈20 numbers); whitelist enforcement; POST /channels/whatsapp/inbound. 2026-05-10: heldForReview respected (no auto-reply when expert review pending); media/attachments passed through to sendMessage (media-only inbound gets [Media message] placeholder); reply truncated to 4096 chars before Twilio send; boot guard logs warning when TWILIO_AUTH_TOKEN is missing; deactivated-org check before creating any conversation. |
| Slack | ⚠️ Partial | Adapter implemented (HMAC-SHA256 verify; app_mention + message events); Slack org resolution wired (resolveSlackOrgId maps team_id/enterprise_id/app_id/channel_id → org via stored Slack integration credentials); still blocked on live Slack credentials (BLK-004) |
| Telegram | ✅ Complete | Per-org tokens in integration_credentials; TelegramModule (fix/issue7) |
| Microsoft Teams | ❌ Stub | Bot Framework webhook; JWT validation stub (BLK-032 — needs credentials) |
| WeChat | ❌ Stub | Official Account webhook XML parsing not done (BLK-042, P3) |
| Channel health API | ✅ Complete | GET /orgs/:orgId/channels/status; GET /orgs/:orgId/channels/webhook-urls |
| Integration credentials (encrypted) | ✅ Complete | AES-256-GCM encryption; integration_credentials table; CRUD at /orgs/:orgId/integrations |
| Circuit breaker | ✅ Complete | Implemented for Slack, Email, Teams error handling |
| Outbound org webhooks | 🔮 Future | Signed payloads; per-org subscriptions — in FUTURE_REQUIREMENTS.md |
6a. Third-Party Integrations (Nango + bespoke)
2026-05-22 model: Nango is the integration infrastructure platform — covers the 700+ provider catalog (QuickBooks, Xero, HubSpot, Salesforce, etc.) via per-Org OAuth Connect Sessions. Bespoke services (Jumio, Shopify, Amazon SP-API) remain for vendors not in Nango or where deeper integration is required. Agent tool dispatch routes both via a single static registry. See PR #399.
| Feature | Status | Notes |
|---|
| Self-hosted Nango server (dev) | ✅ Complete | PR #403 / #228. Railway-deployed; /health and /connection both 200 with dev secret. Public URL + API env wiring documented. |
| Self-hosted Nango server (staging/prod) | ❌ Not built | Tracked under #228 follow-on; needs staging + prod Railway projects + secret rotation procedure. |
NangoModule (NestJS) | ✅ Complete | PR #399 / #229. api/src/integrations/nango/{nango.module,nango.service,nango.client}.ts. |
| Nango Connect Session (OAuth handoff) | ✅ Complete | cfd05d76 / #421. createConnectSession issues per-Org session tokens; getConnection / listConnections / deleteConnection wired. buildNangoConnectionId(orgSlug, provider) is the connection-ID convention. |
| Nango connection revoke | ✅ Complete | bf0c78b0. Deleted Nango connections treated as revoked; legacy local credential rows cleaned via e67ea995. |
| Static agent tool registry | ✅ Complete | PR #399. api/src/agent-api/tool-registry.ts; GET /v1/agent-api/tools/:toolName; AgentToolKind = "bespoke" | "nango". |
| SA Nango catalog autocomplete + link-out | ✅ Complete | #423. /ops/clients/:id integrations panel surfaces tool kind badge and links to Nango catalog. |
| QuickBooks (Nango) | ⚠️ Registered, not wired | quickbooks_query tool descriptor in registry with kind: "nango"; backend action handler + agent prompt wiring deferred. Supersedes the stub row in #176. |
| Xero (Nango) | ⚠️ Registered, not wired | xero_query tool descriptor in registry with kind: "nango"; same status as QuickBooks. |
| HKEX (Nango) | ❌ Not built | #176. Not in Nango catalog; needs bespoke or alt provider. |
| Shopify | ✅ Complete (bespoke) | Pre-existing bespoke proxy; remains bespoke per #399 — deeper integration than Nango exposes. |
| Amazon SP-API | ✅ Complete (bespoke) | Pre-existing bespoke proxy; remains bespoke per #399. |
| Jumio (bespoke KYC) | ⚠️ Skeleton only | #230 — assignee Dankovk. api/src/integrations/jumio/{jumio.module,jumio.service,jumio.client}.ts exist (122 LOC); jumio_lookup tool descriptor registered; JumioService.lookup() throws NotFoundException until per-Org IntegrationCredential row exists. Blocker: no Jumio sandbox account / API key on file — procurement-side, not engineering. OQ-202 (per-Org vs HP-shared key) still open. |
| Per-Org integration credentials encryption | ✅ Complete | #551 / PR #570. integration_credentials.encrypted_config now AES-256-GCM at rest via decryptConfig() helper; boot guard refuses prod without CREDENTIALS_ENCRYPTION_KEY. |
| Tool execution audit (per call) | ✅ Complete | PR #399. Audit row emitted on every tool dispatch with model, provider, masked params; Jumio path uses buildJumioAuditPayload (PII-safe). |
| Tool execution metrics (success/failure per tool per org) | ❌ Not built | #176 work item; deferred until P0s clear. |
| Integration health monitoring + credential rotation | ❌ Not built | #176 work item; rotation procedure documented in Jumio PRD (#230). |
7. Specialist Management
| Feature | Status | Notes |
|---|
| AM creates specialist | ✅ Complete | admin-specialists.controller.ts; Specialist entity with name, avatar, routing email, skill tags |
| AM assigns specialist to org | ✅ Complete | OrgSpecialistAssignment entity; confidence_threshold per specialist per org; isPrimary flag |
| Multiple specialists per org | ✅ Complete | Migration 024; multiple assignments supported |
| Specialist change request | ✅ Complete | SpecialistChangeRequest entity (pending/fulfilled/declined) |
| Specialist routing email | ✅ Complete | bob@h.work style dedicated routing address |
| Email outbound via Gmail Send (DWD service-account impersonation) | ✅ Complete | Phase 2 ADR-0002 originally shipped per-OSA oauth_refresh_token_encrypted (#1065 / #1079); inbound dual-read (#1093); outbound dispatch (#1095), all 2026-05-29. 2026-06-05 update (PR #1722): architecture moved to ONE Google Workspace service account with domain-wide delegation impersonating each Specialist's mailbox — no per-Specialist OAuth tokens. Cloudflare worker retained as inbound fallback during 2-week soak (#1440/#1441). |
| Named account lead assignment per org | ❌ Not built | USER_STORIES SA-C9 (P1 gap) — no UI to set named AM per org |
8. Notifications & Real-Time
| Feature | Status | Notes |
|---|
| WebSocket gateway (Socket.io) | ✅ Complete | NotificationsModule; rooms: experts, org:<id>, pool:<id>; events: expert_queue_item_added, queue_item_resolved, agent_message_sent |
| SSE streaming (agent responses) | ✅ Complete | POST /conversations/:id/messages/stream; server-sent events |
| In-app notifications (AppNotification) | ✅ Complete | AppNotification REST + Socket.io delivery rail shipped via #915 (2026-05-27); item 1 of #170's 5-item scope. Push delivery / email delivery / prefs UI still pending under #170. |
| Client-side polling | ✅ Complete | Frontend polls for new messages as fallback |
| Push notifications (mobile / browser) | ❌ Not built | USER_STORIES E-P3 (P0 gap) — pending under #170 follow-ups |
9. Analytics & Reporting
| Feature | Status | Notes |
|---|
| Analytics page (superadmin) | ✅ Complete | AnalyticsModule + controller; live data from DB: queue status, top request types, pool resolution times, auto-resolve rate, active experts. SA-B4 P0 gap resolved. |
| Client dashboard (summary) | ⚠️ Partial | Dashboard page exists; quota data live via GET /orgs/:orgId/quota; conversation counts from live API; volume chart mock |
| Conversation search | ✅ Complete | Full-text via pg_trgm |
| Correction stats | ⚠️ Partial | LearningService stores corrections; no stats aggregation endpoint or UI confirmed |
| Expert leverage ratio | ✅ Complete | SA-E4 — /ops/analytics Expert leverage section + GET /ops/analytics/expert-leverage (SuperAdmin); from expert_access (ADR-007). PR #3199 |
| Per-org cost tracking | ⚠️ Partial | SA-B5 — per-org LLM cost + Expert workload on /ops/billing?org=<id> (PRs #3195/#3200). Dollarized labour + infra cost → gross margin still pending (#3196) |
| Autonomous handling rate | ✅ Complete | auto_responded flag on Conversation; autoResolveRate surfaced in analytics API and superadmin analytics page |
| SLA performance dashboard | ❌ Not built | USER_STORIES C-V5 (P1 gap) |
10. Billing & Subscriptions
2026-05-29 rail flip: Lago — self-hosted Lago (Railway for dev; AWS for staging + prod — AWS bring-up tracked in #1287) — is the canonical billing rail — Orb was abandoned. Invoicing is per-Specialist monthly rate (sum of assigned Specialists' resolved rates × period), not plan-tier subscription. LagoProvider shipped via #1069; dual pricing strategy (plan_per_rate default, plan_overrides premium) via #1105; client invoice surface via #1155; ops billing UI via #1155 (note: #1165 was the originally-cited Phase-8 PR but was closed-unmerged and superseded by #1155); Phase-7 legacy fixed-plan teardown via #1016. Direct Stripe lives only inside Lago's PSP config. See docs/features/billing.md and docs/decisions/2026-05-29-lago-pricing-strategy.md.
| Feature | Status | Notes |
|---|
| Per-Specialist rate schema | ✅ Complete | specialists.monthly_rate_config (catalog default; 364 roles populated in dev) + org_specialist_assignments.monthly_rate_config (AM override with optional rate_effective_from / rate_effective_until window) |
| AM rate override (per-org) | ✅ Complete | PR #518 / #498 closed; override wins when its window is active, falls back to catalog default, then null |
| Onboarding rate display (client side) | ✅ Complete | PR #518: GET /onboarding/:token/specialist returns monthlyRate: { amount, currency, source: 'override' | 'specialist' } | null; rendered via Intl.NumberFormat in Step 2 of 3-step SPA |
| Lago provider (canonical invoice rail) | ✅ Complete | #1069 LagoProvider (2026-05-29); #1105 dual-strategy (LAGO_PRICING_STRATEGY=plan_per_rate default; plan_overrides requires Lago premium license). Self-hosted Lago — Railway for dev; AWS for staging + prod (AWS bring-up tracked in #1287). See docs/superpowers/specs/2026-05-26-billing-provider-leveraged-design.md §Operational note. |
| Lago client invoice surface | ✅ Complete | #1155 wired /client/settings/billing to Lago Manage Billing portal + per-Specialist subscriptions list (2026-05-31). |
| Lago ops billing UI (Phase 8) | ✅ Complete | #1155 (#1165 was closed-unmerged, superseded by #1155): /ops/billing rebuilt on Lago — subscription DataTable, sync-failure alert, Retry sync; nav opened from saOnly → AM + SuperAdmin (2026-05-31). |
| Phase 7 legacy teardown | ✅ Complete | #1016 removed legacy fixed-plan Stripe path from BillingController (2026-05-28). |
| Mid-month proration / assignment-event ledger | ✅ Delegated to Lago | Lago handles calendar-day proration natively on its subscription engine; no separate ledger needed. Earlier #514 obsoleted by rail flip. |
| Direct Stripe integration | 🗑️ Removed | Lago wraps Stripe as its PSP. Legacy STRIPE_SECRET_KEY env retained only because Lago needs it; standalone Stripe code removed via #1016. |
| Request Finance disabled | ✅ Complete | PR #219: ENABLE_REQUEST_FINANCE flag defaults to false; crypto payment path disabled unless explicitly re-enabled; RF code preserved but inactive |
| Message quota tracking | 🔁 Re-scoped — partial cleanup | GET /orgs/:orgId/quota; OrgThrottlerGuard. No longer a billing construct — abuse protection only. Partial cleanup landed via PR #531 2026-05-23 (global APP_GUARD registration + /auth rate-limit decorator removed). Note: throttler was re-registered in shadow mode via #812 + webhook throttle #839; see ADR-021. |
| Client invoice view UI rewrite | ✅ Wired | /client/settings/billing shows consolidated invoices + per-Specialist line items (Lago), shipped via #1155; JSON-safe serialization #1180. (#528) |
| Client payment collection (add card) | ✅ Wired | Add payment method button → GET /client/billing/checkout-url → Lago Stripe Checkout URL (setup-mode); ensureCustomer eagerly links the customer to the org's Stripe provider (LAGO_STRIPE_PAYMENT_PROVIDER_CODE); Lago auto-charges finalized invoices off-session. (#1268) |
| Trial period | ✅ Complete | 30-day default; trialStartedAt on Organization; TrialBanner in client portal |
| Trial state on billing page | ✅ Complete | 2026-05-10 (commit 8f83779, issue #187): orgs without an active subscription now show "Trial — N days remaining" / "Trial expired" instead of "Free plan" |
| Plan + usage page (client) | ✅ Complete | Lago-backed (#1155). Per-Specialist line itemization shipped. |
| Invoice history / download | ✅ Complete | Lago Manage Billing portal handles invoice history + PDF download (#1155). |
| VAT / tax ID | ⚠️ Partial | VAT field collected; Lago tax handling wiring still pending. |
| All-org subscriptions table (superadmin) | ✅ Complete | /ops/billing rebuilt on Lago in #1155 (#1165 was closed-unmerged and superseded by #1155): stat cards (total/active/sync_failed), subscription DataTable, Retry sync. |
11. API Webhooks (Outbound)
| Feature | Status | Notes |
|---|
| Inbound webhooks (channels) | ✅ Complete | Slack, Email, WhatsApp, Telegram all receive inbound webhooks |
| Outbound org-level webhooks | 🔮 Future | Signed payloads; per-org subscriptions — spec in FUTURE_REQUIREMENTS.md; not started |
| Webhook URL helper | ✅ Complete | GET /orgs/:orgId/channels/webhook-urls returns per-channel webhook URLs for client to configure |
12. Mobile
| Feature | Status | Notes |
|---|
| Responsive web (PWA) | ✅ Complete | ServiceWorkerRegistrar in root layout; responsive Tailwind styles; manifest.json present with correct start_url (/workspace/queue) and shortcut URLs |
| Native mobile app (expert) | ❌ Not built | USER_STORIES E-P4 (P1 gap) |
| Push notifications | ❌ Not built | USER_STORIES E-P3 (P0 gap) |
13. Knowledge Base & AI Training
| Feature | Status | Notes |
|---|
| Correction capture | ✅ Complete | Correction entity (factual_error, tone, missing_step, wrong_action); POST /learning/corrections; LearningService |
| Correction categorization UI | ⚠️ Partial | Error-type enum in entity; UI for selecting category not confirmed in workspace |
| Vector memory (agent) | ⚠️ Deprecated | Retrieval now lives via Haystack (api/src/haystack/*); agent_memories table is retained but unused. Issue #172 (agentic mode tool execution / checkpointing) was closed via PR #696 — no longer the reactivation rationale. |
| KB ingestion from corrections | 🔮 Future | RF-0015 — corrections refine the org KB through a downstream LearningService consumer |
| KB upload (business context) | ✅ Complete | API and onboarding upload endpoint added via Haystack; client KB management UI delivered by humanwork#297 |
| Agent memory visibility (client) | ✅ Complete | USER_STORIES C-T4 delivered by humanwork#297: /client/admin/knowledge-base supports list, upload, search, delete, detail chunks, and parse-status live updates |
| Feature | Status | Notes |
|---|
| Health dashboard | ✅ Complete | /ops/health (SA only); HealthController; API + DB + Redis + channel adapter status |
| Prometheus metrics | ✅ Complete | MetricsService + MetricsController; /metrics endpoint |
| Audit log viewer | ✅ Complete | AuditLog entity; AuditLogController; /ops/audit (SA only — #1238/#1298). Docs: docs/features/audit-log.md |
| Expert pool management (create/add member) | ❌ Removed | Expert pools removed 2026-05-03. Expert ↔ data access is now via expert_access (ADR-007). The legacy org_experts table is deprecated 2026-05-22 and dropped in #540 after soak. |
| Impersonation (superadmin) | ✅ Complete | ImpersonationService; audit-logged; superadmin only |
| Stale org auto-archive | ✅ Complete | StaleOrgService; 90-day threshold |
| Tool permission management (API) | ✅ Complete | ToolPermission entity; PUT /orgs/:orgId/tool-permissions/:toolName |
| Tool permission management (UI) | ❌ Not built | USER_STORIES SA-P5 (P1 gap) — API exists; no frontend |
| Per-org channel adapter health (superadmin UI) | ❌ Not built | USER_STORIES SA-P4 (P1 gap) — API exists; not surfaced in superadmin UI |
| Named account lead per org (UI) | ❌ Not built | USER_STORIES SA-C9 (P1 gap) |
| Expert leverage ratio analytics | ✅ Complete | SA-E4 — /ops/analytics Expert leverage (clients & Specialist instances per Expert); from expert_access. PR #3199 |
| Humanity Protocol credential verification | ⚠️ Stub | /users/:id/hp-verify endpoint is a stub; integration not functional |
15. AI Agent Runtime
| Feature | Status | Notes |
|---|
| Hermes CLI runtime (chat) | ✅ Complete | agent/hermes_client.py; shells out to hermes chat CLI; session mapping: Humanwork conv IDs → Hermes session IDs via cached mapping (first turn creates; subsequent turns resume) |
| Agent API contract v1 | ✅ Complete | /v1/chat, /v1/chat/stream wired to real Hermes runtime; agent/routers/chat.py |
| Agent-service shared secret auth | ✅ Complete | X-Agent-Secret header; middleware/auth.py uses hmac.compare_digest; permissive in dev when env var unset |
| Agent JWT guard (NestJS) | ✅ Complete | AgentTokenGuard validates agent-scoped JWT tokens on agent-api routes |
| Corrections pipeline | ✅ Complete | /v1/corrections on agent forwards to NestJS LearningService via corrections_client.py |
| Chat streaming (true SSE) | ⚠️ Partial | /v1/chat/stream endpoint exists and returns SSE-framed response; currently single-event. Current Hermes docs/source expose stable token streaming through config/TUI gateway message.delta, not a simple hermes chat --stream flag for the quiet subprocess path. |
| Agentic task runtime | ✅ Migrated to RIG (2026-05-25, #696) | The legacy /v1/tasks/* endpoints on the agent service were removed in #696 (RIG hard-cut) and now return HTTP 410 (LEGACY_TASK_AUTHORITY_REMOVED). The Platform /agentic/tasks/* HTTP surface remains but is now backed by the RIG runtime control plane (docs/architecture/RIG_HARD_CUT_RUNTIME_PORT_PLAN.md). LangGraph rollback and /graph/invoke remain removed. |
| RAG / memory retrieval | ⚠️ Partial | Platform-owned Haystack retrieval wired for /v1/agent-api/context and conversation history injection; dev=Railway, staging+prod=AWS ECS Fargate (humanwork-rag Cloud Map service) per ADR-024. pgvector storage is the active vector store. |
| POST-MVP agent surfaces (config/knowledge) | 🔮 Future | /v1/configure and /v1/knowledge return 501 deferred; explicit post-MVP boundary |
| HMAC signature crash fix | ✅ Complete | PR #206: verifyHmac now returns HTTP 401 instead of throwing RangeError on wrong-length signature |
| Pagination fix (has_more) | ✅ Complete | PR #206: has_more in getConversationMessages now correctly compares filtered count |
| Timing-safe learning token check | ✅ Complete | PR #206: assertPlatformToken in learning.controller.ts now uses timing-safe comparison |
| Prompt injection false-positive fix | ✅ Complete | PR #244: SECURITY_PREFIX rule 3 rewritten — addressing agent by assigned persona name is now correctly treated as normal usage, not injection |
| Specialist persona header injection fix | ✅ Complete | PR #244: NestJS no longer passes bare specialistSystemPrompt as full role template; agent now correctly merges persona into role template |
| Expert respond() real-time emit | ✅ Complete | PR #244: ExpertQueueService.respond() now emits agent_message_sent WebSocket event so client sees expert reply in real-time without waiting for 8s poll |
16. Per-Org Dogfood (acmefinancial)
2026-06-04 framing: Acme Financial is the first dogfood org. Eleanora Voss is the assigned KYC Specialist (per scripts/seed-dev.sh:429; eleanora@h852.work on dev). The prior "Bob" label was retired when the canonical seed was updated; references in older PR descriptions and ADRs may still say Bob — these are historical and have been re-aligned in docs/example-flow.md, docs/USER_OPERATIONS_MANUAL.md, and the MVP_P0_RUNBOOK. This section tracks runtime state for the Acme/Eleanora/KYC stack so contributors don't continue work against the stale generic-ecommerce or Bob-era frame. Sourced from #239 work items.
| Component | Status | Notes |
|---|
| Acme org seed (acmefinancial) | ✅ Complete | Seeded in dev; amy@acmefinancial.com (client), Eleanora Voss assigned as primary Specialist. |
| Eleanora Voss Specialist persona | ✅ Complete | Catalog entry + routing email (eleanora@h852.work); persona prompt merged via PR #244 (no more bare-prompt regression). |
KYC role template (kyc_ops.txt) | ✅ Complete | agent/prompts/kyc_ops.txt declares jumio_lookup tool with customer_id + lookup_type (basic|enhanced|sanctions). Verified by agent/evals/test_kyc_role.py. |
| SOUL + skills for Acme | ✅ Complete | orgs/acmefinancial/skills/kyc-ops/{jumio-lookup,sanctions-check,escalation-draft}/SKILL.md present. |
jumio_lookup tool descriptor | ✅ Complete | Registered in api/src/agent-api/tool-registry.ts; dispatch wired via PR #399. |
jumio_lookup end-to-end | ❌ Not shipped | Bespoke Jumio backend (#230) skeleton-only; blocked on Jumio sandbox account. Eval test_kyc_role.py currently asserts the graceful "Jumio not_implemented → escalate" branch only. |
| Per-Org sandboxed agent runtime | ✅ Complete via ECS provisioner | #362 Phase A (per-org Hermes session isolation) shipped earlier; Phase B Railway-per-org provisioner landed 2026-05-22 via PR #589 and was subsequently decommissioned via PR #1584 / #1450 / ADR-028. Only supported agent runtimes are now shared_railway and ecs_per_org. |
| HP credential verification (Eleanora's KYC turn) | ⚠️ Stub | /users/:id/hp-verify endpoint still a stub (see §14); separate from Jumio path. |
| Acme channel adapters | ⚠️ Partial | Email (Cloudflare inbound + Resend outbound) and WhatsApp (Twilio) confirmed live; Slack landed 2026-05-26 via #816 (Connect OAuth) + #1219 (per-org signing_secret) — see MVP_P0_RUNBOOK v2.0. |
| End-to-end smoke (F1 in #239) | ❌ Not built | Live smoke covering Amy → Eleanora → Expert review → outbound reply is the M1.5 closing-out gate. |
Gap Summary
| Priority | Gap count | Examples |
|---|
| P0 gaps | 1 | Push notifications (E-P3). Notes: per-Org sandboxed agent runtime (#362) shipped 2026-05-22 via PR #589; the Railway-per-org code path was subsequently decommissioned in PR #1584 / #1450 per ADR-028 (only shared_railway and ecs_per_org remain). Client approval UI (C-W8) demoted from P0 → P2 on 2026-05-23 — manual approval via chat for MVP. |
| P1 gaps | 9 | Jumio bespoke (#230 — blocked on sandbox account), business context import, SLA dashboard, expert profile UI, named account lead, tool permission UI, per-org channel health UI, HP credential verification, correction categorization UI, defer/reassign frontend. (Expert leverage ratio shipped 2026-06-24 via #3199.) |
| P2 gaps | 5 | Nango QuickBooks/Xero wiring (#176), invoice history, per-org gross-margin (partial: LLM cost + Expert workload shipped via PRs #3195/#3200; dollarized labour+infra cost pending #3196), inline ticket discussion, tool execution metrics |
| Future | 2 | Outbound webhooks, KB ingestion from corrections |
Status key: ✅ Complete | ⚠️ Partial | ❌ Not built | 🔮 Future (in FUTURE_REQUIREMENTS.md)
Workstream Notes & Cleanup Targets
Absorbed from the retired docs/technical-scopes.md on 2026-05-25. Only items still accurate against current code are kept; stale claims (LangGraph 4-node pipeline, R2 zero-impl, AgentMemory as live, Stripe as canonical billing, api/src/collab|tickets|seed|integrations/shopee, api/src/scheduler/ as unregistered, agent/placeholder/ as deletable) have been dropped.
Directory ownership (for parallel work)
Each scope below has roughly exclusive directory ownership; shared files require PR coordination.
| Area | Primary directories | Cross-ref §§ |
|---|
| Platform Core | api/src/{auth,common,admin,audit,users,health,config,database} | §1, §14 |
| Conversation & HITL | api/src/{conversations,expert-queue,notifications,learning,customers,specialists} | §3, §4, §5, §8 |
| Channels | api/src/{channels,email} (incl. channels/whatsapp/, channels/telegram/, channels/teams/) | §6 |
| Billing & Onboarding | api/src/{billing,onboarding,organizations} | §2, §10 |
| Integrations | api/src/integrations/ (shopify, amazon, jumio, nango, credentials), api/src/tools/ | §6a |
| AI Agent | agent/ (entire dir), api/src/{agentic,agents,agent-api}, api/src/haystack/, api/src/kb/ | §15, §16 |
| Client Portal FE | frontend/src/app/client/, frontend/src/app/onboarding/, frontend/src/components/client/ | §3 |
| Internal Surfaces FE | frontend/src/app/{workspace,ops}/, frontend/src/components/{internal,analytics}/ | §4, §9, §14 |
| Frontend Platform | frontend/src/{components/ui,components/shared,contexts,hooks,lib,__tests__}, frontend/src/app/login/ | — |
| Analytics | api/src/analytics/, frontend/src/components/analytics/ | §9 |
| Infra & DevOps | k8s/, .github/workflows/, e2e/, scripts/, docker-compose.yml | — |
Coordination-required shared files:
api/src/common/entities.ts — all TypeORM entities. Touch via PR review.
frontend/src/lib/api.ts — comprehensive API client (~3.2K lines). Imported by every FE scope.
frontend/src/contexts/AuthContext.tsx — JWT parsing + role-based redirect; FE-wide.
api/migrations/ — append-only, sequentially numbered. Coordinate the next number in Slack before opening a PR.
Cleanup targets (verified 2026-05-25)
Real dead-code targets that still exist on disk:
| Target | Path | Action | Notes |
|---|
Legacy /am/* sub-pages | frontend/src/app/am/ | Delete | Root-level redirect to /ops/* already in place. |
Legacy /superadmin/* sub-pages | frontend/src/app/superadmin/ | Delete | Redirects to /ops/* already in place. |
| Orphan top-level routes | frontend/src/app/{dashboard,analytics,integrations,queue}/ | Delete | Not linked from current nav; no role redirect lands here. |
| Duplicate setup route | frontend/src/app/ops/clients/[id]/setup/step2/ | Delete | Keep step-2/. |
| Demo backdoor | frontend/src/lib/api.ts (demo login bypass branch) | Remove before launch | Confirmed P0 blocker in mvp-launch-checklist.md. |
Refactor channels.controller.ts | api/src/channels/channels.controller.ts (~2.1K LOC monolith) | Split per channel adapter | Tracked as part of channels follow-up. |
Previously listed but already deleted (do not re-add to cleanup lists): api/src/collab/, api/src/tickets/, api/src/seed/, api/src/integrations/shopee/. api/src/scheduler/ is NOT dead — it is wired into health.module.ts and expert-queue.module.ts. agent/placeholder/ is retained as a deliberate local-dev fallback (see agent/placeholder/README.md); do not delete without coordinating with the agent team.
Suggested team groupings
| Team | Primary scopes | Primary skills |
|---|
| Platform Core | §1, §5, §14 | NestJS, TypeORM, PostgreSQL, WebSocket |
| Channels | §6 | Twilio, Slack/Teams APIs, Cloudflare workers |
| Commercial | §10, §9 analytics | Lago billing (per-Specialist monthly), onboarding UX, dashboards |
| Integrations | §6a | OAuth, Nango, Shopify/Amazon, credential mgmt |
| Intelligence | §15, §13 | Python, Hermes CLI, GPT-4o, pgvector, Haystack |
| Frontend | §3, §4, FE platform | Next.js 16, React 19, CSS, testing |
| Infrastructure | infra & DevOps | K8s, GitHub Actions, R2, Railway, Playwright |
Collision-avoidance rules
- Each scope has effective exclusive directory ownership. If your change crosses into another scope's directory, tag that scope's owner on the PR.
- The two shared FE files (
lib/api.ts, AuthContext.tsx) and the shared entities file (common/entities.ts) require PR review from affected scope owners.
- Migrations are append-only and sequentially numbered — coordinate the next number before authoring it.
- The
agent/ service is wholly owned by the Intelligence team. Other scopes that need agent changes provide the NestJS-side API surface (api/src/agent-api/); the Intelligence team wires the agent side.
Schema Changes (2026-05-03 audit)
expert_pools and pool_memberships tables removed — Expert ↔ data access is now via expert_access (ADR-007). The earlier intermediate org_experts table (2026-05-03 → 2026-05-22) is deprecated and removed in #540 after soak.
Organization.status consolidated to: pending_am_setup, pending_specialist_assignment, pending_client_confirmation, active, trial_ending, suspended, deactivated.
Organization.metadata JSONB column added for arbitrary fields without migrations.
email_audit_log table added for inbound/outbound email audit trail.
HWORK_DOMAIN env var replaces SPECIALIST_EMAIL_DOMAIN and NEXT_PUBLIC_APP_DOMAIN.