---
path: /lilith-studio/scenes
surface: customer
domain: lilith
auth: signed-in + role:v1_editorial_producer (default)
source: apps/oshun/web/src/app/lilith-studio/scenes/page.tsx
status: walked + e2e-covered
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-07-01 deep browser
  coverage added in apps/oshun/web/e2e/lilith-scene-editor-deep.spec.ts for
  every editor control, drag/drop, graph selection, provenance selection,
  publish readiness, real-BFF publish, draft reset, real-service-worker offline
  replay, and mobile standalone overflow.'
---

# Lilith Studio · Tara venue authoring

## Purpose

Author Tara venue scenes — place V1 library assets, tune lighting, select a
rights-cleared sound bed, bind room interactions, simulate a 4,096-attendee
stadium load, pass color/motion/caption accessibility checks, and publish
through the V1 editorial release branch. The page
(`apps/oshun/web/src/app/lilith-studio/scenes/page.tsx`) returns
`<TaraSceneEditor />` 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`) — "Scene authoring" surface tab →
  "Open Scene authoring" link
- **Direct URL / bookmark** — yes (signed-in)

## Layout regions

`TaraSceneEditor`
(`apps/oshun/web/src/app/lilith-studio/scenes/TaraSceneEditor.tsx`):

- **Header**: eyebrow "Lilith Studio / Scene editor", title "Tara venue
  authoring", lede, two header actions — "Lilith shell" → `/lilith-studio`, "V1
  workflow" → `/studio/review-approval-workflows`
- **Three-column grid**:
  - **Asset lane** (`aria-label="Scene asset library"`):
    - Panel header: "Asset tray" + "Drag assets into a venue zone or place them
      in the selected zone."
    - Asset list — four scene assets from `TARA_SCENE_EDITOR_ASSETS`:
      cedar-altar, lotus-canopy, lantern-path, chime-cluster — each with a
      draggable grip button (`data-testid="lilith-scene-asset-<id>"`,
      `data-lilith-scene-asset=<id>`) and a "Place in selected zone" inline
      action button (`data-lilith-scene-place-asset=<id>`)
  - **Stage panel** (`<main>`):
    - Stage header: "Tara garden venue", selected zone label, status pill
      (`data-lilith-scene-release-state`) — "Draft branch" or "Release edition
      published"
    - **Stage** (`data-testid="lilith-scene-stage"`) — four zone buttons from
      `TARA_SCENE_EDITOR_ZONES`: threshold, mandala, north-grove, sound-corner —
      each `data-testid="lilith-scene-zone-<id>"` with anchor, tone, and
      placement list ("Drop asset here" when empty)
    - **Scene graph panel** (`data-lilith-scene-graph-editor`): node count /
      edge count summary; node button list (`aria-label="Scene graph nodes"`,
      `data-lilith-scene-graph-node=<id>`); detail panel with selected node's
      scope and edges in/out
  - **Control lane** (`aria-label="Scene controls"`):
    - **Lighting** (`data-lilith-scene-lighting-panel`): kelvin/intensity
      readout; segmented buttons for `TARA_SCENE_EDITOR_LIGHTING`
      (`data-lilith-scene-lighting-preset=<id>`) — dawn-soft, twilight- amber,
      moonlit-blue
    - **Audio** (`data-lilith-scene-audio-panel`): rights/loudness readout;
      segmented buttons for rights-cleared `TARA_SCENE_EDITOR_ AUDIO` (none
      filtered out) — forest-drone, singing-bowl, rain- texture
      (`data-lilith-scene-audio-bed=<id>`)
    - **Interaction binder** (`data-lilith-scene-interaction-binder`): Trigger
      select (`data-lilith-scene-trigger-select`, options
      `INTERACTION_TRIGGERS`: approach-altar, touch-chimes, enter-grove), Action
      select (`data-lilith-scene-action-select`, options `INTERACTION_ACTIONS`:
      open-breath-primer, play-mantra-preview, dim-room), "Add binding" primary
      button (`data-lilith-scene-add-binding`), binding list
      (`data-lilith-scene-binding-list`)
    - **Capacity and sharding** (`data-lilith-scene-capacity-panel`): Capacity
      tier segmented (`TARA_SCENE_CAPACITY_TIERS`: studio, salon, festival,
      stadium; `data-lilith-scene-capacity-tier=<id>`), Sharding profile
      segmented (`TARA_SCENE_SHARDING_PROFILES`: single-room, regional-fanout,
      interest-management; `data-lilith-scene-sharding-profile=<id>`), "Run
      4,096 attendee simulation" primary button
      (`data-lilith-scene-run-attendee-simulation`), simulation stats `<dl>`
      (Accepted / Shards / Interest cells;
      `data-lilith-scene-attendee-simulation`, `data-simulation-state`)
    - **Accessibility checker** (`data-lilith-scene-accessibility- checker`):
      color profile segmented (`TARA_SCENE_COLOR_PROFILES`: low-contrast-dusk,
      wcag-aa-cedar), motion segmented (`TARA_SCENE_MOTION_PROFILES`:
      full-sweep, comfort-reduced), caption segmented
      (`TARA_SCENE_CAPTION_PROFILES`: ambient-only, full-spatial-captions),
      check list per `id` (color / motion / caption) with `data-check-passed`,
      remediation list
- **Release panel** (`aria-label="Scene publish controls"`):
  - Readiness grid: Focal asset placed / Lighting preset selected /
    Rights-cleared audio selected / Interaction binding added / Stadium
    simulation passed / Accessibility checker passed
  - "Publish Tara venue" release button (`data-lilith-scene-publish`)
  - Publish status (`data-lilith-scene-publish-status`)
  - Publish manifest textarea (read-only, `data-lilith-scene-publish-manifest`)
- **`LilithProvenanceInspector`** — surface ID `tara-scene-editor`, assets =
  `TARA_SCENE_PROVENANCE_ASSETS`

## States

- [x] **Loading** — client component; no route-local skeleton state. Browser
      coverage waits for the hydrated editor after the signed-in layout gate.
- [x] **Draft (default)** — `publishState='draft'`, `data-publish-ready` depends
      on readiness flags
- [x] **Asset placed** — `placements.length > 0`; deep coverage places all four
      assets and asserts the editor placement count.
- [x] **Zone selected** — `selectedZoneId` updates; `data-selected="true"` on
      the zone
- [x] **Asset dragged into zone** — drop handler adds placement; browser test
      dispatches a real `DataTransfer` drag/drop from `cedar-altar` to
      `mandala`.
- [x] **Interaction binding added** — binding list grows
- [x] **Stadium tier + interest-management profile + simulation run** —
      `data-interest-management-ready="true"`
- [x] **Accessibility passing** — `data-accessibility-ready="true"`
- [x] **Publish blocked** — `canPublish === false`
- [x] **Publish ready** — `canPublish === true`
- [x] **Published** — `data-publish-state="published"` after the real
      `/v1/admin/studio/scenes/publish` BFF route returns the persisted publish
      id and release stream
- [x] **Any change after publish** — returns to draft
      (`setPublishState('draft')`)
- [x] **Offline** — `/lilith-studio/scenes` is an exact service-worker shell
      route; focused coverage warms the real `sw.js` cache and replays the
      editor offline without shell fallback.

## Interactions

### Header

- [x] **"Lilith shell" link** — href `/lilith-studio`
- [x] **"V1 workflow" link** — href `/studio/review-approval-workflows`

### Asset lane

- [x] **Each asset drag handle** (4 — cedar-altar, lotus-canopy, lantern-path,
      chime-cluster)
- [x] **Each "Place in selected zone" inline action button**

### Stage / zones (4 buttons)

- [x] **threshold zone** (`data-lilith-scene-zone="threshold"`)
- [x] **mandala zone**
- [x] **north-grove zone**
- [x] **sound-corner zone**
- [x] **Drop handler** — accepts dragged asset and calls
      `placeAsset(assetId, zoneId)`

### Scene graph

- [x] **Each node button** — sets `selectedGraphNodeId`
- [x] **Edge list** (read-only)

### Lighting

- [x] **Three preset buttons** (dawn-soft, twilight-amber, moonlit-blue)

### Audio

- [x] **Three sound-bed buttons** (forest-drone, singing-bowl, rain-texture)

### Interaction binder

- [x] **Trigger select**
- [x] **Action select**
- [x] **"Add binding" button** (primary)
- [x] **Binding list** (read-only)

### Capacity and sharding

- [x] **Four capacity tier buttons** (studio, salon, festival, stadium)
- [x] **Three sharding profile buttons** (single-room, regional-fanout,
      interest-management)
- [x] **"Run 4,096 attendee simulation" button**
  - Function: `runAttendanceSimulation()` populates accepted attendees, shard
    count, interest cells

### Accessibility checker

- [x] **Two color profile buttons**
- [x] **Two motion profile buttons**
- [x] **Two caption profile buttons**
- [x] **Check results list** — per check with pass/fail
- [x] **Remediation list** — empty state "Accessibility checks passed for scene
      publish." when none

### Release panel

- [x] **Six readiness rows** — read-only
- [x] **"Publish Tara venue" release button** (`data-lilith-scene-publish`)
  - Function: POSTs to `/v1/admin/studio/scenes/publish`; sets
    `publishState='published'` only after the BFF returns a publish id and
    release stream.
  - Disabled when `canPublish === false`
- [x] **Publish manifest textarea** (read-only)

### Provenance inspector

- [x] **`LilithProvenanceInspector`** — deep coverage asserts asset count,
      selects the singing-bowl audio provenance row, and verifies the bundle
      route contract.

## Data & contracts

- **Reads**: in-file constants `TARA_SCENE_EDITOR_ASSETS`,
  `TARA_SCENE_EDITOR_ZONES`, `TARA_SCENE_EDITOR_LIGHTING`,
  `TARA_SCENE_EDITOR_AUDIO`, `INTERACTION_TRIGGERS`, `INTERACTION_ACTIONS`,
  `TARA_SCENE_CAPACITY_TIERS`, `TARA_SCENE_SHARDING_PROFILES`,
  `TARA_SCENE_COLOR_PROFILES`, `TARA_SCENE_MOTION_PROFILES`,
  `TARA_SCENE_CAPTION_PROFILES`, `TARA_SCENE_PROVENANCE_ASSETS`
- **Writes**: client state for authoring edits; publish POSTs to the real
  `/v1/admin/studio/scenes/publish` BFF route and surfaces the persisted
  `publishId` / `releaseStream` response. The current BFF store is the scoped V1
  release-stream persistence for this route.
- **Realtime**: none
- **Caching**: client bundle plus exact service-worker shell route
  `/lilith-studio/scenes` for warmed offline document replay
- **Auth/role check**: proxy enforces signed-in session; the `/lilith-studio/*`
  layout enforces `studio:editorial` / `admin:*` scope

## Cross-references

- Studio shell: [`lilith-studio.md`](./lilith-studio.md)
- Sibling editors: [`lilith-studio-asana.md`](./lilith-studio-asana.md),
  [`lilith-studio-avatar-costume.md`](./lilith-studio-avatar-costume.md),
  [`lilith-studio-concerts.md`](./lilith-studio-concerts.md),
  [`lilith-studio-music.md`](./lilith-studio-music.md),
  [`lilith-studio-personas.md`](./lilith-studio-personas.md),
  [`lilith-studio-provenance.md`](./lilith-studio-provenance.md)
- Tara hub (the consumer of published venues):
  [`../03-tara/tara.md`](../03-tara/tara.md)
- Component source:
  `apps/oshun/web/src/app/lilith-studio/scenes/TaraSceneEditor.tsx`

## E2E coverage

- `apps/oshun/web/e2e/lilith-scene-editor-deep.spec.ts` — granular browser
  coverage for all header links, assets, zone selection, drag/drop, graph
  selection, lighting/audio controls, binding, capacity/sharding, accessibility,
  readiness rows, provenance selection, real-BFF publish, draft reset, real
  service-worker offline replay, and mobile standalone overflow.
- `apps/oshun/web/e2e/lilith-scene-editor-smoke.spec.ts` — primary Tara venue
  authoring/publish flow plus blocked accessibility, non-interest-management,
  access-denied, and shell-entry checks.
- `apps/oshun/web/e2e/lilith-scene-publish.spec.ts` — direct BFF route contract
  for publish/list/error cases and UI publish persistence through the route.
- `apps/oshun/web/e2e/lilith-studio-shell-smoke.spec.ts` — Lilith Studio shell
  route contract and `/lilith-studio/scenes` advertised surface link.

## Resolved questions

- [x] Drag-and-drop has a keyboard-accessible equivalent: each asset card has a
      keyboard-focusable "Place in selected zone" button, and the focused deep
      spec verifies all four inline placement actions.
- [x] The stadium simulation passes only when capacity is `stadium`, sharding is
      `interest-management`, the simulated attendance is 4,096, shard count is
      within the profile max, at least three assets are placed, and at least one
      interaction binding exists.
- [x] Publish destination is traced to `/v1/admin/studio/scenes/publish` and
      verified against the BFF publish/list contract; this is no longer a
      local-only `setPublishState('published')` transition.
