V1 Web PWA · Journey

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

A source-reconciled journey record for V1 Web PWA — shipped behavior, state boundaries, failure modes, and the automation evidence available today.

automated · 2 specs verdict: partial· 2026-05-29
9sections13 minread1table

On this page
Journey at a glance
ActorsContemplative customerReturning customer with a previous kept plateCustomer with a Tara contemplative-arc workspace openAAA-tier customer
Automation2 E2E spec(s) · verdict: partial· 2026-05-29

Journey flow#

Generated from the authored steps below — click a node to jump to that section.

flowchart TD s1["1. Open the Atelier index"] s2["2. Walk into Camera Obscura"] s3["3. Write the line (prompt-only entry)"] s4["4. Cast twelve specimens"] s5["5. Pick the plate"] s6["6. Refine (optional)"] s7["7. Save plate to notebook"] s8["8. Confirm the plate landed in the library"] s9["9. Re-cast from the kept plate"] s1 --> s2 --> s3 --> s4 --> s5 --> s6 --> s7 --> s8 --> s9 click s1 href "#1-open-the-atelier-index" click s2 href "#2-walk-into-camera-obscura" click s3 href "#3-write-the-line-prompt-only-entry" click s4 href "#4-cast-twelve-specimens" click s5 href "#5-pick-the-plate" click s6 href "#6-refine-optional" click s7 href "#7-save-plate-to-notebook" click s8 href "#8-confirm-the-plate-landed-in-the-library" click s9 href "#9-re-cast-from-the-kept-plate" s9 --> v(["verdict: partial"]) click v href "../results/atelier-image-to-library.html" style v stroke:#d6a531,color:#d6a531

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."
  • 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 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)
  • Left rail shows the five-step LAStepRail with current=3: A line, written / Reference, gathered / Twelve specimens / Plate kept · refined / Final · saved to notebook
  • 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.
  • 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 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)#

  • Customer edits the prompt copy in data-atelier-prompt-input; the browser-entered prompt is carried into Cast / Refine payloads.
  • 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)
  • 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#

  • 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.
  • The contact-sheet grid populates with 12 selectable specimen tiles; each contains a procedural SVG illustration.
  • House rule "Slow by default." means the cast is deliberate — there is no auto-streaming spinner, no rapid-fire chrome
  • As specimens settle, the customer reviews the grid

5. Pick the plate#

  • Customer taps a specimen; the draft tile transitions to data-atelier-specimen-status="selected" and exposes data-atelier-specimen-selected="true".
  • 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")
  • Step rail advances to step 4 "Plate kept · refined" when the customer selects a specimen (atelier-image-to-library, 2026-06-26).
  • 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)#

  • 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.
  • 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#

  • Customer taps "Save plate to notebook" through AtelierPlateActions mode="save"
  • House rule "Slow by default." means the save is intentional, not auto-flushed — the customer chooses when to keep
  • 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
  • Step rail advances to step 5 "Final · saved to notebook" after the explicit save action (atelier-plate-actions, 2026-06-26).
  • Save result: "Saved to your library — find it under kept offerings."
  • 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#

  • Customer navigates to /library?kind=generated-artifact (via the shell Library tab, the WEB_SHELL_ROUTE_PATHS.library route)
  • LibraryDashboard mounts; pre-hydration placeholder briefly shows; once hydrated the saved-items list includes the new plate row
  • Library kind filter: the kept plate surfaces under generated-artifact.
  • Saved item card renders the plate signals (ConfidenceBadge, LibraryGroundingBadge, ProvenanceBadge) — the provenance badge reflects the 3 sources from the Atelier
  • 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.
  • Verify: 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)

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#

  • 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)
  • 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)
  • 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 — 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. /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#

Cross-references#

  • Feature spec:
  • Related journeys:
  • Component sources:
    • apps/oshun/web/src/components/lilith/atelier.tsx (AtelierIndex, AtelierImage)
    • apps/oshun/web/src/lib/lilith-data/atelier-depth.tsgetAtelierIndex, getAtelierImage, types
    • apps/oshun/web/src/components/library/LibraryDashboard.tsx — library landing for the kept plate
    • apps/oshun/web/src/lib/library/webLibraryStore.tsuseOshunWebLibraryStore

Open questions / known gaps#

  • 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.
  • Library kind taxonomy — current implementation uses generated-artifact.
  • 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.
  • 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.
  • 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?