UCCA — Trace Emission Spec¶
What this is. The envelope spec's hard rule (§3): the trace map is recorded at generation time, never reconstructed afterward. The finding: the generator currently emits nothing. This spec designs the emission — the record, the moment, the store, and the collection — and resolves the one real tension in the design: trace emits at generation, but IDs mint at assembly.
1. The tension, named¶
ENVELOPE-MAPPING.md (grounded, correct against the frozen schema) mints module_id / block_id / question_id at assembly. The envelope spec §3 requires trace records at generation. A generation-time trace record therefore has nothing stable to point at — the content ID it traces doesn't exist yet. Any bridge built of provisional references reconciled later is a reconstruction step in costume, and reconstruction is the forbidden shape.
2. The resolution (proposed ruling): IDs mint at generation¶
Move ID minting into the generation loop. The moment the generator produces an element, it mints the element's ID and emits the trace record in the same breath — the binding between requirement and content is created with the content, atomically.
This is compatible with every frozen constraint: the schema requires IDs be minted, unique, and stable within artefact version — it does not care when minting happens. Regeneration still = new version + new IDs (the generation loop mints fresh every run, which makes that rule automatic rather than enforced). Assembly's job simplifies from "mint and structure" to "collect and structure."
Consequence: ENVELOPE-MAPPING.md's "mint at assembly" amends to "minted at generation, collected at assembly" — one line, dated, citing this spec. Flag, don't silently fix; Alex reconciles his own doc.
3. The trace record¶
One record per element-generation event, emitted at the moment of generation:
{
"content_id": "…", // the ID minted this instant (module/block/question level)
"content_kind": "module|block|question",
"requirement_refs": ["…"], // the triumvirate refs this element was generated AGAINST (≥1)
"generated_at": "ISO-8601",
"request_id": "…" // the LLM call that produced it (joins the cost ledger)
}
Notes:
- requirement_refs is plural — one element may address several
requirements; one requirement may be covered by several elements. The trace
relation is many-to-many by nature.
- request_id joins trace to cost — the same request_id the cognitive-cost
ledger records. One key links "what this element cost" to "what it was
generated against." (Future billing and future audit meet at this join;
price-tag location noted, nothing built.)
- Exact field names conform to the frozen pack's trace-map vocabulary at
assembly time — the record above is the internal emission shape; the
assembler maps it onto the pack's surface. Verify against frozen bytes.
4. What "generated against" means (the honesty definition)¶
A trace record may be emitted if and only if the requirement ref was in the generation context for that element — the requirement (its text/structure from the triumvirate payload) was part of what the LLM was instructed with when it produced the element. That is the by construction claim, made precise.
Forbidden emissions: - A record for a requirement that was merely nearby (in the same module's scope but not in the element's generation context). - A record added because post-hoc reading suggests the content happens to address a requirement. That is diagnosis; it is unbuilt, differently warranted, and never enters a generation trace map.
Untraced content is honest content. Glue text, introductions, transitions — elements generated for coherence rather than against a requirement — carry no trace records, and their absence from the trace map is truthful. The trace map reports what was constructed against requirements; it is not obliged to be total. (The internal validation gate checks closure — every emitted ref resolves — never totality.)
5. The store (reuse the proven pattern)¶
Trace records append to a run-local JSONL as they're emitted —
{run_dir}/ai/trace_records.jsonl — the same append-only pattern as the
cognitive-cost ledger, sitting beside it. Rules inherited from that pattern:
append-only during the run; one record per line; never rewritten. The assembler
reads the file at collection (envelope spec §2 step 2), maps records onto the
pack's trace-map surface, and the internal validation gate checks closure
(every requirement_ref resolves to a ref present in the job's input
triumvirate; every content_id exists in the assembled payload).
Emission failure = generation failure: if a trace record cannot be written, the element's generation has failed (retry per the consumer's budget); the engine never proceeds past an element whose trace is lost. An envelope must never be assembled from a run whose trace file is incomplete relative to its content — that would ship the reconstruction risk §3 of the envelope spec forbids.
6. Where emission lives in the generation loop¶
The generator's per-element step becomes, atomically:
resolve requirement context → LLM call (request_id) → receive element
→ mint content_id → append trace record → append cost record → element done
Build notes: - The generation loop is already driven by the triumvirate (pointed forward, it generates against requirements) — the requirement_refs for each element are therefore already known at the call site; emission is recording what the loop already knows, not new reasoning. - If any current generation step produces content WITHOUT requirement context (bulk generation of a whole module in one call, say), that step either (a) carries module-level trace granularity honestly (the pack permits finest actually-traced level; module is the floor per Q-B4.1), or (b) is split until element-level context is real. Choosing (a) or (b) per step is Alex's build call — recorded per step, with the granularity the trace map then truthfully claims.
7. What this spec deliberately does not decide¶
- Prompt/context construction details (build territory; §4's definition is the constraint, not the method).
- The pack's exact trace-map field names at the surface (frozen bytes win; assembler maps internal records onto them).
- Anything diagnosis-shaped. Reading content to find what it covers is the other direction of the reasoner, differently warranted, unbuilt, parked.
Trace emission. IDs mint at the moment of making; the trace record is born in the same breath as the element it describes; "generated against" means the requirement was in the room; untraced glue is honest; and a run that loses a trace record loses the element. Collect, never reconstruct. This closes the last design gap in the engine core.