# Journey: Atelier · image room → kept plate in the library

A signed-in customer opens The Atelier index, walks into the Camera Obscura
(image room), writes a prompt, casts twelve specimens, picks one, saves the
plate to their notebook, and confirms it lands in `/library` with the room's
provenance intact. Exercises the V1 Atelier rooms as a slow, line-first,
provenance-shown studio surface for the consumer — separate from Yemaya Studio's
authoring tier and from the Living Scene card.

## Personas

- **Contemplative customer** — primary actor; treats the Atelier as a slow
  artisan workshop ("a small workshop attached to the reading desk"); reaches it
  via the Lilith index, the Atelier index card, or a saved link
- **Returning customer with a previous kept plate** — verifies that the saved
  artifact persists in the library and resumes at Camera Obscura; restoring a
  plate-specific prompt, seed, and style remains an explicit gap
- **Customer with a Tara contemplative-arc workspace open** — verifies that the
  Atelier composition surface respects the tone band and that the Provenance
  stripe shows the right Sophia source bindings
- **AAA-tier customer** — for whom the AAA Score Editor lives in Yemaya Studio,
  not the Atelier; this journey deliberately walks the contemplative-tier
  surface and notes the AAA hand-off as a fall-through

## Pre-conditions

- Customer signed in
- `/atelier` and `/atelier/image` are reachable (both auth-gated by middleware);
  the Lilith design system mounts the Atelier rooms
- `getAtelierIndex()` and `getAtelierImage()` return populated fixture data;
  `/v1/atelier/cast` is a real fail-closed provider seam, while a dedicated
  `/atelier/plates/*` persistence API is not shipped
- Library state is hydrated for the customer and synchronizes through the
  library client-state seam
- House rules govern every generative surface per the Atelier index: "Only from
  a line.", "Provenance shown.", "Slow by default."

## Steps

### 1. Open the Atelier index

- [ ] Customer navigates to `/atelier` (via the Lilith index section VIII card
      41, the customer nav `LCustomerNav active="explore"` shell, or a direct
      URL)
- [ ] Page server-renders `apps/oshun/web/src/app/atelier/page.tsx` which awaits
      `getAtelierIndex()` from
      `apps/oshun/web/src/lib/lilith-data/atelier-depth.ts` and renders
      `<AtelierIndex data>`
- [ ] Masthead reads "The atelier · est. 2026" with the kicker "Five rooms of
      making" and the title "The _atelier_."
- [x] Five room cards render in an auto-fit room shelf: Camera Obscura, Foundry
      of Voices, Cinematheque, Forms, Stage of Motion
- [ ] Three house rules render below the rooms
- [ ] **Verify**:
      [`customer/11-atelier/atelier.md`](../customer/11-atelier/atelier.md)
      layout regions, States. 2026-06-25 row-cell pass asserts the index root
      count attributes, all five room-card number/name/meta/engine/href cells,
      and all three house-rule title/description cells.

### 2. Walk into Camera Obscura

- [ ] Customer taps the "X · Camera Obscura · image" card → navigates to
      `/atelier/image`
- [ ] Page server-renders `apps/oshun/web/src/app/atelier/image/page.tsx` which
      awaits `getAtelierImage()` and renders `<AtelierImage data>`
- [ ] Plate head reads "An image, drawn from a _line_." with right-aligned chips
      "Specimen 047", "Saved to notebook", "Plate finalised" (accent)
- [x] Left rail shows the five-step `LAStepRail` with `current=3`: A line,
      written / Reference, gathered / Twelve specimens / Plate kept · refined /
      Final · saved to notebook
- [x] Center column shows the prompt textarea seeded with "A monk, seated on a
      stone bench, lit only by a high north window…", chips (Aspect 4:5, Plate
      cream, Steps 38, Seed `0x4f2a · locked`, Re-cast accent), and the 4×3
      contact sheet of 12 selectable specimen buttons with `p.07` marked `kept`.
- [x] Right column shows Plate detail (`p.07`), field rows (Engine / Aspect /
      Steps / Guidance / Seed / Style / Sources), a separator, then
      `Provenance · 3 sources` listing the fixture provenance — currently
      Vermeer · light study, Hammershøi · interiors, Lilith · plate library
- [ ] **Verify**:
      [`customer/11-atelier/atelier-image.md`](../customer/11-atelier/atelier-image.md)
      layout, three-column body, Provenance lane. 2026-06-25 row-cell pass
      asserts the route source/specimen counts, plate-head chips, step-rail
      states, prompt chips, contact-sheet specimen states, selected plate field
      rows, and provenance source/kind cells.

### 3. Write the line (prompt-only entry)

- [x] Customer edits the prompt copy in `data-atelier-prompt-input`; the
      browser-entered prompt is carried into Cast / Refine payloads.
- [x] House rule "Only from a line." enforces the prompt-only entry — no
      reference image input on this surface (Reference, gathered is its own step
      in the rail but the demo flow keeps the prompt as the
      single-source-of-truth)
- [x] Customer adjusts the aspect chip and the style chip; the seed chip shows
      `locked` so subsequent casts vary the prompt against a stable seed (per
      the on-screen affordance "Lock seed · vary prompt"). The browser controls
      now update the prompt chips, plate detail fields, compare summary, and
      Cast/Refine payloads (`atelier-image-to-library` +
      `atelier-plate-actions`, 2026-06-26).

### 4. Cast twelve specimens

- [x] Customer taps "Cast twelve more" through
      `AtelierPlateActions mode="cast"`; this POSTs `/v1/atelier/cast`. In local
      real-dev infra without Stability credentials, the seam fails closed with
      typed provider-not-configured state rather than fabricating a successful
      cast. The request body includes the edited prompt, selected specimen id,
      selected aspect, selected style, seed-lock state, and `intent: cast`.
- [x] The contact-sheet grid populates with 12 selectable specimen tiles; each
      contains a procedural SVG illustration.
- [x] House rule "Slow by default." means the cast is deliberate — there is no
      auto-streaming spinner, no rapid-fire chrome
- [x] As specimens settle, the customer reviews the grid

### 5. Pick the plate

- [x] Customer taps a specimen; the draft tile transitions to
      `data-atelier-specimen-status="selected"` and exposes
      `data-atelier-specimen-selected="true"`.
- [x] The right column's Plate detail pane updates with the picked tile's plate
      id (`Plate · p.03` in the Playwright path) and the same `LField` rows
      (Engine, Aspect 4:5, Steps 38, Guidance 6.4, Seed `0x4f2a · locked`, Style
      "Cormorant warmth", Sources "3 · cited")
- [x] Step rail advances to step 4 "Plate kept · refined" when the customer
      selects a specimen (`atelier-image-to-library`, 2026-06-26).
- [x] Provenance list is enumerated under the picked plate so the customer sees
      exactly which source images and which Lilith plate-library entries
      informed the cast — honoring house rule "Provenance shown."

### 6. Refine (optional)

- [x] Customer can tap "Refine · vary slightly"
      (`AtelierPlateActions mode="refine"`) to nudge the picked plate; it POSTs
      `/v1/atelier/cast` with `intent: refine`, the edited prompt, selected
      specimen id, aspect, style, and seed-lock state. In local real-dev infra
      without Stability credentials, it fails closed with typed
      provider-not-configured state.
- [x] Customer can also tap "Lock seed · vary prompt" or "Compare side-by-side";
      seed-lock toggles the chip/field/payload state and Compare opens a
      kept-vs-active side-by-side panel (`atelier-image-to-library`,
      2026-06-26).
- [ ] When the customer is satisfied with the kept plate they advance to step 7

### 7. Save plate to notebook

- [x] Customer taps "Save plate to notebook" through
      `AtelierPlateActions mode="save"`
- [x] House rule "Slow by default." means the save is intentional, not
      auto-flushed — the customer chooses when to keep
- [x] Save flow writes to the library store via `saveOshunWebLibraryItem` with a
      new `WebLibrarySavedItem` carrying:
  - `kind: 'generated-artifact'`
  - `domain: 'nisaba'` (the current non-domain library fallback used by the save
    helper)
  - `resumePath: '/atelier/image'` so the customer can return to Camera Obscura
  - kept-plate title, summary, format label, context, and resume action
- [x] Step rail advances to step 5 "Final · saved to notebook" after the
      explicit save action (`atelier-plate-actions`, 2026-06-26).
- [x] Save result: "Saved to your library — find it under kept offerings."
- [x] Telemetry: `atelier_plate_saved` fires after the accepted save with room
      id `camera-obscura`, prompt hash, seed `0x4f2a`, seed-lock state, and
      source-set size.

### 8. Confirm the plate landed in the library

- [x] Customer navigates to `/library?kind=generated-artifact` (via the shell
      `Library` tab, the `WEB_SHELL_ROUTE_PATHS.library` route)
- [x] `LibraryDashboard` mounts; pre-hydration placeholder briefly shows; once
      hydrated the saved-items list includes the new plate row
- [x] Library kind filter: the kept plate surfaces under `generated-artifact`.
- [x] Saved item card renders the plate signals (`ConfidenceBadge`,
      `LibraryGroundingBadge`, `ProvenanceBadge`) — the provenance badge
      reflects the 3 sources from the Atelier
- [x] Customer sees the row's "Open in Atelier" resume action; clicking back to
      a plate-specific variant is not yet parameterized because the saved row's
      current `resumePath` is `/atelier/image`.
- [x] **Verify**:
      [`customer/02-home-discovery/library.md`](../customer/02-home-discovery/library.md)
      Saved item row, filters, Storage write

### 9. Re-cast from the kept plate

- [ ] Customer (later) returns from the library row; the room re-mounts with the
      saved plate selected and the prompt / seed / style pre-populated
- [ ] House rule "Provenance shown." holds — the 3-source list re-renders from
      the saved provenance, not refetched
- [ ] Customer can refine, cast again, or save a new variant; each variant
      becomes its own library item with attribution to the parent plate (per the
      lineage rule from
      [Reshare, Lineage, and Remix Attribution](../../V1/features.md#reshare-lineage-and-remix-attribution))

## Post-conditions

- A new `WebLibrarySavedItem` exists in `useOshunWebLibraryStore` for the
  customer; serialized to `localStorage['oshun.library.collections.v1']` via the
  dashboard's persistence pipeline
- The saved row resumes at the unparameterized `/atelier/image` route; no claim
  is made that it restores the selected plate, prompt, or parent lineage yet
- The library row exposes the partial provenance badge and lineage note carried
  by the saved artifact; plate-specific room re-entry remains open
- Verified telemetry is `atelier_plate_saved`; the broader room-enter/select/
  re-entry trail is not presented as shipped instrumentation

## Failure modes

- [x] **Save/cast/refine/control action integrity** — "Save plate to notebook",
      "Cast twelve more", specimen tap-to-select, "Refine · vary slightly",
      aspect/style choices, "Lock seed · vary prompt", and "Compare
      side-by-side" are no longer inert: save writes a generated artifact to the
      web library, cast/refine drive the real fail-closed `/v1/atelier/cast`
      seam with the browser-entered prompt, selected specimen, aspect, style,
      and seed-lock state, and Compare opens a kept-vs-active plate panel.
- [ ] **Provenance list empty** — house rule "Provenance shown." is violated;
      the room must surface "no provenance recorded" rather than hiding the lane
      silently
- [ ] **`getAtelierImage()` 5xx** — no route-local `error.tsx` exists; inherited
      app handling is the only current boundary
- [ ] **Offline cast** — the entire view is static markup, so the room renders
      offline; cast / save actions degrade per the offline pattern (queue or
      disable with explanation per
      [`shell/03-pwa-behavior.md`](../shell/03-pwa-behavior.md))
- [ ] **AAA-tier customer expecting Yemaya Studio depth** — the Atelier is the
      consumer tier; if AAA tier is the call site the room should offer "Open in
      Yemaya Studio" as a hand-off (verify whether the Atelier currently
      surfaces such an affordance)
- [ ] **Library kind mapping wrong** — if `plate` maps to `notebook` and the
      customer expects a dedicated `plate` chip, the kind rail needs a new chip;
      otherwise the plate hides under a less-obvious filter
- [ ] **Prompt-only entry refused by Lilith pre-screen** — the room must surface
      the rejection cleanly without leaking the rejection reason (per the
      broader Lilith pre-screen pattern)
- [x] **Mobile / narrow breakpoint** — `/atelier` and `/atelier/image` now use
      auto-fit grid tracks, and `atelier-image-to-library` verifies both routes
      at a 390 px viewport with no horizontal overflow and the image room
      stacked rail → composer → plate detail.

## E2E coverage

Spec: `apps/oshun/web/e2e/atelier-image-to-library.spec.ts` (12 tests)

Spec (wave-7, plate actions; deepened 2026-06-27):
[`apps/oshun/web/e2e/atelier-plate-actions.spec.ts`](../../apps/oshun/web/e2e/atelier-plate-actions.spec.ts)
— the `AtelierPlateActions` island replaced the inert buttons: "Save plate to
notebook" now writes a generated-artifact row into the web library, reads the
persisted saved-items metadata back from the real BFF, renders the Library row's
partial provenance badge + lineage note, and hydrates the same provenance state
on a clean second-device `/library?kind=generated-artifact` load. "Cast twelve
more" POSTs `/v1/atelier/cast` — a real fail-closed Stability image seam that
returns `503 provider_not_configured` without creds. The action spec also wires
"Refine · vary slightly" through the same real cast seam and asserts Cast/Refine
carry the browser-entered prompt, selected specimen, aspect, style, and
seed-lock state in the request payload. So save + cast + refine are driven
end-to-end (the deeper generation-success path stays env-cred-gated).

| Step                                                         | Test name                                    | Covered? |
| ------------------------------------------------------------ | -------------------------------------------- | -------- |
| 1 — /atelier masthead                                        | step 1 masthead + "Five rooms of making"     | ✓        |
| 1 — five room cards                                          | step 1 five room cards with names and hrefs  | ✓        |
| 1 — three house rules                                        | step 1 three house rules below rooms grid    | ✓        |
| 1/2 — mobile index + image-room stacking                     | mobile overflow/stacking test                | ✓        |
| 2 — plate head + header chips                                | step 2 plate head renders                    | ✓        |
| 2 — left step rail (all five, current=3)                     | step 2 step rail                             | ✓        |
| 2/3/5/6 — editable prompt, controls, select, compare, rail   | steps 2,3,5 prompt/select test               | ✓        |
| 2 — right plate detail + provenance header                   | step 2 plate detail + Provenance             | ✓        |
| 2 — Camera Obscura link to /atelier/image                    | step 2 Camera Obscura card link              | ✓        |
| 4 — "Cast twelve more" button present + state/payload        | step 4 cast/lock/compare buttons             | ✓        |
| 6/7 — "Refine" + "Save plate to notebook" controls/rail      | step 7 save + telemetry + refine seam        | ✓        |
| 8 — saved plate visible in Library generated-artifact filter | `atelier-plate-actions` save + BFF hydrate   | ✓        |
| 8 — Library provenance badge + lineage note read-back        | `atelier-plate-actions` provenance read-back | ✓        |
| failure — Provenance sources all named (house rule II)       | failure mode provenance sources              | ✓        |

**Not covered (and why):**

- Step 9 (re-cast from the kept plate) — library resume currently returns to
  `/atelier/image`; plate-specific query hydration and parent/variant lineage
  are not implemented.

**Coverage depth**: partial — see [`coverage.md`](./coverage.md). `/atelier` and
`/atelier/image` now have a client room island for the live step rail, editable
line, aspect/style/seed controls, specimen selection, compare panel, save,
`atelier_plate_saved` telemetry, and real cast/refine seam, while `/atelier`
remains a server-rendered room index. No longer a render-only specimen — the
prompt, controls, select, compare, save, cast, and refine interactions
transition real browser/BFF state. The save action now proves server read-back,
Library partial-provenance badge read-back, privacy-preserving telemetry, and a
clean second-device Library hydrate; only the deeper generation-success path
stays env-cred-gated.

**Uncovered**: plate-specific library resume / variant lineage, crisis-frame
suppression, AAA Studio handoff, and the cast **success** path (image bytes
returned), which is env-cred-gated behind Stability creds. The prompt edit,
controls, specimen selection, compare panel, save-to-library BFF sync, Library
provenance badge read-back, `atelier_plate_saved` telemetry, clean-device
library landing, cast seam, and refine seam interactions are now covered
end-to-end.

## Per-view files touched by this journey

- [`customer/11-atelier/atelier.md`](../customer/11-atelier/atelier.md) — the
  index card row + house rules
- [`customer/11-atelier/atelier-image.md`](../customer/11-atelier/atelier-image.md)
  — the Camera Obscura room itself
- [`customer/02-home-discovery/library.md`](../customer/02-home-discovery/library.md)
  — where the kept plate lands
- [`customer/10-lilith/lilith.md`](../customer/10-lilith/lilith.md) — the Lilith
  index card 41 that links to the Atelier
- [`shell/03-pwa-behavior.md`](../shell/03-pwa-behavior.md) — offline behavior
  for the static room compositions

## Cross-references

- Feature spec:
  - [Living Scenes overview](../../V1/features.md#living-scenes) and
    [Composition Surface and Cinematographic Technique Catalog](../../V1/features.md#composition-surface-and-cinematographic-technique-catalog)
    — the broader generative-surface contract the Atelier consumes (the Atelier
    is the consumer-tier presentation; the authoring tier lives in Yemaya
    Studio)
  - [Reshare, Lineage, and Remix Attribution](../../V1/features.md#reshare-lineage-and-remix-attribution)
    — applies to re-casts from a kept plate
- Related journeys:
  - [`scene-keep-and-share.md`](./scene-keep-and-share.md) — the Atelier's
    kept-plate notion is the artisan cousin of the Living Scene's kept artifact;
    both honour the "keep is separate from share" gate
  - [`first-time-anonymous-visitor.md`](./first-time-anonymous-visitor.md) —
    anonymous visitors do not reach the Atelier (signed-in only)
- Component sources:
  - `apps/oshun/web/src/components/lilith/atelier.tsx` (`AtelierIndex`,
    `AtelierImage`)
  - `apps/oshun/web/src/lib/lilith-data/atelier-depth.ts` — `getAtelierIndex`,
    `getAtelierImage`, types
  - `apps/oshun/web/src/components/library/LibraryDashboard.tsx` — library
    landing for the kept plate
  - `apps/oshun/web/src/lib/library/webLibraryStore.ts` —
    `useOshunWebLibraryStore`

## Open questions / known gaps

- [x] Current save path — `AtelierPlateActions` writes the kept plate to the web
      library as `generated-artifact`; a future `/atelier/plates/...` BFF may
      supersede this local-library seam.
- [x] Library `kind` taxonomy — current implementation uses
      `generated-artifact`.
- [x] Confirm the responsive behaviour of `/atelier` and `/atelier/image` — both
      now use auto-fit grids and are covered at 390 px by
      `atelier-image-to-library`.
- [x] Confirm whether tap-to-select on the specimen tiles is wired — draft
      specimen buttons now update `data-atelier-selected-plate` and are covered
      by Playwright.
- [x] Confirm the "Compare side-by-side" UX — it opens an inline kept-vs-active
      comparison panel in the center column.
- [ ] Document whether `Save plate to notebook` honors crisis-frame suppression
      (per `crisis-aware-tone-policy.md`) — kept plates generated during a
      crisis frame should default to private and not be shareable
- [ ] Confirm the Atelier hand-off into Yemaya Studio for AAA tier — is there an
      "Open in Studio" affordance on the room, and which Yemaya Studio surface
      accepts the hand-off?
