V1 Web PWA · Walk result

Dependency result: Atelier scene contract

A dated evidence record for V1 Web PWA: observed behavior, current source reconciliation, automation evidence, and explicit proof limits.

verdict: partial· date unknown
8sections4 minread1table

On this page
  • Opened: 2026-05-29 as a product-design dependency because Atelier had only a goal3_stub_atelier_scene row and no canonical scene contract.
  • Reconciled: 2026-07-18 against the shipped contract, persistence registry, generated Prisma model, migration, Postgres adapter, and live-DB adapter test. This was a source reconciliation, not a fresh production walk.
  • Verdict: partial — the original contract-design dependency is closed: AtelierSceneSchema, v1_atelier_atelier_scene, its migration, and a creation dual-write now exist. Runtime authority remains split because reads and later lifecycle transitions stay on legacy seams, the paired inserts are not one transaction, and the test does not prove the inserted row parses through the canonical Zod contract.
  • Primary spec: apps/oshun/bff/src/routes/domain-stubs-postgres.test.ts.

Result at a glance#

Evidence lane Current result Authority limit
Contract design Single recipient, cue privacy, render reference, share receipt, and gates ship Source schema; not external privacy or notification validation
Persistence model Registry, generated Prisma model, migration, indexes, and tombstone fields ship Presence does not establish production migration rollout
Creation write scenes.add() writes legacy and canonical rows Sequential inserts; no shared transaction or repair queue
Initial mapping Free-text recipient becomes { displayLabel, knownUserId: null } in drafting Render, privacy hash, share record, and notes begin null
Automated evidence Live-DB test reads the canonical row and checks tenant, state, and recipient Requires configured Postgres; does not call AtelierSceneSchema.parse
Read/lifecycle path Owner listing still reads goal3_stub_atelier_scene No canonical finishing, sharing, acknowledgement, archive, or readback

Evidence map#

The solid lane is implemented today. Dashed edges show where a canonical draft row stops short of becoming the authoritative scene lifecycle.

flowchart LR A[Authenticated create scene] --> B[Legacy Atelier insert] B --> C[Canonical draft insert] C --> D[v1_atelier_atelier_scene] D -. reads not cut over .-> E[Owner scene list] D -. no connected transition .-> F[Render and cue privacy hash] F -. no connected transition .-> G[Share receipt and acknowledgement] B -. no atomic rollback .-> H[Possible split write]

Proven observations#

Contract and persistence are now concrete#

  • libs/contracts/src/atelier/index.ts defines a canonical scene with an authenticated sender, one structured recipient, lifecycle state, cue-privacy manifest hash, latest render, share record, sender notes, and timestamps. Refinements require render evidence for finishing and privacy/share evidence for shared.
  • libs/oshun/persistence/src/contract-persistence-registry.ts registers the contract. The generated Prisma schema maps it to v1_atelier_atelier_scene, and migration 20260528024226_atelier_studio_metis_canonical_contracts creates the table and canonical identity, tenant, payload-hash, live-scan, and tombstone indexes.
  • These facts retire the old statements that no canonical model existed or that product decisions still blocked defining the contract.

Creation now leaves two records#

  • scenes.add() first inserts the legacy drafting row and then inserts a canonical drafting row with tenant house, the authenticated owner as sender_user_id, and the typed recipient projection. Unknown recipients stay honest as knownUserId: null.
  • The canonical row starts with null cue-privacy, render, share, and notes fields. That is appropriate for a draft and avoids inventing downstream evidence.
  • The live-DB adapter test creates a scene and reads the canonical table back, checking the contract schema id, initial state, recipient label, and null privacy hash.

Boundaries and gaps#

  • A canonical table is not canonical read authority. listForOwner() still selects the legacy table, so a canonical-only repair or later canonical update would not appear in the customer list.
  • A dual-write is not an atomic write. The stub insert and canonical insert are separate queries. A failure on the second query can leave a legacy row without its canonical peer; no outbox, reconciliation worker, or rollback is shown in this adapter.
  • Column assertions are not contract validation. Generated SQL accepts text ids while AtelierSceneSchema requires UUIDs. The adapter generates a scene-… id and the focused test never parses the reconstructed row through the Zod schema.
  • A draft projection is not the scene lifecycle. No connected adapter path populates latestRender, cuePrivacyManifestHash, shareRecord, recipient acknowledgement, or archive state.
  • A manifest hash is not privacy enforcement. The contract encodes the evidence field and gates, but this result does not run intent redaction, compare a rendered manifest, or prove recipient-visible cues remain private.
  • Migration source is not deployment proof. The migration exists in the repository; no production database inventory or rollout receipt was inspected.

Re-run evidence#

The canonical-table assertion lives in the BFF Postgres suite:

bash
pnpm exec vitest run \
  apps/oshun/bff/src/routes/domain-stubs-postgres.test.ts \
  -t "scenes.add\(\) dual-writes"

That test needs the repository's live Postgres test configuration. A green run proves the initial paired-row mapping for the configured database; it must not be summarized as canonical read cutover, atomicity, or a render-to-share flow.

Source trail#

Cross-references#

Open questions#

  • Which service owns canonical reads, and what repair process reconciles legacy rows created if the second insert fails?
  • Will creation move to one database transaction or an outbox-backed write so the two records cannot silently diverge?
  • Which endpoint validates and writes render, cue-privacy, share, acknowledgement, and archive transitions through AtelierSceneSchema?
  • How will existing scene-… identifiers be migrated to a contract-valid id strategy without breaking legacy links?
  • What production evidence confirms the migration, indexes, and cue-privacy gates are deployed and exercised for real tenants?