---
path: /personas
surface: admin
domain: content
auth: signed-in (admin) + scope:admin:workspace:persona
source: apps/oshun/admin/src/app/personas/page.tsx
status: drafted
last_walked: '—'
---

# Content · Personas, voices, avatars

## Purpose

Govern persona registry, voice profiles, avatar packs, release channels, policy
packs, and disclosure state. `operatorPromise`: "Every persona, voice, and
avatar carries an attested policy pack, lineage, and release posture visible to
operators."

## Entry points

- Sidebar item **"Personas"** (AdminSidebar — content group)
- Review → personas handoff (`review → personas` per
  `OSHUN_ADMIN_WORKSPACE_RELATIONSHIPS`) — "Review decision must be applied to a
  persona release channel."

## Layout regions

Inside `AdminShell`:

- `<WorkspaceEntryPoint workspaceId="personas" ...>`
- When `detail.accessible`, ten panels in this order:
  - `<PersonaRegistryPanel registryEntries={registrySnapshot.registryEntries}>`
  - `<TaraAiPersonaSponsorPanel />`
  - `<TaraAiPersonaSequenceScriptPanel />`
  - `<TaraAiPersonaNameplatePanel />`
  - `<PersonaGovernanceEditor entries={governanceSnapshot.entries}>`
  - `<ToneCalibrationReviewPanel runs={releaseSnapshot.toneCalibrationRuns}>`
  - `<RehearsalOutcomesPanel rehearsals={releaseSnapshot.rehearsals}>`
  - `<PremiumCastApprovalPanel approvals={releaseSnapshot.premiumCastApprovals}>`
  - `<VoiceProfileReviewPanel voiceProfileReviews={voiceSnapshot.voiceProfileReviews}>`
  - `<AvatarPackReviewPanel avatarPackReviews={avatarSnapshot.avatarPackReviews}>`

Five separate snapshot extractors run against `detail.result.value`:

- `extractPersonaRegistrySnapshot` → `registryEntries`
- `extractPersonaGovernanceSnapshot` → `entries`
- `extractPersonaReleaseSnapshot` → `toneCalibrationRuns`, `rehearsals`,
  `premiumCastApprovals`
- `extractVoiceProfileSnapshot` → `voiceProfileReviews`
- `extractAvatarPackSnapshot` → `avatarPackReviews`

Three of the panels (`TaraAiPersonaSponsorPanel`,
`TaraAiPersonaSequenceScriptPanel`, `TaraAiPersonaNameplatePanel`) take no props
from the snapshot.

## States

- [ ] Anonymous → middleware redirect to
      `/unauthorized?reason=missing-session&returnTo=/personas`
- [ ] Signed-in (admin) without `admin:*`, `admin:studio`, or
      `admin:workspace:persona` → `WorkspaceEntryPoint` "Access not granted";
      the ten panels hidden
- [ ] Scope ok + BFF ok → all ten panels render
- [ ] Scope ok + BFF `workspace-unavailable` → entry-point "Workspace data
      unavailable"; panels hidden
- [ ] Snapshot extractors fall through to defaults when `detailValue` is null
- [ ] Note IA id `personas` (plural), `bffWorkspaceId: 'persona'` (singular) —
      BFF endpoint is `/v1/admin/workspaces/persona`

## Interactions

Top-level interactive elements live inside each child panel:

- [ ] **PersonaRegistryPanel** — registry list / table
- [ ] **TaraAiPersonaSponsorPanel** — sponsor management (no props)
- [ ] **TaraAiPersonaSequenceScriptPanel** — sequence-script editing
- [ ] **TaraAiPersonaNameplatePanel** — nameplate management
- [ ] **PersonaGovernanceEditor** — governance entry editor
- [ ] **ToneCalibrationReviewPanel** — tone calibration run results
- [ ] **RehearsalOutcomesPanel** — rehearsal outcomes
- [ ] **PremiumCastApprovalPanel** — premium cast approvals
- [ ] **VoiceProfileReviewPanel** — voice profile review (overlap with
      `../../isis/isis-voice-cloning.md`)
- [ ] **AvatarPackReviewPanel** — avatar pack review

## Data & contracts

- **Reads**: `loadWorkspaceDetail('personas', session)` →
  `/v1/admin/workspaces/persona`; five snapshot extractors share the same
  `detail.result.value`
- **Writes**: per panel (governance edits, calibration runs, rehearsal outcomes,
  premium cast approvals, voice / avatar review dispositions)
- **Realtime**: _None at page level._
- **Auth/role check**: middleware + page
  (`canEnterAdminWorkspace(scopes, 'personas')` → required scopes
  `['admin:*', 'admin:studio', 'admin:workspace:persona']`)

## Cross-references

- Shell: `shell/01-app-shell.md`, `shell/04-workspace-pattern.md`
- Workspace definition: `libs/oshun/navigation/src/admin-ia.ts` (`personas` —
  `kind: 'registry'`, `operatorRelationship: 'register'`,
  `bffWorkspaceId: 'persona'`)
- Handoff origin (`from → personas`): `../governance/review.md`
- Handoff destination (`personas → rights`): `../safety/rights.md`
- Library: `apps/oshun/admin/src/lib/persona-registry-detail.ts`,
  `persona-governance-detail.ts`, `persona-release-detail.ts`,
  `voice-profile-detail.ts`, `avatar-pack-detail.ts`
- Related Isis route (model side): `../../isis/isis-voice-cloning.md` (clone
  workflow)
- Component sources: `PersonaRegistryPanel.tsx`,
  `TaraAiPersonaSponsorPanel.tsx`, `TaraAiPersonaSequenceScriptPanel.tsx`,
  `TaraAiPersonaNameplatePanel.tsx`, `PersonaGovernanceEditor.tsx`,
  `ToneCalibrationReviewPanel.tsx`, `RehearsalOutcomesPanel.tsx`,
  `PremiumCastApprovalPanel.tsx`, `VoiceProfileReviewPanel.tsx`,
  `AvatarPackReviewPanel.tsx`

## Open questions / known gaps

- [ ] The three `TaraAiPersonaXxxPanel` components take no props — document
      their internal data sources (self-fetching panels? module-level constants
      like Lilith?)
- [ ] Naming inconsistency: IA `personas` (plural), BFF `persona` (singular).
      Other workspaces also drift (incidents/incident, models/model)
- [ ] `VoiceProfileReviewPanel` overlap with both `../safety/trust-safety.md`
      (cloned-voice review) and the isis voice-cloning admin route — clarify
      ownership boundaries
