V1 Web PWA · Walk result

Dependency result: Metis course-proposal 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 to define the upstream creator proposal separately from the curated MetisCourseBuild it may eventually produce.
  • Reconciled: 2026-07-18 against the shipped proposal contract, persistence registry, generated Prisma model, migration, creation adapter, and live-DB test. This was a source reconciliation, not a fresh production walk.
  • Verdict: partial — proposer identity, review chain, seed sources, promotion links, and rejection semantics now have a canonical schema and initial dual-write. The runtime only creates an empty awaiting-triage projection; review, grounding, Veritas, promotion, rejection, canonical reads, atomicity, and contract-valid ids remain unproved.
  • Primary spec: apps/oshun/bff/src/routes/domain-stubs-postgres.test.ts.

Result at a glance#

Evidence lane Current result Authority limit
Contract design Proposer, sources, four-step review, promotion links, and rejection states ship Encoded policy; no reviewer or governance validation
Persistence model Registry, Prisma table, migration, canonical indexes, and tombstones ship Repository evidence, not a production rollout receipt
Creation write courseProposals.add() inserts legacy and canonical rows Sequential queries; no transaction or repair mechanism
Initial mapping Legacy syllabus-review state maps to awaiting-triage; arrays start empty Does not perform triage, grounding, Veritas review, or promotion
Automated evidence Live-DB test checks tenant, proposer, weeks, state, and empty arrays No Zod parse and no lifecycle transition assertions
Read/lifecycle path Proposal listing still reads the legacy table No canonical review receipt, rejection, build spawn, or read cutover

Evidence map#

The current implementation creates the durable starting point. The governed path from idea to course remains dashed.

flowchart LR A[Submit course idea] --> B[Legacy proposal row] B --> C[Canonical awaiting-triage row] C -. not connected .-> D[Metis triage] D -. not connected .-> E[Sophia grounding] E -. not connected .-> F[Veritas review] F -. not connected .-> G[Spawn course build] C -. reads still legacy .-> H[My proposals] B -. no atomic rollback .-> I[Possible split write]

Proven observations#

Proposal and build are now distinct contracts#

  • MetisCourseProposalSchema carries the proposer, tenant, title, domain path, target weeks, rationale, up to three source references, a bounded review chain, lifecycle state, spawned build ids, rejection notes, and timestamps.
  • The review vocabulary explicitly distinguishes triage, grounding, Veritas, and ready steps. Soft rejection leaves a proposal open for another pitch; hard rejection is the terminal/tombstone case; promotion requires at least one spawned build id.
  • The registry and generated Prisma model map the contract to v1_metis_course_proposal. Migration 20260528024226_atelier_studio_metis_canonical_contracts creates the table and standard canonical indexes.

Creation records an honest empty review state#

  • courseProposals.add() writes the legacy awaiting-syllabus-review row, then writes its canonical peer as awaiting-triage, using the authenticated owner as proposer. Source refs, review entries, and spawned builds start empty; rejection notes start null.
  • The live-DB test reads the canonical peer and verifies the tenant, contract schema, proposer, target weeks, state mapping, and empty source/build arrays.
  • The list path remains backed by goal3_stub_course_proposal, so the new row is a write-side bridge rather than the authoritative proposal service.

Boundaries and gaps#

  • An awaiting-triage row is not a review workflow. No endpoint appends an actor-bound triage, grounding, or Veritas verdict or enforces step ordering.
  • A promotion field is not promotion. No connected route creates a MetisCourseBuild, writes spawnedBuildIds, and returns a linked receipt.
  • A canonical table row is not necessarily contract-valid. The adapter generates course-proposed-… ids and accepts owner strings, although the contract requires UUIDs. The test checks columns without parsing the object.
  • A dual-write is not atomic. The legacy and canonical inserts are separate statements. No transaction, outbox, or repair worker closes partial failure.
  • A canonical write is not canonical read authority. Lists stay on the legacy table, so canonical review state would not automatically appear in the current customer surface.
  • Review vocabulary is not governance proof. This result does not validate reviewer permissions, source grounding quality, retraction conflicts, notifications, or appeal/re-pitch semantics.

Re-run evidence#

Run the focused live-DB assertion with the repository Postgres test setup:

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

A green result proves the current initial projection. It must not be reported as a review chain, a grounded proposal, a promoted course build, or canonical read cutover.

Source trail#

Cross-references#

Open questions#

  • Which service records each review entry, enforces reviewer role and step order, and exposes the receipt to the proposer?
  • How are seed sources grounded and checked against Veritas conflicts before a proposal can become ready-for-promotion?
  • What atomic operation spawns one or more builds and updates the proposal without orphaning either side?
  • How will current non-UUID proposal identifiers be reconciled with the canonical contract?
  • When will reads move to the canonical model, and what process repairs split or historically missing peers before cutover?