Persona Manager → Human.work Integration Spec
Status: approved 2026-05-13 · Builder source: prompts/persona-builder.md
This spec defines the design surface for integrating the Persona Manager (a
standalone POC at hwork-persona-manager/) into the Human.work monorepo. It
is the authoritative reference for the Builder agent during Phases 0–6 of
TODO_PERSONA.md.
1. Scope
The Persona Manager adds two video-conversation workflows to Human.work:
| Workflow | Trigger | Tavus role | Agent-service role |
|---|---|---|---|
| Onboarding | First deployment of a Specialist to a client org | Hosts a ~60min structured intake video call; its own LLM drives the conversation | Not in-loop. Tavus webhooks call back to the Human.work API to record function-call outputs. |
| Reporting | Scheduled or ad-hoc check-in | Hosts the video call; in-conversation responses about work history are fetched live | Receives /chat calls from Tavus with role="reporting_meeting" (see §6.3). |
2. Five new tables (additive only)
All new tables are created in a single new migration. No existing table is
altered. Each table carries organization_id for tenancy and gets an RLS
policy in the migration matching the patterns in migrations
9 (RLS), 12 (MemoryRLS), 13 (RlsMissingTables), 17 (OrgOnboardingRLS),
27 (OnboardingTablesRLS), 33 (RlsComplete). PM-0067 corrected the
originally-cited list — migration 20 (OnboardingOtps) has no RLS content.
| Table | Purpose | Key FKs |
|---|---|---|
environment_profiles | Structured operating-environment facts captured during onboarding for an OrgSpecialistAssignment. One row per (org, specialist) deployment. | org_id → organizations, assignment_id → org_specialist_assignments, specialist_id → specialists |
meetings | A scheduled or ad-hoc video touchpoint. kind in (onboarding, reporting). References Tavus persona/conversation IDs. | org_id, assignment_id, created_by → users |
meeting_decisions | Decisions logged via reporting function calls. Also mirrored into actions (see §4). | meeting_id, org_id |
meeting_priorities | Priority changes logged via reporting function calls. | meeting_id, org_id |
tavus_function_definitions | Persona function-call library. Versioned JSON Schema. SuperAdmin-managed. | none (global) |
Full column-level schema is defined in the PM-0003 migration (Phase 0).
3. Entity mapping — POC ↔ hwork-app
The standalone POC has its own entity model. The integration maps as follows:
| POC entity (hwork-persona-manager) | hwork-app equivalent |
|---|---|
client_org | Organization (existing) |
user (with tier) | User + platformRole + OrgMembership.org_role (existing) |
role_template + role_template_version | Specialist (existing); versioning deferred to ADR-0003 |
deployment | OrgSpecialistAssignment (existing) |
environment_profile | environment_profiles (new) |
meeting | meetings (new) |
work_log_reference | Not ported. The brief assembler reads live from Conversation/Message/Action via service-layer queries (ADR-0007 of the POC is moot here). |
function_call_definition | tavus_function_definitions (new) |
audit_log (POC) | Not ported. Use existing AuditService.log(...) → audit_logs table. |
4. The Action extension-point pattern
The existing Action entity (created in migration 11) is designed as an
extension point. It carries actorType, actionType, payload, result,
risk_level, status. The persona manager uses it for reporting decisions
only:
await actionRepo.save({
orgId,
conversationId: null, // reporting decisions are not message-bound
messageId: null,
actorType: 'reporting_meeting',
actionType: 'decision_logged',
payload: { meetingId, decisionId, summary, manager_user_id },
result: { meetingDecisionId },
riskLevel: 'low',
status: 'completed',
});
This is the only sanctioned write to an existing table from persona manager code. All other mutations go to the new tables.
Reporting escalations are different — they are not Action writes; they are
calls to ExpertQueueService.create(...), which itself creates an
ExpertQueueItem and emits the existing notification events. The persona
manager does not duplicate the queue surface.
5. Tavus webhook HMAC mirror
The Tavus webhook controller lives in a new module
(api/src/tavus/tavus.controller.ts) and mirrors the HMAC verification
pattern used in api/src/channels/channels.controller.ts (lines 47–62 for
the boot-guard pattern, body verification inline at each endpoint).
Specifically:
- A boot guard fails fast in deployed envs if
TAVUS_WEBHOOK_SECRETis unset — mirrorschannels.controller.ts:47-62(the email boot-guard block). - Each webhook endpoint reads the raw body, computes
createHmac('sha256', secret), and usestimingSafeEqualagainst the header. The cleanest existing reference for the per-request verification is the email handler atchannels.controller.ts:445-465(single-secret HMAC-SHA256). Slack (v0:prefix, lines 396-405), Twilio (HMAC-SHA1, line 1360+), and WeChat (line 1665) are variants of the same shape. Header name: accept bothTavus-SignatureandX-Tavus-Signature; accept bare hex orsha256=prefix. - No code from
channels.controller.tsis imported or modified. The pattern is reproduced cleanly so the channels module stays decoupled.
Idempotency: each webhook payload carries a Tavus idempotency key. The
router dedupes in Redis (using the existing getRedisClient() from
api/src/common/redis.util.ts) with a tavus:webhook:idem:<key> namespace
and a 24-hour TTL.
6. Agent service touchpoints
6.1 Onboarding prompt — new file only
agent/prompts/onboarding_meeting.txt is a new template, parameterized via
the existing {agent_name} and {agent_context_section} placeholders. The
template is also rendered by persona-generator.service.ts (in NestJS) when
creating the Tavus persona — NestJS reads the file from disk and substitutes
parameters before calling tavusClient.createPersona(prompt).
6.2 Reporting prompt — new file only
Same pattern: agent/prompts/reporting_meeting.txt. The reporting persona's
pre-call brief is injected as part of {agent_context_section} via a
structured envelope (see PM-0040 brief assembler).
6.3 Loader behavior — no code change required
agent/prompts_loader.py:load_prompt(role) resolves prompt files by role.
A chat call with role="onboarding_meeting" will naturally load
prompts/onboarding_meeting.txt. No mode parameter or loader change is
needed. Builder rule #9 was originally drafted under the assumption a
mode param would be required; PM-0001 context read confirmed otherwise.
The rationale is captured inline above; no separate ADR is needed
(PM-0071 cleanup).
7. Frontend surface
New routes only:
/client/admin/specialists/[id]/onboarding/— start, in-progress, post-call review/client/admin/specialists/[id]/meetings/— list, detail, in-session, post-session/ops/personas/function-calls(SuperAdmin) — function-call CRUD/ops/personas/replicas(SuperAdmin, Phase 5) — Tavus replica directory/ops/personas/templates(SuperAdmin, Phase 5) — persona prompt templates
Additive functions are appended to frontend/src/lib/api.ts:
startOnboarding, getEnvironmentProfile, updateEnvironmentProfile,
approveEnvironmentProfile, plus meeting CRUD/start. No signature changes
to existing functions.
WebSocket events extend the existing /notifications namespace:
meeting_started, meeting_function_call, meeting_completed. Emitted via
the existing NotificationsGateway — handlers obtain the gateway via DI
and call server.to('org:<orgId>').emit(...).
8. SOUL.md non-mutation
The persona manager generates orgs/<slug>/SOUL.proposed.md only. The
operator review/promote workflow for SOUL.md itself remains the existing
manual process. The Environment Profile approval does not write to
SOUL.md; it only updates a row in environment_profiles and writes the
proposed file.
9. Out of scope (deferred)
- Role-template versioning (POC
role_template_version) — deferred to ADR-0003 when a versioning need actually arises. - POC's Celery + Redis async workers — replaced by BullMQ (already installed) only for the persona archival job (PM-0066).
- POC's separate Postgres database — collapsed into the main hwork-app Postgres. Tables live under the same connection.
10. References
prompts/persona-builder.md— Builder operating promptdocs/decisions/persona-manager/ADR-0001-integration-approach.md— chosen integration approach (forthcoming, PM-0002)hwork-persona-manager/docs/architecture.md— POC architecture (reference only; not the integration's authority)api/migrations/9, 12 (MemoryRLS), 13, 17, 27, 33 — RLS patterns to mirrorapi/src/channels/channels.controller.ts:23-62— HMAC pattern to mirrorapi/src/notifications/notifications.gateway.ts— Socket.io pattern to extendagent/prompts_loader.py— prompt assembly pipeline