# Journey: Atelier · motion room → Cinematheque handoff

A signed-in customer opens the Stage of Motion room, reviews the rigged
viewport, verifies the bake/timeline state for take 02, and sends the kept
motion clip forward to Cinematheque. This covers the V1 Atelier motion surface
as a deliberate read-only composition stage: the rendered timeline is
inspectable but not yet editable, draft actions are honest placeholders, and the
downstream handoff is a real route link. As of 2026-06-30 the handoff also
carries an explicit URL-scoped context (`shot=sh-03`, `take=take-02`,
`voice=foundry-take-03`, `light=forms-cream-north`) and Cinematheque renders
that inbound context. This is intentionally not described as durable project
persistence.

## Personas

- **Contemplative customer** — primary actor; enters the Atelier from Explore or
  the Lilith index and wants to inspect a motion clip without entering Studio
- **Returning maker** — checks which take is kept and whether the downstream
  room can continue from the motion plate
- **AAA/studio-bound customer** — verifies that consumer Atelier stays a
  manuscript-style inspection room; production retargeting/new-take creation is
  not exposed here yet

## Pre-conditions

- Customer signed in
- `/atelier/motion` and `/atelier/video` are reachable behind the customer auth
  shell
- `getAtelierMotion()` returns the populated fixture with five timeline tracks
  and three takes
- The route has no local `loading.tsx` or `error.tsx`; those states are captured
  as inherited/absent rather than fabricated

## Steps

### 1. Open Stage of Motion

- [x] Customer navigates directly to `/atelier/motion` or uses the Atelier index
      "XIV · Stage of Motion" card.
- [x] Page server-renders `apps/oshun/web/src/app/atelier/motion/page.tsx`,
      awaits `getAtelierMotion()`, and renders `<AtelierMotion data>`.
- [x] Route root exposes `data-atelier-motion-page`,
      `data-atelier-motion-track-count="5"`,
      `data-atelier-motion-take-count="3"`,
      `data-atelier-motion-current-take="take 02"`,
      `data-atelier-motion-current-frame="74"`, and
      `data-atelier-motion-bake-progress="41"`.
- [x] Plate head reads "The atelier · stage of motion" and "A figure, set in
      motion." with chips "Take 02 · 06s", "Rig · 24 joints", and "Baking ·
      41%".
- [x] Step rail exposes all five steps with step 4 "Baking · keyframes" current
      and step 5 "Plate · clip kept" pending.

### 2. Inspect the stage viewport

- [x] Center stage renders the 16:9 SVG viewport with perspective grid, rigged
      figure, onion-skin echo poses, IK markers, and labels
      `STAGE · PERSP · 38 mm`, `f · 074 / 144`, and
      `RIG · 24 joints · IK on heels`.
- [x] The viewport exposes current frame `074`, total frames `144`, and rig
      joints `24`.
- [x] Viewport click is read-only: it does not rotate, scrub, navigate, or
      change the playhead attributes.

### 3. Inspect the timeline and bake state

- [x] Timeline exposes `data-atelier-motion-frame-count="144"`,
      `data-atelier-motion-duration="06 s"`, and
      `data-atelier-motion-frame-rate="24 fps"`.
- [x] Frame ruler renders 13 labels from `000` to `144`.
- [x] Five fixture tracks render with exact names, curves, and key counts: Spine
      · root / ease / 7 keys; Head · pitch / ease / 5; Hand L · IK / step / 5;
      Hand R · IK / step / 5; Breath · param / sine / 7.
- [x] Frame 72 keyframes expose `data-atelier-motion-keyframe-selected="true"`;
      tracks without frame 72 expose no selected keyframe.
- [x] Playhead remains fixed at `data-atelier-motion-playhead-frame="74"` and
      `data-atelier-motion-playhead-position="51"` after ruler and keyframe
      clicks.
- [x] Bake card shows Rendering 41%, ETA 03:42, "Baking keys → motion clip ·
      take 02", and "59 / 144 frames".

### 4. Review the kept take and downstream note

- [x] Clip panel shows take 02 metadata: length 06.000 s, 144 frames at 24 fps,
      24 joints with IK heels, live actor source, clamped bezier curves, loop
      state, and `usd · alembic · glb` format.
- [x] Three take rows render from fixture data: take 01 archived, take 02 kept
      and selected, take 03 draft.
- [x] Compose downstream panel names Cinematheque (sh 03), foundry voice take
      03, and forms light `cream-north`.
- [x] The panel exposes the machine-readable handoff attributes:
      `data-atelier-motion-handoff-source="motion"`,
      `data-atelier-motion-handoff-state="url-context"`,
      `data-atelier-motion-handoff-shot="sh 03"`,
      `data-atelier-motion-handoff-take="take 02"`,
      `data-atelier-motion-handoff-voice="foundry · take 03"`, and
      `data-atelier-motion-handoff-light="forms · cream-north"`.

### 5. Use available actions

- [x] "New take" and "Re-target rig" render as disabled-placeholder `LBtn`
      controls with `aria-disabled="true"`, `data-placeholder="true"`, and the
      placeholder tooltip; clicking them leaves the customer on
      `/atelier/motion`.
- [x] "Open in Cinematheque" is a real primary link with the handoff URL:
      `/atelier/video?from=motion&shot=sh-03&take=take-02&voice=foundry-take-03&light=forms-cream-north`.
- [x] Activating the link navigates to `/atelier/video`, where Cinematheque
      renders "The atelier · cinematheque" and "A short film, storyboarded."
- [x] Cinematheque renders the inbound "Motion handoff" context panel with
      target shot `sh 03`, motion take `take 02`, voice `foundry · take 03`, and
      light `forms · cream-north`.

### 6. Edge-state checks

- [x] At 390 px width, the layout stacks rail → stage/timeline/bake → clip/takes
      without horizontal overflow.
- [x] Under reduced motion, the motion page subtree exposes zero animations; the
      onion-skin and bake state remain static values.
- [x] After the route has loaded, browser offline mode leaves the stage SVG,
      playhead, and route root visible.

## Post-conditions

- Customer can inspect the kept motion take and understand that take 02 is the
  current downstream clip.
- Customer can route to Cinematheque through the primary action, carrying the
  URL-scoped motion context into the receiving page.
- No fake mutation is implied for new-take, retarget, timeline scrub, or
  viewport rotation; each unbuilt interaction is captured as read-only or
  placeholder.

## Failure modes

- [x] **Timeline misread** — selected frame 72 accents and fixed f.074 playhead
      are asserted from DOM attributes, not inferred from text alone.
- [x] **False affordance** — placeholder buttons expose disabled semantics and
      do not navigate.
- [x] **Mobile overflow** — 390 px browser check fails if the desktop grid leaks
      horizontally.
- [x] **Offline after load** — static route markup remains visible after the
      browser context goes offline.
- [ ] **Route-local load/error** — no local `loading.tsx` or `error.tsx` exists
      yet; inherited app behavior is not a substitute for a room-specific state.
- [x] **URL-scoped handoff** — navigation to `/atelier/video` carries target
      shot, take, voice, and light context, and Cinematheque renders the inbound
      panel from that context.
- [ ] **Durable project-state handoff** — no persisted project-level object is
      stored yet; the current handoff is URL context only.

## Data & contracts

- Route: `apps/oshun/web/src/app/atelier/motion/page.tsx`
- Component: `apps/oshun/web/src/components/lilith/atelier.tsx`
  (`AtelierMotion`)
- Data: `apps/oshun/web/src/lib/lilith-data/atelier-depth.ts`
  (`getAtelierMotionFixture()`)
- Downstream route: `apps/oshun/web/src/app/atelier/video/page.tsx`
- Downstream query contract:
  `from=motion&shot=sh-03&take=take-02&voice=foundry-take-03&light=forms-cream-north`
- Writes: none in the current customer surface
- Realtime: none

## E2E coverage

Spec: `apps/oshun/web/e2e/atelier-motion-to-cinematheque.spec.ts` (4 tests)

**Coverage depth:** deep for the shipped static/read-only route contract. The
spec runs through authenticated real dev infra, verifies the rendered fixture
shape, drives available action semantics, proves the `/atelier/video` handoff
plus inbound URL context, and covers mobile, reduced-motion, and offline-stable
loaded markup. Remaining gaps are product work: route-local loading/error
boundaries, a real new-take or retarget workflow, and a persisted project-state
bridge into Cinematheque.

## Per-view files touched

- [`customer/11-atelier/atelier-motion.md`](../customer/11-atelier/atelier-motion.md)
  — primary Stage of Motion route
- [`customer/11-atelier/atelier-video.md`](../customer/11-atelier/atelier-video.md)
  — receiving Cinematheque route and inbound context panel
- [`customer/11-atelier/atelier.md`](../customer/11-atelier/atelier.md) — room
  index entry

## Cross-references

- Cinematheque render-monitor journey:
  [`atelier-video-render-queue.md`](./atelier-video-render-queue.md)
- Coverage matrix: [`coverage.md`](./coverage.md)

## Open questions / known gaps

- [ ] Add route-local loading and error states for both sides of the handoff
- [ ] Decide whether new-take and rig-retarget workflows belong in the consumer
      room or only in the authoring studio
- [ ] Replace URL-only context with a persisted project reference before
      describing the handoff as durable
