UCCA — Envelope Assembly Spec¶
What this is. The generator produces a run bundle; the contract promises a signed envelope with four parsed surfaces. This spec designs the assembly between them: what maps, in what order, validated how, signed when. It is the last undesigned gap before the proof job.
1. The four surfaces (pointers, not restatements)¶
The envelope's surfaces are defined by the frozen pack and the v1 JSON schema — build against those bytes, not this list:
- Content payload — per
ucca-content-payload-schema-v1.json, verbatim. Blocks encodedmodules[].blocks[]; IDs at module/block/question level. - Trace map — at
source_refgranularity, per the pack. - Provenance block — per the pack's frozen definition (the one that passes the client's Q-A3 completeness test). Citation kinds split per the standing ruling: dated raw snapshot = pointer; normalised hash = attestation.
- Signatures — machine signatures attesting provenance only. Never a
human compliance signature; the engine raises hands, never signs
(FOUNDATION-01 §2). Crypto per the existing
/ns/ucco/v1envelope and keys worker (the security reference implementation) — reuse, don't reinvent.
2. Assembly order (strict)¶
run bundle → (1) content payload → (2) trace map → (3) provenance block
→ (4) internal validation gate → (5) envelope hash → (6) sign
→ (7) store {locus}/envelopes/{job_id}.json → job: completed
Rules:
- Signing is last. The signature covers the assembled, validated envelope —
never a draft, never parts.
- Nothing stores to the envelopes prefix unsigned. A failed assembly leaves
its wreckage in diagnostics/{job_id}/ (spine §3) and the job terminates
failed with JOB_ENVELOPE_ASSEMBLY_ERROR. Atomicity per spine §2.2: whole
and signed, or not at all.
3. Trace by construction — the one hard rule of this spec¶
The trace map is recorded at generation time, never reconstructed afterward.
The engine's claim (FOUNDATION-01 §1) is that pointed forward it "generates material that traces coverage by construction." That property is only real if the generator emits a trace record at the moment it generates each content element — this block was generated against that requirement ref — and assembly merely collects those records into the trace map surface.
Forbidden: any post-hoc step that reads finished content and infers which requirement it addresses. That is diagnosis (unbuilt, and a different warranty), and a reconstructed trace shipped as a constructed one would be a false claim in a signed artefact — the exact false-blessing shape the engine exists to prevent.
Build consequence: if the current generator does not already emit per-element trace records, adding that emission is part of wiring the engine core — it is not optional instrumentation. Every trace record carries the requirement-side ref (the triumvirate anchor) and the content-side ID it produced.
4. ID minting and versioning (per the frozen rulings)¶
- Module/block/question IDs are minted at assembly, stable within artefact version only. Regeneration = new artefact version = new IDs, minted fresh — never reused, never patched.
- The requirement side (the triumvirate refs) is the cross-version anchor. Anything longitudinal (comparing versions, client-side diffing) anchors on requirement refs, never on content IDs.
- The artefact version identifier lives where the v1 schema puts it — verify against the schema bytes.
5. The internal validation gate (step 4, before hash/sign)¶
Assembly validates before signing. Minimum checks:
- Schema conformance — the content payload validates against
ucca-content-payload-schema-v1.json(the literal file, in CI and at runtime). - Trace closure — every trace-map entry's requirement ref resolves to a ref present in the job's input triumvirate payload; every content element carries at least the trace records the generator emitted for it; no orphan refs either direction. (Closure ≠ coverage: this checks the map is internally sound, not that coverage is complete — coverage claims are the trace map's content, not the validator's judgement.)
- ID uniqueness — all minted IDs unique within the artefact version.
- Provenance completeness — every field the pack's provenance definition requires is present and typed correctly (mechanical presence check against the frozen definition).
- Two-lens vocabulary sweep — no field in any surface speaks about persons or renders verdicts on material (pack §4.4). Output speaks about material and the engine's own reasoning, in the pack's vocabulary.
Any check fails → no hash, no signature, no envelope; wreckage to diagnostics;
job failed. The validator's output (which check, where) goes in the
diagnostics, engine-side only.
6. Signing (reuse the machinery)¶
- Envelope hash computed over the assembled surfaces (canonical serialisation —
match whatever
/ns/ucco/v1already does; don't invent a second canon). - Signature via the keys worker; the envelope carries the verification method / key ID per the existing envelope format.
- What the signature attests, stated in the envelope where the pack puts it: provenance of the artefact — that this engine produced these surfaces from that input at this time. It attests nothing about compliance, adequacy, or fitness. If any field's name or description could read as the latter, it fails the §5.5 sweep.
7. Mapping from the run bundle (observation → intent, the standing lesson)¶
human_runs/…CHCPRT025…/outputs/course_data.json is observation-typed — it
records what a past run happened to produce. The v1 schema is intent-typed —
what is promised. The mapping is derived by Alex against the schema, using
course_data.json as evidence of available material, never as the target shape.
Where the bundle lacks something the schema requires, that is a build finding
(the generator must start producing it), not a schema problem.
8. The test path (Tim's ask: get what's built in place and test it)¶
Golden-run sequence, in order, each step exercising more of the spine:
- Assembly unit test — feed the CHCPRT025 bundle to the assembler directly (no spine); assert a valid, signed envelope or a precise validator failure. This will immediately surface every §7 mapping gap — that's its job.
- Spine integration — a synthetic conformed payload through
gate → queue → consumer with the engine core stubbed to replay the CHCPRT025
material; assert the full envelope lands at
{locus}/envelopes/{job_id}.jsonand the poll returns it. - Live run — same payload, real LLM calls (GLM-5.2 per ADR-0004; ~one free course/day on the daily allowance), cost ledger writing, trace emitted by construction.
- The proof job — a genuinely conformed payload end-to-end to terminal effect on the deployed runtime. That run mints ADR-0002 and ADR-0004.
Steps 1–3 are pre-deploy (local/dev per Alex's judgement); deploy remains gated on the core being real, per the standing rule that a non-functional gate doesn't go live.
9. What this spec deliberately does not decide¶
- Field-by-field mapping table (derived by Alex against the frozen schema per §7; recorded as a build artefact when derived).
- Canonical serialisation details (reuse
/ns/ucco/v1's existing canon). - Anything the diagnosis direction will need (parked with diagnosis).
- Any change to a frozen shape — divergences found while building are v1.1 candidates, flagged not resolved.
Envelope assembly. Four surfaces the pack defines, assembled in one order, validated before hashed, hashed before signed, signed before stored — and the trace map is born during generation or not at all. The pack wins every disagreement with this page. Build the mapping, run the golden tests, then the proof job mints two ADRs at once.