---
path: /lilith-studio/personas
surface: customer
domain: lilith
auth: signed-in + role:v1_editorial_producer (default)
source: apps/oshun/web/src/app/lilith-studio/personas/page.tsx
status: walked
last_walked: '2026-05-29 automated runtime walk (Playwright headless) — render,
  /v1 data (2xx), console/page-errors, expected content, screenshot verified;
  live screen-reader, touch, offline, and telemetry-delivery checks pending a
  manual AT pass. Evidence: WALKTHROUGH/results/runtime-sweep-2026-05-29.md;
  content re-verified 2026-06-03 against current source

  2026-06-28 focused automated Playwright + component walk — route hydration,
  seeded draft, validation acknowledgement, all identity inputs, genre
  guardrails, catalog controls, clear/public-figure voice build branches,
  matched and no-match hand-off runtime states, add/edit/remove hand-off rule
  rows, publish-ready/blocked/published states, manifest readouts, provenance
  inspector, shell entry, and mobile no-horizontal-overflow check verified.
  Evidence: apps/oshun/web/e2e/lilith-saraswati-persona-dossier-smoke.spec.ts;
  apps/oshun/web/src/app/lilith-studio/personas/SaraswatiPersonaDossierEditor.test.tsx'
---

# Lilith Studio · Saraswati persona dossier editor

## Purpose

Publish a complete Saraswati artist persona only when backstory, voice
signature, avatar, genre cell, catalog policy, and operator hand-off rules all
pass validation. Backed by `@oshun/tenant-saraswati-stage`
(`createSaraswatiPersonaDossierEditorState`, voice-signature build / review,
operator hand-off enforcement, dossier publish). The page
(`apps/oshun/web/src/app/lilith-studio/personas/page.tsx`) returns
`<SaraswatiPersonaDossierEditor />` directly; the
`<ShellLayout active="studio">` wrapper and the editorial scope gate are
supplied by the route's `apps/oshun/web/src/app/lilith-studio/layout.tsx`.

## Entry points

- **Lilith Studio shell** (`/lilith-studio`) — "Persona dossiers" surface tab →
  "Open Persona dossiers" link
- **Direct URL / bookmark** — yes (signed-in)

## Layout regions

`SaraswatiPersonaDossierEditor`
(`apps/oshun/web/src/app/lilith-studio/personas/SaraswatiPersonaDossierEditor.tsx`):

- **Header**: eyebrow "Saraswati editorial", title "Persona dossier editor",
  lede, two header actions — "Studio shell" → `/lilith-studio` and "Validate
  dossier" button (`data-saraswati-persona-validate`)
- **Multi-panel grid**:
  - **Identity panel** (`aria-labelledby="persona-identity-title"`):
    - Persona id input (`data-saraswati-persona-id`)
    - Display name input (`data-saraswati-persona-display-name`)
    - Backstory summary textarea (`data-saraswati-persona-backstory`)
    - Sophia citation ids input (comma-separated;
      `data-saraswati-persona-citations`)
    - Cultural review ref (`data-saraswati-persona-cultural-review`)
    - Rights review ref (`data-saraswati-persona-rights-review`)
    - Voice signature ref (`data-saraswati-persona-voice-signature`)
    - Avatar ref (`data-saraswati-persona-avatar-ref`)
    - Genre cell select (`data-saraswati-persona-genre-cell`); options from
      `SARASWATI_PERSONA_GENRE_CELLS`
    - Genre guardrail list (`aria-label="Genre guardrails"`) — read-only per
      `cell.id`
  - **Catalog policy panel**:
    - Release cadence select (`data-saraswati-persona-release-cadence`) —
      options: Single monthly / EP quarterly / Album at GA
    - Track count input (`data-saraswati-persona-track-count`)
    - Remix rights mode select (`data-saraswati-persona-remix-rights`) —
      Editorial only / Licensed community remix / Blocked remix
    - Distribution mode select (`data-saraswati-persona-distribution-mode`) —
      In-world only / In-world plus DSP / Concert exclusive window
    - Provenance required toggle (`data-saraswati-persona-provenance-required`)
    - Explicit content toggle (`data-saraswati-persona-explicit-content`)
  - **Voice signature build panel** — voice probe controls and receipt textarea
    (`aria-label="Voice signature build receipt"`); public-figure match probe
    option vs "clear" build
  - **Voice ownership clauses list**
    (`aria-label="Voice ownership contract clauses"`)
  - **Operator hand-off rules panel**:
    - "Add rule" button (`data-saraswati-persona-handoff-add`)
    - Runtime prompt input (`data-saraswati-persona-handoff-runtime-prompt`);
      default copy "A fan asks for medical advice in persona voice before the
      listening room opens."
    - Runtime decision result block
      (`data-saraswati-persona-handoff-runtime-result`)
    - Rule grid (`data-saraswati-persona-handoff-rule-list`); per rule: Remove
      icon (`aria-label="Remove <label>"`), Rule ID, Label, Trigger, Scope
      select, Severity select (Routine / Priority / Urgent), SLA input, Queue
      input
  - **Validation panel**: empty state (`data-saraswati-persona-no-issues`) or
    issue list (`data-saraswati-persona-issue-list`)
  - **Publish controls**:
    - "Publish dossier" button (`data-saraswati-persona-publish`)
    - Publish receipt textarea (`data-saraswati-persona-publish-manifest`,
      `aria-label="Persona dossier publish receipt"`)
- **`LilithProvenanceInspector`** — surface ID `persona-dossier-editor`

## States

- [x] **Hydrated route** — client component reaches `data-hydrated="true"` after
      mount in the real browser
- [x] **Seeded draft** — `SARASWATI_PERSONA_DOSSIER_SEED` populates fields
      including persona id `persona-saraswati-anaya-veil`
- [x] **Validation pending** — `validationAcknowledged === false`
- [x] **Validation acknowledged** — pressing "Validate dossier" sets the flag
      (consumed by other UI sections)
- [x] **Voice build clear** — `voiceProbeMode='clear'`
- [x] **Voice build public-figure match** —
      `voiceProbeMode='public-figure-match'`; probe rejection appears
- [x] **Hand-off rule matched** — `data-handoff-runtime-state` reflects match;
      `primaryMatch.scope` populates `data-handoff-runtime-scope`
- [x] **Hand-off rule not matched** — `data-handoff-runtime-state="no-match"`;
      `data-handoff-runtime-scope="none"`
- [x] **Publish blocked** — `editorState` reports issues; publish button
      disabled
- [x] **Publish ready** — all required fields valid
- [x] **Published** — `publishReceipt` set; manifest textarea populated
- [ ] **Offline** — client-only; works offline

## Interactions

### Header

- [x] **"Studio shell" link** — href `/lilith-studio`
- [x] **"Validate dossier" button** (`data-saraswati-persona-validate`)
  - Function: `setValidationAcknowledged(true)`

### Identity panel

- [x] **Persona id input**
- [x] **Display name input**
- [x] **Backstory summary textarea**
- [x] **Sophia citation ids input** (comma-separated → list)
- [x] **Cultural review ref input**
- [x] **Rights review ref input**
- [x] **Voice signature ref input**
- [x] **Avatar ref input**
- [x] **Genre cell select**
- [x] **Genre guardrail list** — read-only per genre cell

### Catalog policy panel

- [x] **Release cadence select**
- [x] **Track count input**
- [x] **Remix rights mode select**
- [x] **Distribution mode select**
- [x] **Provenance required toggle**
- [x] **Explicit content toggle**

### Voice signature build

- [x] **Voice probe mode toggle** (Clear / Public-figure match)
- [x] **Build voice signature button** — submits `voiceBuildInput` to
      `buildSaraswatiVoiceSignature`
- [x] **Voice receipt textarea** (read-only,
      `aria-label="Voice signature build receipt"`)

### Operator hand-off rules

- [x] **"Add rule" button** (`data-saraswati-persona-handoff-add`)
- [x] **Runtime prompt input** (textarea / input)
- [x] **Runtime decision result block**
- [x] **Each rule row**:
  - Remove icon button (`aria-label="Remove <label>"`)
  - Rule ID, Label, Trigger, Scope, Severity, SLA, Queue editable fields

### Validation

- [x] **No-issues empty state** (`data-saraswati-persona-no-issues`)
- [x] **Issue list** (`data-saraswati-persona-issue-list`) — per-issue detail
      rows

### Publish

- [x] **"Publish dossier" button** (`data-saraswati-persona-publish`, release
      button)
  - Function: `publishSaraswatiPersonaDossier(...)`
  - Disabled when validation reports issues
- [x] **Publish receipt textarea** (read-only)

### Provenance inspector

- [x] **`LilithProvenanceInspector`** — read its own walkthrough

## Data & contracts

- **Reads / writes**: in-process via `@oshun/tenant-saraswati-stage`:
  `SARASWATI_PERSONA_DOSSIER_REQUIRED_FIELDS`, `SARASWATI_PERSONA_DOSSIER_SEED`,
  `SARASWATI_PERSONA_GENRE_CELLS`, `SARASWATI_VOICE_RIGHTS_OWNERSHIP_SUMMARY`,
  `SARASWATI_VOICE_SIGNATURE_BUILD_SEED`,
  `SARASWATI_VOICE_SIGNATURE_PUBLIC_FIGURE_MATCH_PROBE`, plus the family of
  `*Saraswati*` mutators/getters
- **Realtime**: none
- **Caching**: client bundle
- **Auth/role check**: inherited from `lilith-studio/layout.tsx`, requiring
  `admin:*` or `studio:editorial`
- **Validation rules**:
  - Persona id must match `persona-saraswati-*`
  - Display name needs at least two words
  - Backstory needs at least 35 words, at least two `sophia:saraswati:*`
    citations, and cultural/rights review refs
  - Voice signature must point to `voice-sig:consent-ledger:*`; avatar must
    point to `avatar:saraswati:*`
  - Catalog policy requires a valid cadence, remix-rights mode, distribution
    mode, at least 12 debut tracks, and required provenance bundle
  - At least two operator hand-off rules are required; each rule needs valid
    `handoff-*` id, two-word label, specific trigger, supported scope/severity,
    `lilith-*` queue, and 1-60 minute SLA

## Cross-references

- Studio shell: [`lilith-studio.md`](./lilith-studio.md)
- Concert pipeline (consumes the persona):
  [`lilith-studio-concerts.md`](./lilith-studio-concerts.md)
- Sibling editors: [`lilith-studio-asana.md`](./lilith-studio-asana.md),
  [`lilith-studio-avatar-costume.md`](./lilith-studio-avatar-costume.md),
  [`lilith-studio-music.md`](./lilith-studio-music.md),
  [`lilith-studio-scenes.md`](./lilith-studio-scenes.md),
  [`lilith-studio-provenance.md`](./lilith-studio-provenance.md)
- Component source:
  `apps/oshun/web/src/app/lilith-studio/personas/SaraswatiPersonaDossierEditor.tsx`
- E2E/unit evidence:
  - `apps/oshun/web/e2e/lilith-saraswati-persona-dossier-smoke.spec.ts`
  - `apps/oshun/web/src/app/lilith-studio/personas/SaraswatiPersonaDossierEditor.test.tsx`

## Open questions / known gaps

- [x] Document current publish persistence — `publishSaraswatiPersonaDossier`
      returns an in-process receipt and does not write to a back-end in the
      current V3 tenant package
- [x] Document the full validation rules (which inputs gate publish)
- [x] Verify the `voice-signature` public-figure match probe — it rejects
      impersonation by policy; cross-reference Lilith voice-quality policy
      ([`V1/features.md#lilith-persona-policy`](../../../V1/features.md#lilith-persona-policy))
- [ ] Cold offline behavior for `/lilith-studio/personas` remains unproven
      beyond client-only implementation review
