ADR-0001 ยท Tavus integration approach
2026-06-05/06 status: Phase 1 production-shape shipped โ #2052 (
tavus_personas+video_callstables), #2054 (security guardrails preventing cross-client data leakage), #2055 (on-demand video call creation API).
Status: Accepted โ implemented 2026-05-13 via PM-* commit series. POC repo hwork-persona-manager/ archived. ยท Date: 2026-05-13 ยท Deciders: Builder agent (autonomous) ยท Supersedes: none
Historical note: This ADR was authored under the original "Persona Manager" working name. The platform-facing surface is the Tavus integration โ API module
api/src/tavus/(TavusManagerService,TavusController), frontend routes/ops/tavus/*(rename completed via #1031, closes #630; legacy/ops/personas/*paths 308-redirect for ~30 days). References below to the archived POC repohwork-persona-manager/and migration filenames (PersonaManagerInit.tsetc.) are immutable historical artifacts.
Contextโ
A standalone Tavus integration POC at the archived hwork-persona-manager/ repo is build-complete.
It is its own FastAPI + Next.js stack with its own Postgres, its own Celery
queue, its own audit log, and its own hwork_client outbound to the main
h.work application.
The integration task is to land its functionality inside the existing
h.work monorepo (api/ NestJS, agent/ FastAPI, frontend/ Next.js)
without disrupting a production-grade codebase that is one operational task
away from MVP launch (45/46 P0 items complete per
MVP_LAUNCH_CHECKLIST.md).
The Builder operating prompt (prompts/persona-builder.md) sets a hard
constraint: do not modify existing code unless a task explicitly tells
you to.
Three high-level approaches were considered.
Optionsโ
A โ "Lift and shift": import the POC repo as-isโ
Copy hwork-persona-manager/apps/api into the monorepo as a third backend
app alongside api/ and agent/. Run it on its own port and DB connection.
Bridge to hwork-app over HTTP using the POC's existing hwork_client.
- Pros: preserves the POC verbatim. Zero re-implementation effort.
- Cons: duplicates auth, multi-tenancy, audit, the Postgres connection, CI orchestration, deployment surface, secret management. Two systems drift over time. Cross-tenant isolation now spans two services and two DBs โ testing it nightly becomes a meaningfully harder problem. Violates the spirit of "single source of truth for org data."
B โ "Additive re-implementation": rebuild inside the NestJS appโ
Build new NestJS modules (api/src/environment-profiles/,
api/src/meetings/, api/src/tavus/) and new Next.js routes that read from
the existing entities, write to five new tables, and reuse all existing
cross-cutting infrastructure (AuditService, OrgRlsInterceptor,
NotificationsGateway, JwtAuthGuard, PlatformRolesGuard,
OrgRolesGuard, ExpertQueueService).
- Pros: single Postgres connection, single auth model, single audit trail, single multi-tenancy mechanism. The integration touch points are countable and reviewable. Tests run in the existing harness. Nightly cross-tenant isolation already covers the new tables when added to its list (PM-0005). No drift between two parallel systems.
- Cons: re-implements ~70% of what the POC already wrote in Python +
SQLAlchemy. The POC's Celery workflow becomes BullMQ. The POC's separate
Postgres becomes a schema namespace inside the main one. Some POC
abstractions don't translate (e.g.
work_log_referencecollapses to live service-layer reads).
C โ "Hybrid extraction": vendor select POC pieces, ignore the restโ
Pull only the Tavus client, prompt templates, and function-call definitions
from the POC; ignore everything else and rebuild the rest. Use the POC's
hwork_client reversed โ call from POC outward into hwork-app โ converted
to a NestJS module.
- Pros: salvages the genuinely reusable POC outputs (Tavus contract understanding, function-call schemas, prompt structure).
- Cons: ambiguous boundary. Halfway between A and B without the benefits of either. Hard to draw the line during implementation.
Decisionโ
Option B. Additive re-implementation inside the NestJS app.
The five new tables (environment_profiles, meetings, meeting_decisions,
meeting_priorities, tavus_function_definitions) stand alone and reference
existing entities by FK. New code lives in new directories. Cross-cutting
infrastructure is reused, not duplicated. Agent service gets new prompt
files but no code changes (the existing load_prompt(role) already routes
on filename).
Update (2026-06-04): PR #1602 renamed the DB table
environment_profilesโclient_briefsat the SQL level. The API TypeScript module is stillapi/src/environment-profiles/with theEnvironmentProfileentity (mapped toclient_briefs) until follow-up #1199 lands the module/entity rename. Treat references toenvironment_profilesin this ADR as the historical name; the design and decision are unchanged.
The POC remains in hwork-persona-manager/ as reference material for entity
mapping, function-call schemas, and Tavus contract details, but it is not
imported, deployed, or run.
Consequencesโ
Positiveโ
- One auth model, one audit log, one DB connection, one multi-tenancy mechanism, one notification gateway.
- The integration touch points are explicit and small: additive functions in
frontend/src/lib/api.ts, single-line module imports inapi/src/app.module.ts, additive entity entries in.github/workflows/agent-cross-org-isolation-nightly.yml. Anything else in an existing file is a violation requiring arequires_existing_code_changeevent. - Reporting decisions ride the existing
Actionentity as a designed extension point (actorType=reporting_meeting). The existing audit log viewer surfaces them for free. - Reporting escalations go through
ExpertQueueService.create(...). The expert workspace queue surface is reused unchanged. - The brief assembler reads live from
Conversation/Message/Action/ExpertQueueItem/Correctionvia service-layer queries โ no parallel work-log store, no sync job. - Single Postgres + single
OrgRlsInterceptorcovers tenancy. Each new table gets an RLS policy in its migration matching migrations 9, 12, 17, 20, 27, 33.
Negative / acceptedโ
- Re-implementation effort. The POC's Python services become TypeScript services. The mitigation is that the POC's domain model carries over cleanly to TypeORM entities โ most of the translation is mechanical.
- The POC's Celery + Redis async pattern is replaced by BullMQ (already in
package.jsonand minimally used). Only the persona archival job (PM-0066) actually needs background scheduling; the rest of the workflow is request-response. - Versioning of role templates (POC's
role_template_versionentity) is deferred. See ADR-0003 if/when an actual need arises.
Neutralโ
- The
agent/prompts/directory becomes a shared prompt registry read by both the agent service (load_prompt(role)) and the NestJSpersona-generator.service.ts(file read + parameter substitution before passing totavusClient.createPersona(prompt)). This is fine because the agent's loader keys onroleand the NestJS reader simply renders the same template into a different consumer. No code change to the loader.
Permitted touches to existing filesโ
These are sanctioned by the Builder prompt and listed exhaustively here so future cycles can reference them without re-deriving:
| File | Allowed change | Why |
|---|---|---|
frontend/src/lib/api.ts | Append new exported functions only. No signature changes, no reordering, no deletion. | Single API client convention in the frontend. |
api/src/app.module.ts | Add imports: [..., NewModule] single-line entries. | NestJS module registration. |
.github/workflows/agent-cross-org-isolation-nightly.yml | Append new entity names to the test list. | Cross-tenant isolation is existential; new tables must be covered. |
Every other existing file is read-only. Any task that appears to require
otherwise must emit a requires_existing_code_change event.
Verificationโ
- PM-0001 confirmed all referenced entities and patterns exist where the Builder prompt indicated.
- The HMAC mirror pattern (PM-0012) reproduces
channels.controller.ts:47-62without import. - The Action extension-point write (PM-0044) is exercised by the Phase 6 Playwright E2E test (PM-0061) which asserts the row appears in the existing audit log viewer.
Referencesโ
prompts/persona-builder.mdโ Builder operating prompt (canonical)docs/specs/tavus-integration.mdโ system-level integration spec (renamed frompersona-manager-integration.mdon 2026-06-02)hwork-persona-manager/docs/architecture.mdโ POC referenceapi/migrations/9, 12 (MemoryRLS), 13, 17, 27, 33 โ RLS patterns (PM-0067 fix)api/src/channels/channels.controller.ts:23-62โ HMAC patternapi/src/common/entities.tsโActionextension-point entity