wiki-vault/Sources/Dev/2026-06-04-synthesis-layer-design-session-extraction-contract-closed.md

55 lines
6.3 KiB
Markdown

---
created: '2026-06-04'
path: Sources/Dev
project: synthesis-layer
tags:
- dispatch
- lovebug
- claude-code
- mcp
- automation
type: session-notes
---
Continuation of the `synthesis-layer` design session (companion to `2026-06-04-synthesis-layer-design-session-handoff-model-resolved`). Closed Open Item #3 — the extraction contract — and resolved the contract-versioning question by drawing a write-side/read-side split. Next: reconcile against the `execution-journal` vault plan.
## Outcome
The extraction contract is specified, the validation gate is placed, and the read posture is named. #3 is closed.
**1. The handoff schema is data, not hardcoded.** Applied petal-dispatch's D-4 principle (`event_type` as a reference table, open set, never hardcoded) one level up — to the handoff schema itself. The fields a handoff can carry are rows in a `contract` table, not a baked-in struct. Each row defines one field: its key, whether it's required, who reads it, its semantics, and its value shape (flexible JSON, e.g. `ruled_out` shape is `{type: list, item: {approach, reason}}`). Adding a handoff field is an insert, not a code change — same extensibility property as event types.
**2. The contract feeds the validator, not just documentation.** The handoff is trustworthy (unlike the rejected Option-A worker-emitted handoff) precisely because it is schema-validated at write time. With the schema now living as data, the validator reads the contract table and gates the `derived` write: required fields present, shapes match, reject/retry on mismatch. The contract has two write-time consumers — the extractor (how to generate) and the validator (how to enforce). This is what keeps "schema is data" from degrading into "schema is vibes."
**3. The `status` field is forced by Option B.** The extractor reads a worker that exited, and the worker may have exited done / blocked / partial. The harness branches hard on this, so `status` is a strict enum field in the contract — it was not in the original draft's candidate four (goal / current-state / next-step / ruled-out) but B requires it.
**4. `ruled_out` is the load-bearing field.** The whole "doomed to repeat history" premise rests on the harness not re-proposing a ruled-out approach. For that to be mechanical rather than semantic, `ruled_out` is structured as a list of `{approach, reason}` (its shape row in the contract enforces it), not prose. The harness checks a candidate next-step against it as a lookup, not a fuzzy comparison.
**5. Write-side / read-side split — the key clarification that dissolved versioning.** The contract is WRITE-SIDE ONLY. It governs *generation + validation*. It is never consulted at read time. The handoff is READ-SIDE SELF-DESCRIBING — it carries its own structure in its immutable payload. A reader (harness, compressor, human) reads the handoff directly and never touches the contract. Because nobody reads a handoff *through* the contract, it does not matter which contract version produced it — so no version-stamping, no version-capture, no recovery story is needed. The earlier worry about stamping each handoff with its contract version was inventing a read-path coupling that does not exist.
**6. Drift model, finalized.** Contract rows are immutable; the contract *set* is append-only and grows as the system learns (drift-by-accretion, not drift-by-mutation — same model as the vault: frozen artifacts, evolving corpus). Handoffs are immutable once written and each carries its own shape, so handoff shape drifts over time as the contract grows. Old handoffs remain readable because they are self-describing — readers handle whatever fields are present.
**7. Read-side discipline: readers must be shape-tolerant.** The natural and only cost of self-describing + drifting. The harness reads what exists, branches on the fields it cares about, handles absence gracefully, tolerates unrecognized fields — same posture as petal-dispatch's open `event_type` set. Cheap as long as readers are written as "read what exists," not "parse a fixed struct."
## Topics Covered
- Handoff schema as data (`contract` table) vs hardcoded struct; chose data, per D-4.
- Contract as input to the validator, not just documentation; two write-time consumers (extractor + validator).
- `status` enum forced by Option B; `ruled_out` as load-bearing structured field.
- Contract-versioning worry and its dissolution via the write-side/read-side split.
- Drift-by-accretion (immutable rows, append-only set) vs drift-by-mutation.
- Shape-tolerant readers as the load-bearing read-side discipline.
## Key Learnings
- **The contract governs writing, the handoff governs reading.** This single split removes all versioning machinery: nothing reads a handoff through the contract, so the contract version that produced a handoff is irrelevant at read time. Version-stamping would have been solving a coupling that doesn't exist.
- **Schema-as-data is safe only when the validator reads the same table.** An open self-describing field set is great for extensibility and useless as a behavioral contract unless the validation gate is driven by the same rows — then extending the contract automatically extends both what's validated and what the harness knows to look at.
- **Drift is wanted here and is free.** Immutable rows + append-only set delivers the desired drift with no mutation problem to manage.
- **Self-describing payloads push the cost onto readers, cheaply.** The price of no-versioning is shape-tolerant readers, which is the same open-set posture petal-dispatch already takes for event types.
## Follow-ons
- [ ] Reconcile against the `execution-journal` vault plan (NEXT) — the handoff model (derived tier, contract table, id hierarchy, write/read split) almost certainly reshapes it; check for conflicts and overlap.
- [ ] Carry the contract + handoff spec into the eventual superseding `synthesis-layer` project-plan once reconciliation is done.
- [ ] Phase 0 ask to `petal-dispatch` now also includes: a `contract` table (append-only, immutable rows) and a validation gate on the `derived` write path — in addition to the `derived` record type and the `project_id`/`session_id`/`task_id`/`handoff_id` columns.
- [ ] Discipline to state in the plan: contract `field_key` is stable and never reused with a different meaning (supersession is a new row, never an edit) — keeps self-describing recovery honest even though it's not relied on at read time.