# Persona, Avatar, and Voice Packs

Personas, avatars, and voice packs are the platform's governed _embodiments_ —
the named characters a customer talks to (a Tara contemplative teacher, an Arete
coach, a Metis explainer, the shell concierge), together with the optional
synthetic voice and avatar that give them a face and a sound. They are
first-class, lifecycle-managed assets with declared modality combinations,
consent artifacts, watermarks, lineage attribution, and multi-party release
gates — never ad-hoc prompt strings. This page sits at the cross-domain layer of
V1: every domain (`tara`, `arete`, `veritas`, `nyx`, `nisaba`, `metis`) and
every operator surface (`shell-assistant`, `studio`, `admin`) looks personas up
by id, and a premium voiced or avatar experience ships only after
[Lilith Persona Policy](./lilith-persona-policy.md),
[Sophia Grounding](./sophia-grounding.md) (where pedagogical),
[Iris Memory and Identity](./iris-memory-identity.md), the
[Isis Generation Control](./isis-generation-control.md) release gate,
rights/provenance, and trust-and-safety review have all signed off.

The registry that backs all of this is deeply implemented, not a contract
skeleton. `@oshun/persona-registry` (`libs/oshun/persona-registry`) ships the
record shape, the seven-role catalog, the avatar/voice/consent contracts, the
realism and impersonation policy tables, the disclosure-visibility gates,
roughly fourteen named evaluation suites with their drift detectors, the
launch-cast assembly, and an end-to-end signoff flow — about sixty-seven
re-exported modules in one barrel. The honestly _aspirational_ parts are the
runtime media themselves: real avatar **rendering** and lip-sync against
ground-truth video, and real **cloned-voice TTS output**. Provider integration
files for the latter exist
(`libs/isis/ai-providers/src/providers/tts/elevenlabs-provider.ts`,
`libs/isis/audio-generation/src/voice/elevenlabs-client.ts`), but the registry
deliberately governs _whether and how_ a voice or avatar may render; it does not
itself render frames or synthesize audio. Where a capability is provider-gated
or spec-only, this page says so.

## Where This Fits in V1

The registry is the system of record that other subsystems consume:

| Subsystem       | Relationship to personas                                                                                               | See                                                                                                                          |
| --------------- | ---------------------------------------------------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------- |
| Lilith          | Owns the policy pack a persona binds to; defines the seven canonical roles, tone bands, capability ceilings, hard-bans | [Lilith Persona Policy](./lilith-persona-policy.md)                                                                          |
| Iris            | Holds per-persona memory scope (shared-with-umbrella vs. isolated); enforces the declared boundary                     | [Iris Memory and Identity](./iris-memory-identity.md)                                                                        |
| Isis            | Runs the release gate before a voiced/avatar persona reaches a surface; enforces generation tiers                      | [Isis Generation Control](./isis-generation-control.md), [Generation Tiers and Surfaces](./generation-tiers-and-surfaces.md) |
| Sophia          | Grounding fidelity scorecard for pedagogical personas (teacher, explainer, comparative)                                | [Sophia Grounding](./sophia-grounding.md)                                                                                    |
| Voice providers | Supply the actual TTS engine behind a `VoiceProfile` (ElevenLabs and local engines, swappable)                         | [External Model Intelligence and Execution Providers](./external-models-and-execution.md)                                    |

Cross-references: bare §N point at the `V1/TODOS.md` backlog (the registry
implements the §14.x persona/avatar/voice tasks and the §12.8 role catalog);
deps§N at `V1/DEPENDENCIES.md`. The features hub is
[../features.md](../features.md).

## Persona Registry and Metadata

The registry is modeled as an **immutable snapshot**: a
`PersonaRegistrySnapshot` (`index.ts`) is a map of `PersonaRecord`s keyed by a
stable `PersonaId`, plus `orderedIds` (registry-declared order), a
`builtAtUnixSeconds` timestamp, and a deterministic `fingerprint`. The
fingerprint is an FNV-1a 64-bit hash over a stably-stringified snapshot (keys
sorted recursively), so distributed callers — a domain BFF, the admin app, an
eval runner — can verify they agree on the same registry state without a full
deep-equal. All lookup and filtering operations are O(1) or O(n) and never
mutate the snapshot; timestamps are _inputs_, not outputs (the module is pure
data + pure functions, no IO, no clock).

### Dependency-free by design

A deliberate architectural fact worth documenting: the registry **does not
import** `@oshun/persona-policy-lilith`. The `PersonaRecord` carries an opaque
`policyPackReference` string that the _caller_ resolves against the Lilith
package (`libs/oshun/persona-policy-lilith` — a real package with `src`, tests,
and a `project.json`). Keeping the registry dependency-free means any domain can
consume persona records without dragging in the policy engine, and the policy
layer can evolve independently of the record shape.

### The persona record

`PersonaRecord` (`index.ts:158`) is the contract-stable core. (Note: the
platform also has a separate `Persona` type in
`libs/contracts/src/common/persona.ts` — a `z.infer` of `PersonaSchema` — but
the registry's own record type is `PersonaRecord`, not `Persona`. Where
`V1/ARCHITECTURE.md` names "Persona / VoiceProfile / AvatarPack" as the registry
contracts, the accurate names are `PersonaRecord`, `VoiceProfile`, and
`AvatarPack`.) Its fields:

| Field                                           | Meaning                                                                                                                                        |
| ----------------------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| `id`                                            | Stable `PersonaId`, URL-safe slug (lowercase, digits, hyphens, 3–64 chars)                                                                     |
| `displayName`, `summary`, `rationale`           | Human-readable identity + admin-surfaced reasoning                                                                                             |
| `family`                                        | One of nine `PERSONA_FAMILY_KINDS`: `contemplative`, `coach`, `explainer`, `guide`, `scholar`, `support`, `moderator`, `reviewer`, `executive` |
| `status`                                        | `PersonaApprovalStatus` lifecycle state (below)                                                                                                |
| `surfaces`                                      | Subset of `PERSONA_SURFACES` the persona inhabits                                                                                              |
| `policyPackReference`                           | Opaque Lilith policy-pack handle (resolved by the caller)                                                                                      |
| `ownerTeam`                                     | Governance owner                                                                                                                               |
| `createdAtUnixSeconds` / `updatedAtUnixSeconds` | Audit timestamps (inputs)                                                                                                                      |
| `tags`                                          | Free-form filter tags                                                                                                                          |
| `lineage?`                                      | Declared tradition slugs (e.g. `theravada-vipassana`, `stoic-roman`); empty = not lineage-bound                                                |
| `scope?`                                        | A `PersonaScope` bounding the conversational territory                                                                                         |
| `toneProfileRef?`                               | Reference to a tone profile in `persona-tone-calibration-rubric.ts`                                                                            |

`PersonaScope` makes the boundary concrete: a `kind` (one of
`PERSONA_SCOPE_KINDS` — `general-assistant`, `contemplative-teaching`,
`habit-coaching`, `fact-checking`, `sleep-guidance`, `scholarly-research`,
`operator-shell`, `creative-companion`, `enterprise-advisor`), `inboundTopics`
it may speak to, `excludedTopics` it must refuse or hand off, and a
`memoryBoundary` of `shared-with-umbrella` or `isolated-to-persona`. The
thirteen `PERSONA_SURFACES` span the six domains plus `shell-assistant`,
`shell-routines`, `shell-achievements`, `shell-desktop`, `shell-wearable`,
`studio`, and `admin`.

### Roles vs. families

The nine _families_ above are a coarse taxonomy; the seven canonical _roles_ are
the Lilith-governed behavioral envelope. `roles/catalog.ts` defines exactly
seven `PersonaRoleDefinition`s, frozen at build time and asserted as a Set of
seven by `roles.test.ts`, in this order: `teacher`, `coach`, `explainer`,
`steward`, `comparative`, `narrator`, `assistant`. Each role declares a
**capability ceiling**, a **default tone band**, and a **hard-ban** set. The
capability ceiling matters because it caps what a persona of that role may ever
do — for example only some roles may render an avatar at all:

| Role          | Default tone band      | Capability ceiling (highlights)                                                              | Notable hard-bans / notes                                                       |
| ------------- | ---------------------- | -------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------- |
| `teacher`     | `contemplative`        | `voice-tts`, `avatar-render`, `living-scene-render`, `memory-write/read`, `scheduled-invite` | Bans `cross-lineage-mix`; "never mixes lineages in one arc"                     |
| `coach`       | `reflective`           | `voice-tts`, `avatar-render`, `memory-write/read`, `scheduled-invite`                        | "Surfaces practice not prescription"; no living-scene render                    |
| `explainer`   | `neutral`              | `voice-tts`, `avatar-render`, `living-scene-render`, `memory-read`                           | Read-only memory; "evidence-bound explanations; no prescriptions"               |
| `steward`     | `contemplative-strict` | `voice-tts`, `memory-write/read`, `sensitive-category-recall`                                | No avatar; "sensitive recalls allowed within consent"                           |
| `comparative` | `contemplative`        | `voice-tts`, `avatar-render`, `living-scene-render`, `memory-read`, `cross-lineage-mix`      | The _only_ role allowed to cross lineages — "must name each lineage explicitly" |
| `narrator`    | `instructional`        | `voice-tts`, `avatar-render`, `living-scene-render`                                          | "Read-only narration; never writes memory or schedules invitations"             |
| `assistant`   | `briefing`             | `voice-tts`, `memory-write/read`, `scheduled-invite`                                         | "Concierge for surfaces; no avatar/scene render by default"                     |

Every role hard-bans `medical-advice`, `financial-advice`, `legal-advice`,
`urgency-amplifier`, `fear-framing`, `guarantee-language`, and
`spiritual-prescription`. `validateRole` and `validateRoleCatalog` enforce that
every capability id and ban id is canonical and that the catalog has exactly the
seven keyed roles with matching ids — a build-time integrity check that the role
table cannot drift.

### Lifecycle and approval state

`PersonaApprovalStatus` encodes the canonical §14.1 path:

```
drafted → in-review → rehearsal → approved-for-test →
approved-for-release → released → deprecated → retired
```

Legacy aliases (`draft`, `approved`, `published`) are retained for backward
compatibility; `canonicalizePersonaApprovalStatus` and
`PERSONA_APPROVAL_STATUS_ALIASES` normalize them (`draft`→`drafted`,
`approved`→`in-review`, `published`→`released`) so downstream gates only reason
over the eight canonical states in `PERSONA_APPROVAL_STATUSES_CANONICAL`. The
completeness audit (2026-06-22) marks the persona approval workflow **PARTIAL**:
registry lifecycle and signoff are well covered, while full
deprecate/retire/recovery transitions and the consumer-picker round-trip are
thinner — an honest gap, not a fabricated "done."

Around the record, sibling modules implement: per-stage approval
(`qa-reviewer-roles.ts`), disallowed phrasing and prohibited claim classes
(`token-time-disclosure-filter.ts`, `persona-allowed-context-taxonomy.ts`), and
scorecards (`scorecards.ts`) across tone, gentleness, citation hygiene,
grounding fidelity, audience-appropriate language, and regression baselines.

## Avatar Pack Contracts

`AvatarPack` (`avatar-pack.ts:150`) binds an avatar to a `personaId` and pins
the renderable assets and their guardrails:

- **Assets** — `baseMesh`, `blendshapesAsset`, `textureSet` (each an
  `AvatarAssetRef` with a 64-hex-char SHA-256 content hash, content type, and
  positive byte size), a `riggingVersion`, the full `exposedBlendshapes` set,
  and a `visemeMap` for lip-sync.
- **Modality + entitlement gating** — allowed modality combinations (text-only,
  voice, voice + avatar) are validated per platform and per entitlement
  (`modality-combinations.ts`, `modality-platform-entitlement.ts`).
- **Likeness controls** — source identity (synthetic-only, consented individual,
  consented composite), distance from real-person likeness, and an
  identity-protection denylist (`likeness-control.ts`).
- **Watermark** — `AvatarWatermarkMetadata` declares the algorithm, whether it
  is `appliedPerFrame`, and whether `c2paEnabled` provenance is produced.
- **License** — `AvatarLicenseMetadata` with licensor, license reference,
  validity window, and a `commercialUse` flag.

`validateAvatarPack` is genuinely strict and domain-specific, not a shape check.
It enforces a **minimum safety blendshape set** (every pack must expose
`mouth-closed`, `mouth-ah`, `eye-blink-left`, `eye-blink-right`), requires every
canonical viseme id to appear in the `visemeMap` with activations referencing
only exposed blendshapes at weights in `[0, 1]`, requires strictly-positive
rendering constraints (`minFramesPerSecond`, `maxTriangleCount`,
`maxTextureResolutionPx`, `maxBlendshapesPerFrame`) with known render targets,
and — the load-bearing rights coupling — **forces watermarking and C2PA when the
license permits commercial use**:

```
if (pack.license.commercialUse && !pack.watermark.appliedPerFrame) → error
if (pack.license.commercialUse && !pack.watermark.c2paEnabled)     → error
```

`checkAvatarPackLicense` (and the `AvatarPackLicenseCheckResult` at
`avatar-pack.ts:313`) decides at render time whether the license is in effect
for `nowUnixSeconds` (returning `license not yet in effect`, `license expired`,
or `license active`). The lip-sync/expression/animation _quality_ floors that
block release live in the eval suites (below); the pack contract pins the assets
and watermark posture, and `avatar-release-gate.ts` composes the gate.

## Voice Profile Contracts

`VoiceProfile` (`voice-profile.ts:195`) is the per-voice contract. Its fields
include `profileId`, `personaId`, a `kind` (`VoiceProfileKind`), an internal
`engineId` (`VoiceEngineId`) plus an opaque `engineModelRef`, the commercial
`vendor` (`VoiceVendor` — e.g. ElevenLabs, Microsoft — kept distinct from the
internal engine), `supportedLocales`/`defaultLocale` (BCP-47, with the default
required to appear in the supported set), an `acousticBaseline`, a
`prosodyProfile`, a `naturalnessBaseline`, per-locale `languageCoverage`, and
the consent linkage: `sourceIdentityRef` and `consentReference` (both `null` for
purely synthetic voices), plus a `requiredConsent` array of
`VoiceConsentArtifactKind`s. `validateVoiceProfile` enforces the slug pattern,
BCP-47 locale validity, non-empty descriptive fields, canonical engine/kind ids,
and acoustic sanity (e.g. positive `rateMultiplier`).

### Provenance and the C2PA requirement

`VoiceProvenanceRequirements` carries a `watermarkAlgorithm`,
`requireSessionScopedSignature`, `requireSpokenSynthesisDisclosure` (a spoken
"this voice is synthetic" intro on first use), and `requireC2paManifest`. That
last flag is a **real, cross-cutting policy input** — `requireC2paManifest` is
referenced not only here but in `voice-provider-abstraction.ts`,
`realism-impersonation-thresholds.ts`, and `watermark-provenance.ts` — so a
single contract field threads provenance enforcement through the provider layer
and the release gates.

### Provider abstraction (swappable engines)

`voice-provider-abstraction.ts` makes the engine swappable.
`CANONICAL_VOICE_PROVIDERS` enumerates `oshun-native`, `elevenlabs`,
`xtts-local`, `tortoise-local`, `openvoice-local`, and `custom`;
`CANONICAL_VOICE_PROVIDER_CAPABILITY` enumerates `synthetic-tts`, `cloned-tts`,
`multilingual`, `streaming`, `emotion-conditioning`, `long-form`. The module
ships `CANONICAL_CLONED_VOICE_SAFEGUARDS`, per-output-class
`CANONICAL_AUDIO_WATERMARK_REQUIREMENTS`, per-voice-class
`CANONICAL_VOICE_QUALITY_REQUIREMENTS`, a
`CANONICAL_VOICE_PROVIDER_FALLBACK_ORDER`, and evaluators
(`evaluateCanonicalVoiceQuality`, `evaluateCanonicalAudioIntegrity`) plus
`buildVoiceProfileAdminListItem` for the studio chip. The **fallback chain** is
the contract for "when the primary provider fails, fall back per voice id with
acceptable degradation rules." The actual ElevenLabs integration that backs the
swappable claim is real on disk —
`libs/isis/ai-providers/src/providers/tts/elevenlabs-provider.ts` and
`libs/isis/audio-generation/src/voice/elevenlabs-client.ts` exist — but the
registry governs _whether_ a profile may render; exercising the provider
end-to-end (real cloned-voice audio out) is the provider-gated,
not-yet-verified-here boundary.

### Cloned-voice rules and quality

Cloned-voice profiles require a signed consent artifact per identity, mandatory
watermark, denylist enforcement, a per-tenant allowlist, an abuse-risk
scorecard, and a revocation cascade (`voice-cloned-controls.ts`). Quality
scoring (naturalness, prosody coherence, pace adherence, silence density,
emotional-modulation appropriateness) is measured per language against
baselines; below-baseline blocks release (`voice-quality-extended.ts`,
`eval-coherence.ts`). The provider fallback chain has its own validation and
tests in `voice-fallback-chain.ts`.

## Realism, Impersonation, and Trust Gates

These gates encode "non-deceptive realism" and "non-impersonation" as concrete,
ranked policy tables rather than prose.

### Realism tiers and policy classes

`realism-impersonation-thresholds.ts` defines ordered tiers. Avatar photorealism
runs `illustrative` → `stylised-3d` → `semirealistic-3d` → `photoreal-generic` →
`photoreal-specific` (ranks 0–4). Voice naturalness runs `flat-tts` →
`warm-synthetic` → `naturalistic` → `cloned-consented` → `cloned-unconsented`
(ranks 0–4). The **universal prohibition** is explicit:
`VOICE_PROHIBITED_TIERS = ['cloned-unconsented']` — cloning a specific real
voice without consent is always impermissible, enforced regardless of policy
class. Per-policy-class tables (`PER_POLICY_CLASS_AVATAR_REALISM_POLICIES`,
`PER_POLICY_CLASS_VOICE_REALISM_POLICIES`,
`PER_POLICY_CLASS_IMPERSONATION_POLICIES`) cap the maximum tier a persona class
may reach, and `decideAvatarRealism` / `decideVoiceRealism` /
`decideNonImpersonation` turn a measurement into an allow/block decision against
that ceiling.

### Prompt-time impersonation detection

`prompt-impersonation-detection.ts` implements the "prompt-time
impersonation-attempt detection" trust gate as real regex matchers, not a
placeholder. `PROMPT_IMPERSONATION_PATTERNS` catches phrasings like _"pretend
you are [NAME]"_, _"act as the real [NAME]"_, _"from now on you are…"_, _"this
is the real [NAME]"_, _"channel [NAME]"_, and prompt-injection markers (_"ignore
your instructions / system prompt"_). `detectPromptImpersonationAttempt` returns
a `PromptImpersonationDetectionResult` with hits and a
`PromptImpersonationRiskLevel` of `none`/`low`/`moderate`/`high`/`block`.

### Disclosure visibility — measured, not asserted

The "disclosure visibility ≥ N time-on-screen" trust gate is implemented
concretely across three modules:

- `disclosure-visibility-measurement.ts` defines `DisclosureVisibilityFloors`
  and per-surface defaults (`DISCLOSURE_VISIBILITY_FLOORS_PER_SURFACE`). The
  **customer** surface is strictest: `minTotalVisibleMs` 30,000 (30s
  cumulative), `minSessionVisibleFraction` 0.85, `minFontPixelHeight` 14px,
  `minContrastRatio` 4.5 (WCAG AA), `maxOcclusionFraction` 0.05.
  `studio-preview` and `admin` relax these (admin: 10s, 0.6 fraction, 12px, 3.0
  contrast, 0.1 occlusion). `evaluateDisclosureVisibilityMeasurement` turns a
  sampled `DisclosureVisibilityMeasurement` into a pass/fail verdict.
- `token-time-disclosure-filter.ts` compiles per-persona ban tables
  (`compilePersonaBanTable`, `CompiledPersonaBanTable`) and feeds streaming
  tokens through them, emitting a `TokenTimeBanIncidentEnvelope` when phrasing
  crosses a banned line.
- `disclosure-axis-drift-detector.ts` watches the disclosure axis over time
  (`evaluateDisclosureDrift` against `DISCLOSURE_DRIFT_THRESHOLDS` and a
  `DisclosureDriftBaseline`) so a regression in synthetic-marker visibility is
  caught rather than silently shipped.

For audio-only contexts the `requireSpokenSynthesisDisclosure` flag on the voice
profile carries the "voice-only intro disclosure" requirement. Brand-consistency
checks live in `brand-style-consistency.ts` (off-brand renderings blocked when
brand-binding metadata is required).

### Watermark provenance

`watermark-provenance.ts` enumerates audio (`WATERMARK_AUDIO_ALGORITHMS`) and
video (`WATERMARK_VIDEO_ALGORITHMS`) algorithms and a set of
`PROVENANCE_SCENARIOS`, each mapped to a `ProvenanceRule` in `PROVENANCE_RULES`.
`checkProvenanceCompliance` validates a `ProvenanceClaim` against the scenario
rule and returns a `ProvenanceComplianceResult`, with
`validateProvenanceRule(s)` guarding the rule table itself.

## Consent — the ConsentLedger primitive

The real consent primitive is the **`ConsentLedger`** (`consent-ledger.ts:113`),
not a generic "consent record" (there is no type named `ConsentRecord` in the
registry — where `V1/ARCHITECTURE.md` says personas "bind a `ConsentRecord`,"
the accurate primitive is `ConsentLedger`). A ledger holds `ConsentArtifact`s
indexed `byKind`, where `CONSENT_ARTIFACT_KINDS` are `cloned-voice-release`,
`avatar-likeness-release`, `published-name-consent`, `lineage-release`,
`publicity-waiver`, `minor-guardian-consent`, `talent-license`, and
`rights-transfer`. Each artifact carries a `licensor`, signing timestamp and
signer, an optional `expiresAtUnixSeconds`, a `ConsentArtifactScope` (which
surfaces and modalities it covers and whether commercial use is permitted), and
an optional `ConsentRevocation` with a typed `ConsentRevocationReason` (e.g.
`licensor-request`, `incident-response`, `legal-demand`, `subject-death`,
`minor-aged-out`).

The runtime gate `authorisePersonaConsent` is the verify operation: given the
required kinds, the requested `surface`, `modality`, and `commercialUse` flag,
and the persona's ledger, it returns an `authorised` verdict plus per-kind
`findings`. A kind passes only if the ledger holds at least one artifact for
that persona that is **not revoked**, **not expired** for `nowUnixSeconds`, and
whose scope **covers** the requested surface, modality, and commercial-use flag
— otherwise it reports the most informative failure (`missing` / `expired` /
`revoked` / `scope-mismatch`). This is how a revocation cascade actually bites
at render time: revoke the artifact and the next authorization fails closed.

## Lifecycle, Rehearsal, and Release Gates

Beyond the per-record status field, the registry assembles the _actual launch
cast_ — richer than generic "release signoff" prose:

- **`launch-roster.ts`** — the canonical `LAUNCH_ROSTER` of `LaunchRosterEntry`
  rows: one persona per family, each with `familyId`, derived `audience`
  (`customer`/`operator`), `launchSurfaces`, a release `wave`
  (`wave-1`/`wave-2`/`wave-3`, where wave-1 is the minimum viable launch), and a
  `premiumCast` flag set when the persona uses a real person's likeness,
  lineage, or name. Lookups: `listLaunchRoster`, `findLaunchEntryByPersonaId`,
  `listLaunchEntriesByFamily/ByWave/ByAudience`, all validated by
  `validateLaunchRoster`.
- **`launch-multimodal-assignments.ts`** — `LAUNCH_MULTIMODAL_ASSIGNMENTS` maps
  each launch persona to its concrete voice profile and avatar pack;
  `listLaunchVoiceProfiles` / `listLaunchAvatarPacks` materialize the assigned
  media, and `validateAllLaunchMultimodalAssignments` checks coherence across
  the cast.
- **`launch-persona-config.ts`** — `LaunchPersonaProductionConfig` ties together
  the prompt config (`PERSONA_STYLE_DIRECTIVES`), policy overrides, grounding
  config (`PERSONA_CITATION_POSTURES`), memory config (`PERSONA_MEMORY_SCOPES`,
  `PERSONA_MEMORY_MODES` of `durable`/`ephemeral`/`suppressed`,
  `PERSONA_SENSITIVE_CATEGORIES`), disclosure config, and entitlement config —
  the per-persona production wiring.
- **`launch-family-signoff.ts`** — the multi-party signoff engine:
  `evaluateLaunchPersonaSignoff` per persona, `evaluateLaunchFamilySignoff` per
  family, and `evaluateAllLaunchFamiliesSignoff` for the whole cast, each
  requiring captured signoff and a `LaunchRollbackPlanEvidence` rollback plan,
  with `summarizeLaunchSignoff` rolling the verdict up. The
  `launch-family-signoff.e2e.test.ts` and `admin-approval-rollback.e2e.test.ts`
  exercise the full signoff and rollback paths end-to-end.

Rehearsal fixtures (`rehearsal-fixture-categories.ts`, `rehearsal-qa.ts`) cover
tone, lineage, crisis, unsafe-claim, voice quality, avatar realism, lip-sync,
and expression, capturing results into the release scorecard. QA reviewer roles
(`qa-reviewer-roles.ts`) name the human reviewers (Lilith policy reviewer,
persona ops, voice ops, avatar ops, brand, accessibility). Memory scope
(`persona-memory-scope-policy.ts`), escalation (`persona-escalation-policy.ts`),
and handoff (`persona-handoff-policy.ts`) are declared and enforced per persona,
including when to break persona for a crisis or refer to an operator. The
customer-facing picker and disclosure-at-selection live in
`customer-persona-browse.ts` and `customer-selection-disclosure-gate.ts`; the
operator/admin catalog in `operator-persona-catalog.ts`.

## Evaluation Suites

The features prose describes the eval suites; the code goes further — each is an
individually implemented and tested module. The roughly fourteen suites and
drift detectors in `persona-registry/src`:

| Module                                  | What it evaluates                                                                                                                                                                                      |
| --------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `eval-lipsync-alignment.ts`             | Per-locale alignment against ground-truth audio/video pairs across `LIPSYNC_ALIGNMENT_AXES`; `LIPSYNC_ALIGNMENT_DEFAULT_THRESHOLD = 0.85`, with `LIPSYNC_ALIGNMENT_HARD_FAIL_AXES` enforced at `≥ 0.9` |
| `eval-coherence.ts`                     | Voice/content coherence and naturalness                                                                                                                                                                |
| `eval-expression-quality.ts`            | Emotion ↔ content alignment, micro-expression naturalness                                                                                                                                              |
| `eval-multimodal-identity-coherence.ts` | Consistency of identity across text, voice, and avatar                                                                                                                                                 |
| `eval-deceptive-realism-risk.ts`        | Whether realism crosses into deception                                                                                                                                                                 |
| `eval-impersonation-risk.ts`            | `evaluateImpersonationRisk` composite over the identity-protection signals                                                                                                                             |
| `eval-cloned-voice-red-team.ts`         | Adversarial impersonation / identity-breach prompts against cloned voices                                                                                                                              |
| `eval-disclosure-visibility.ts`         | Synthetic identifier rendered, readable, persistent                                                                                                                                                    |
| `eval-style-consistency-drift.ts`       | Drift in persona style consistency over time                                                                                                                                                           |
| `disclosure-axis-drift-detector.ts`     | Disclosure-visibility regression detection                                                                                                                                                             |

Tone evals run per-persona/per-Lilith-rubric
(`persona-tone-calibration-rubric.ts`); lineage evals per-tradition attribution
and boundary respect (the `comparative` role being the only one allowed to cross
lineages, and required to name each). Expression-coherence evals run against
`expression-emotion-content-pairs.ts`, and lip-sync evals against
`lipsync-ground-truth-pairs.ts`. The contract is **release-blocking**: any eval
below baseline blocks release, and a rollback plan must be declared and tested.
Note that the _runtime media_ the lip-sync and cloned-voice suites would score —
real rendered avatar frames and real synthesized cloned-voice audio — are the
provider-gated boundary; the suites are implemented against their input
envelopes, with the live rendering/TTS exercised by the provider integrations
rather than by the registry.

## Personas as Embodiments Across Domains — the Aja Bridge

Personas are not only voices and faces in chat; in embodied-instruction domains
they front a movement substrate. The Aja adapter (`@oshun/embodiment-aja`,
`libs/oshun/embodiment-aja`) is the real bridge. `adapter.ts` declares
`SUPPORTED_DOMAINS = ['fitness', 'yoga', 'dance', 'martial-arts', 'sports', 'rehabilitation']`
and
`AjaEmbodiedInstructionConsumer = 'metis' | 'assistant' | 'studio' | 'admin'`,
importing its contracts from `@oshun/contracts/aja` (`libs/contracts/src/aja`,
which ships `embodied-instruction.ts`, `jobs.ts`, `primitives.ts`, and
`formats.ts`). The package metadata is explicit that Aja is a **substrate, not a
shell peer** (`productPosition: 'substrate_not_shell_peer'`), surfacing through
Metis and the assistant — a persona-led demonstration/coaching-overlay flow with
health and availability typed throughout. The package exports `./adapter` and
`./canonical-adapter`.

## The Studio-Facing Embodiment Substrates

The registry references several large media subsystems as _studio-only_
substrates. An accuracy note for the architecture doc: these are not single
libraries — each is a **multi-package collection** under its own npm scope (e.g.
`@themis/academic-integrity`), and the single-line adapter rows in
`V1/ARCHITECTURE.md` understate them:

| Subsystem | Scale (top-level libs)                         | Examples                                                                                                    |
| --------- | ---------------------------------------------- | ----------------------------------------------------------------------------------------------------------- |
| Bellona   | ~40 sub-libs                                   | `maya`, `openusd`, `davinci`, `blender`, `godot`, `metahuman`, `mocap`, …                                   |
| Neith     | ~71 sub-libs                                   | `audio-runtime`, `animator`, `inverse-modeling`, …                                                          |
| Themis    | ~71 sub-packages (no top-level `package.json`) | `@themis/academic-integrity`, …                                                                             |
| Hathor    | ~18 sub-libs                                   | `characters`, `quests`, `lore-compiler`, `narrative-generation`, `simulation`, …                            |
| Yemaya    | ~60 top-level module dirs                      | `sdk`, `case-*` detective pipeline, `comfyui-integration`, `asset-generation`, `av-sync`, `blend-kernel`, … |

These are confirmed **studio-only**:
`apps/oshun/web/src/app/studio/{bellona,hathor,neith}/` exist with sub-route
trees, and there are **no** top-level customer routes
`apps/oshun/web/src/app/{bellona,hathor,neith,themis}` — and no `studio/themis`
route either. Whether every sub-lib in these trees is uniformly production-grade
versus partially scaffolded is out of scope to fully verify here; the
persona-registry contracts that govern _which_ personas may use _which_
embodiment, and under what consent and disclosure, are the part that is deeply
implemented. (A staleness note: the `V1/ARCHITECTURE.md` Cross-Domain Support
table describes Yemaya only as a "rendering substrate … used through
`libs/yemaya/sdk` and BFF route"; `libs/yemaya/sdk` does exist, but Yemaya is
now a ~60-module domain — that single line undersells its current scope.)

## Edge Cases and Rationale

- **Why deterministic fingerprints?** A persona's behavioral envelope is
  safety-critical. The snapshot fingerprint lets the admin app, a domain BFF,
  and an eval runner _prove_ they are reasoning over the same registry without
  trusting a clock or a deep-equal — a divergence shows up as a fingerprint
  mismatch, not a subtle behavioral drift.
- **Why a `cloned-unconsented` tier that is always banned?** Modeling the
  impermissible tier explicitly (rather than omitting it) means the validator
  can hard-bar it regardless of policy class and the red-team suite can assert
  that no path reaches it.
- **Why force watermark + C2PA on commercial avatar packs?** Commercial reuse is
  exactly where provenance and synthetic-disclosure obligations are strongest;
  coupling them in `validateAvatarPack` makes a non-watermarked commercial pack
  un-shippable by construction.
- **Why a consent ledger keyed by kind with scope coverage?** A single talent
  might consent to voice on customer surfaces but not admin previews, for
  non-commercial use only. Scope-coverage checking turns those nuances into
  render-time allow/deny decisions and makes revocation a real cascade rather
  than a metadata annotation.
- **Honest boundary.** The registry, role catalog, consent ledger,
  realism/impersonation tables, disclosure gates, eval suites, launch assembly,
  and signoff flow are implemented and tested. Live avatar rendering against
  ground-truth video and live cloned-voice TTS output go through provider
  integrations (ElevenLabs + local engines) that this page does not claim to
  have exercised end-to-end — they are provider-gated, and the registry's job is
  to govern, watermark, disclose, and gate them, not to render them itself.

## Related

- [Lilith Persona Policy](./lilith-persona-policy.md) — the seven roles, tone
  bands, capability ceilings, and the policy pack a persona binds to
- [Iris Memory and Identity](./iris-memory-identity.md) — per-persona memory
  scope and boundary enforcement
- [Isis Generation Control](./isis-generation-control.md) — the release gate for
  voiced/avatar personas
- [Generation Audience Tiers and Surface Boundaries](./generation-tiers-and-surfaces.md)
  — surface and entitlement gating
- [External Model Intelligence and Execution Providers](./external-models-and-execution.md)
  — the swappable voice/TTS providers
- [Sophia Grounding](./sophia-grounding.md) — grounding-fidelity scorecards for
  pedagogical personas
- [Creator Surfaces, Voice, Music, and 3D Generation](./creator-voice-music-3d.md)
  — the studio embodiment substrates (Bellona, Neith, Yemaya, …)
- [Mentor Presence — Meditating with a Master](./mentor-presence.md) — the
  embodied-mentor experience that consumes these avatar/voice/consent contracts
  in the meditation player (§36; implemented, release-gated)
- [Privacy, Consent, Data Portability, and User Controls](./privacy-consent-portability.md)
  — consent at the platform level
- [Subsystem Glossary](./glossary.md) — definitions for Aja, Lilith, Isis, and
  the studio substrates
- The features hub: [../features.md](../features.md)
