Skip to content

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: generationlive (R-1 + R-3), optional contextualisation_brief (§5). - job_type: diagnosishonest 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 runningcomplete|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.

  1. 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).
  2. Trace map{ requirement_ref(instrument,id,source_ref), element_ref, relation } at source_ref granularity; element_ref = finest ID actually traced, module-level minimum.
  3. 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 adds report_id + hand_raises[] (stable ID + exact requirement ref); document_citations[] (raw→canonical hash, distinct from corpus_citation).
  4. 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 severing worlds/ 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 acceptedcomplete; 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_LIMITS has 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.