UCCA — Input-Path Build Brief¶
What this is. Pack §10 expanded into a reviewable build plan for the input path. Five discrete, independently mergeable steps. This is engine-side work only — no fence crossing. Every step carries its drift-check. The build is downstream of the freeze: this brief reads from the frozen pack and the v1 schema; it decides nothing new. Anything the pack left open is flagged in §7, not resolved here.
Spec of record: docs-site/docs/strategy/ucca-input-path-contract-pack-v0.1.md (v1.0 FROZEN) and
ucca-content-payload-schema-v1.json. Section refs below (§2/§3/§4/§10) are the pack's.
Step 1 — The gate (structural verification only)¶
Decided (pack §2): the gate performs structural checks only, no domain knowledge. Each check and its reject code:
| Check | Reject code |
|---|---|
| Authentication (per-client key) | GATE_AUTH |
| Triumvirate schema version accepted | GATE_SCHEMA_VERSION |
| Structural validation: required fields, types, enums, non-empty arrays | GATE_STRUCTURE |
| Adapter signature verifies over the envelope | GATE_SIGNATURE |
| Structural reference integrity (IDs resolve within the job; acyclic prerequisites) | GATE_REFERENCES |
Validation block present + self-consistent (adapter's validate() ran) |
GATE_UNVALIDATED |
| Size / rate limits | GATE_LIMITS |
Build: the gate runs at the Cloudflare edge (skin/body split, §10). Validate the submitted
triumvirate against engine/triumvirate/schema.py (v1.0). Malformed bounces WHOLE — no partial
acceptance; nothing enters the engine on any gate failure. Semantics (does the packaging make
sense? is the clause mapping faithful?) are the conformer's warranty and are never re-checked here.
Drift-check: none of these checks may reference a VET concept. If a check only passes because the input is VET-shaped, it belongs in the client's adapter, not the gate.
Gaps flagged → §7: the adapter-signature scheme (7.1), the auth key mechanism (7.2), the size/rate limit values (7.3).
Step 2 — Error codes (the error object)¶
Decided (pack §2): error objects follow the March error-model shape: machine code, severity,
phase, affected element, message, remediation. The GATE_* codes above are the gate's set. The
March ING_/TRN_ taxonomies are conformer-side and out of contract scope — do not implement them
engine-side.
Build: one error object type, emitted on 4xx from POST /v1/jobs when a gate check fails.
Deterministic: the same malformed input yields the same code every time (gate determinism is a
warranted property, pack §8).
Drift-check: error messages describe structure ("required field missing", "signature invalid"), never domain judgement ("unit not competent") — that would be the engine judging domain sense, which it does not do.
Step 3 — Job lifecycle (async, one model, two directions)¶
Decided (pack §3):
- POST /v1/jobs — body { job_type, triumvirate, ...type-specific } → 202 {job_id, status:"accepted"}
after gate checks pass; gate failures return 4xx + error object.
- GET /v1/jobs/{job_id} — status accepted | running | complete | failed, UCCO envelope on complete.
- POST /v1/webhooks — optional completion callback, per client.
- No sync generation call. No latency/SLA guarantee in-contract.
- job_type: generation — live (R-1 + R-3), optional contextualisation_brief (§5).
- job_type: diagnosis — honest 501 stub: the endpoint exists, the gate checks RUN in full
(integration is real), then returns 501 {reason:"diagnosis_not_yet_available", contract_version}.
Body shape reserved {triumvirate, documents[]}. When R-2 lands, only the 501 is removed.
Build: an async job store behind the gate; the reasoner (behind the edge) picks up accepted
jobs, moves them running→complete|failed. The job-store technology is an engine-side
implementation choice (no client-facing contract impact beyond the polling shape above) — pick per
substrate, not per this brief.
Drift-check: the job model is domain-neutral by construction — it carries a triumvirate, not a qualification. Keep it that way.
Gaps flagged → §7: webhook callback semantics — retry, and whether the callback is signed (7.4); the reasoner's execution substrate (7.5).
Step 4 — Envelope assembly (the four parsed surfaces)¶
Decided (pack §4): the engine returns a UCCO envelope (/ns/ucco/v1; see engine/envelope.py,
renderer/vc.py). The client parses exactly four surfaces; everything else is opaque pass-through.
- Content payload — frozen to
ucca-content-payload-schema-v1.json(module/block/question IDs stable within artefact version; provenance excluded — lives in §4.3; fossils dropped). - Trace map —
{ requirement_ref(instrument,id,source_ref), element_ref, relation }at source_ref granularity;element_ref= finest ID actually traced, module-level minimum. - Provenance block — every §4.3 field satisfiable from the envelope alone (Q-A3 completeness):
run_id, timestamps,direction,triumvirate_schema_version+triumvirate_hash,corpus_citation(pointer vs attestation — never merged),contextualisation_brief_id+ hash; diagnosis addsreport_id+hand_raises[](stable ID + exact requirement ref);document_citations[](raw→canonical hash, distinct from corpus_citation). - Signatures — engine signature over the envelope + adapter signature carried through. Machine signatures attest provenance ONLY (D-5) — nothing in the envelope carries or substitutes for a human compliance signature.
Output-vocabulary constraint (pack §4.4, canon-bound): every emitted field/phrase speaks about material — trace / no-trace, present / not-traceable — never compliant/competent/pass/fail. No field is a verdict. This is testable at review and will be tested — write the test with the build.
Drift-check: the two-lens rule (raises hands, never signs; speaks about material, never persons) is not a VET nicety — it is the property that makes every client's artefacts audit-defensible. Do not special-case it for VET.
Step 5 — re-source contextualisation, then sever worlds/¶
Decided (ADR-0002 + pack §10 DoD): the engine carries no domain knowledge. worlds/ (~115M),
plus tga_data/ and tga_pdfs/, still live inside the engine repo — the live half of the ADR-0002
violation.
Amendment (2026-07-02, ratified). Recon found the live generator imports contextualisation from
worlds/—generator/generate_complete_course.py:46: from worlds.rtopacks.config.cultural_flavours import get_cultural_flavour(call sites lines 865–874). So severingworlds/naively breaks the generator. The severance is therefore three clauses in order, not one — and the split is mechanism from content, which is the subtle part:
5.1 — Re-source the contextualisation MECHANISM into the engine, before the severance diff. The
logic that applies voice/jurisdiction/audience adaptation to generation stays engine-owned. The
generator already holds the application hook (it accepts cultural_flavour= at line 874); the change
is to its input source — take the directive from the job's opaque contextualisation_brief
(pack §5), not from a worlds/ import.
5.2 — The contextualisation CONTENT does NOT move into the engine. cultural_flavours (the MATE
voice text) and voice_guidance.py (200 lines of TGA-keyed AU framing — whose own docstring says it
"belong[s] to the RTOpacks world, not to the engine") are client domain content: they fail the
drift-check. Relocating them wholesale to an engine-owned path would move the ADR-0002 violation, not
fix it. That content becomes payload-side data, arriving per-job via the opaque brief (pack §5).
The two client fossils in worlds/ (rtopacks + usa_for_later) are the proof case: correctly split,
the mechanism serves both with zero client code in the engine.
5.3 — The severance diff then cuts worlds/ whole (git rm -r worlds/ tga_data/ tga_pdfs/), as a
standalone reviewed commit. Grep the engine for residual worlds/ imports; the engine must build and
the input path must run with worlds/ gone. This is the diff that, with a live path and one exercised
job, mints ADR-0002's remedy.
Sizing finding (report, not absorbed). Assessed: the mechanism/content split is bounded — it
stays a Step 5 sub-clause, not its own step. Evidence: the generator already accepts the applied
directive (cultural_flavour=), so 5.1 is a one-input rewire, not a new subsystem; 5.2 is relocating
~260 lines of content data (56-line cultural_flavours + 204-line voice_guidance) to the client/
payload side. If, on building, 5.1 proves to need a real contextualisation-application subsystem
(beyond prompt-prepending), promote it to its own step and re-report.
Drift-check: after severance, re-run "does anything here only work because the client is VET?"
against the whole gate/reasoner. If yes, it followed worlds/ out the door incorrectly — it belongs
in the adapter (content) or the payload brief, still outside the engine.
6. Definition of Done — three independently verifiable legs¶
The register flips ADR-0002's remedy ruled (B, pre-proof) → minted only when all three are
shown, each with its own clean evidence:
| Leg | Independently verified by |
|---|---|
| A. Input path live | POST /v1/jobs returns 202 for a well-formed signed triumvirate and 4xx + GATE_* for each malformed class; /_health + /_build (security posture) respond. Curl transcript. |
| B. One conformed job exercised gate→engine→envelope to terminal effect | A single real conformed job runs accepted→complete; GET /v1/jobs/{id} returns a UCCO envelope with all four surfaces populated, signed, provenance passing the Q-A3 completeness check. The envelope itself is the evidence. |
C. worlds/ severed |
worlds//tga_data//tga_pdfs/ absent from the engine repo; grep for imports clean; engine builds and leg B still passes without them. The severance commit SHA is the evidence. |
Keep the three legs separable so the register flip cites three clean artefacts, not one entangled claim.
7. v1.1 clarification candidates (surfaced, NOT resolved here)¶
Drafting this brief surfaced these — the frozen pack did not decide them. Do not resolve them in the build; raise them. Those marked (cross-fence) need RTOpacks; the pack froze the shape, not these mechanics.
- 7.1 Adapter-signature scheme (cross-fence). §2 requires the gate verify the adapter signature "over the envelope," but the algorithm, key distribution, and canonicalization (exactly which bytes are signed) are not pinned. The gate cannot verify a signature without an agreed scheme.
- 7.2 Auth key mechanism (cross-fence). "Per-client key" (§2/§10) — the concrete carrier (header name, format, rotation) is unspecified; the client must know how to authenticate.
- 7.3 Size / rate limit values.
GATE_LIMITShas no numbers in the pack; the client needs the limits it will be held to. - 7.4 Webhook callback semantics. "Optional completion callback" (§3) — retry policy and whether the callback is signed are undefined.
- 7.5 Reasoner substrate. §10's "reasoner in its real runtime (the substrate ruling)" references a substrate ruling not filed in this repo. Confirm it before wiring the reasoner host; out of scope for this brief.
Build brief, downstream of the freeze. Five reviewable steps, one severance diff, three verifiable DoD legs, five honest gaps raised not buried. Intent-typed from the pack; the pack still wins. Build the input path; mint ADR-0002.