- Opened: 2026-05-29 to define the upstream creator proposal separately from
the curated
MetisCourseBuildit 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-triageprojection; 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#
MetisCourseProposalSchemacarries 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. Migration20260528024226_atelier_studio_metis_canonical_contractscreates the table and standard canonical indexes.
Creation records an honest empty review state#
courseProposals.add()writes the legacyawaiting-syllabus-reviewrow, then writes its canonical peer asawaiting-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, writesspawnedBuildIds, 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#
- External-dependency index
- Metis proposal contract
- Persistence registry
- Generated Prisma schema
- Canonical migration
- Postgres adapter
- Live-DB adapter test
Cross-references#
- Metis proposal view
- Metis BYOM journey
- Editorial review and approval result
- Studio scene contract result
- External-dependency convention
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?