UCCA — Job Spine Spec¶
What this is. The machinery under the frozen contract's job surface (
POST /v1/jobs→ 202 → poll / webhook). The pack promises the surface; this designs the spine: states, stores, the queue message, and the vocabulary rules. Alex builds the spine against this instead of deriving it mid-build.
1. The state machine¶
Five states. Terminal means terminal: completed and failed never transition.
| State | Meaning | Entered when |
|---|---|---|
accepted |
Passed the gate; job-id minted; 202 returned. Warranty begins here (fence ruling: no warranty before the gate, full warranty after). | Gate verdict OK |
queued |
On the queue; no container owns it yet. | Enqueue success |
running |
A container owns it; LLM calls in flight; cost ledger writing. | Consumer pickup |
completed |
Envelope assembled, signed, stored in R2; poll returns it. | Envelope stored + signed |
failed |
Terminal, with machine-readable reason. | Retry budget exhausted or unrecoverable error |
Deliberate absences (do not add without a ruling):
- No cancelled. The frozen pack offers no cancellation. Adding it invents
client surface → v1.1 candidate if ever wanted.
- No retrying state. Retries happen inside running; the client sees
running until terminal. Retry mechanics are engine business, not contract
surface.
2. The three governing rules (ruled by Tim, 2026-07-02)¶
2.1 Gate rejects are not jobs. A payload that bounces at the gate receives a
GATE_* error and no job-id, no row in the jobs table. The job ledger starts
at accepted: every row is something the engine owed work on. (Rejects are logged
— see §6 — but in the ops log, never the job ledger. Two ledgers, two meanings:
the ops log records what knocked; the job ledger records what we owed.)
2.2 Partial failure is failure. A job that dies mid-run returns failed,
never a partial envelope. The envelope is atomic — it exists whole and signed,
or not at all. A half-traced envelope is the false-blessing shape
FOUNDATION-01 §2 exists to prevent. The cost ledger still records every call made
(append-only; money was spent; provenance is honest). No output artefact ships.
2.3 Job rows are permanent. No TTL, no eviction. Volume is trivial and job history is audit surface for a trust company. Revisit only if volume ever makes this a real question; deleting audit trail to save nothing is not a default.
3. Keep, don't ship — the diagnostics rule (Tim, 2026-07-02)¶
Two views of every failure:
- Client view: terminal
failed+ machine-readable reason code. Clean, bounded, final. - Engine view (admin.ucca.online): the full autopsy — failing step, the error
that killed it, all cost-ledger rows for the run, and intermediate work product
stashed in R2 under an engine-side diagnostics prefix
(
diagnostics/{job_id}/…).
Rules: the diagnostics prefix is never client-visible, never signed, never referenced by any envelope. Surfaced to us, summarised to them. The wreckage never ships; the wreckage is never discarded.
4. Failure vocabulary (two-lens rule, applied to error codes)¶
Failure codes speak about the job and the engine's own state — never about the material. Error codes are output surface; the two-lens rule (pack §4.4) applies to them exactly as to envelopes.
Permitted shape: JOB_LLM_UNREACHABLE, JOB_STEP_RETRY_EXHAUSTED {step},
JOB_ENVELOPE_ASSEMBLY_ERROR, JOB_INTERNAL_ERROR.
Forbidden shape: anything readable as a verdict on the client's material
("content inadequate", "coverage insufficient"). The engine reports its own
reasoning state, not judgements. If a failure is caused by material the engine
cannot process, the code still describes the engine's state ("could not proceed
at step N"), and the detail lives in engine-side diagnostics.
5. Stores¶
5.1 D1 — jobs table (job state):
CREATE TABLE jobs (
job_id TEXT PRIMARY KEY, -- minted at accept
status TEXT NOT NULL CHECK (status IN
('accepted','queued','running','completed','failed')),
job_type TEXT NOT NULL, -- 'generation' | 'diagnosis' (501 path)
payload_ref TEXT NOT NULL, -- R2 key of the validated payload
envelope_ref TEXT, -- R2 key of the signed envelope (completed only)
failure_code TEXT, -- machine-readable, per §4 (failed only)
webhook_url TEXT, -- optional, per pack
created_at TEXT NOT NULL, -- ISO-8601 UTC
updated_at TEXT NOT NULL,
completed_at TEXT -- terminal timestamp
);
State transitions update status/updated_at only; rows are never deleted
(§2.3). Placement/storage-locus note: payload_ref/envelope_ref are keys that
tolerate a storage-locus identifier (the residency bake-in from the ADR-0004
relay) — the key format is {locus}/{path} with locus default now.
5.2 D1 — cognitive_cost table (the ledger, migrated from JSONL):
Fields per the on-disk ledger (provider, model, unit_id, request_id,
input_tokens, output_tokens, pricing ref, cost_usd, cumulative_after_usd,
ts_utc) plus job_id. Insert-only: no UPDATE, no DELETE, enforced in the
data layer (and by convention in code review). Provenance-adjacent and future
billing data; an updatable ledger is not a ledger.
5.3 R2 layout:
{locus}/payloads/{job_id}.json -- validated payload (gate-passed bytes)
{locus}/envelopes/{job_id}.json -- signed envelope (completed jobs only)
{locus}/diagnostics/{job_id}/… -- engine-side autopsy (never client-visible)
6. The gate's reject behaviour (precise, mechanical, structural)¶
Rejects say why — with a GATE_* code and the location of the structural
fault. The conformer needs precision to fix and resubmit; a silent bounce is
hostile. Examples of shape: GATE_SCHEMA_VERSION (unsupported triumvirate
version), GATE_SIGNATURE (adapter signature invalid), GATE_STRUCTURE
(+ field pointer), GATE_SIZE, GATE_RATE.
The why is always structural, never semantic. "Field X missing" — yes. Anything that drifts toward judging content — no. Semantics are the conformer's warranty (fence ruling); the gate never re-checks them and must never appear to have judged what it did not judge.
Rejects are logged engine-side in an ops log (full reject record: code, timestamp, fault location, source identity) so persistent gate-failure patterns are visible in admin — but per §2.1, never in the jobs table.
7. The queue message¶
The queue carries pointers, never payloads.
The validated payload lives in exactly one place (R2); the queue moves a claim ticket. This keeps the payload from existing in two systems, keeps queue messages tiny, and makes replay/redelivery safe (redelivery re-reads the same bytes).
8. Webhook (per pack, optional)¶
On terminal transition, if webhook_url present: POST {job_id, status} —
status only, no envelope in the webhook body; the client polls for the envelope.
Failures of webhook delivery are logged, retried on a small budget, then dropped
— webhook delivery is best-effort and never affects job state.
9. What this spec deliberately does not decide¶
- Retry budgets and backoff numbers (Alex's implementation call; record them in code and in the findings when chosen).
- Container concurrency (ADR-0004: cap is a first-class control, initial 2).
- The diagnosis path internals (501 per the pack; full gate checks run; a
rejected-or-accepted job that reaches the diagnosis runner returns the honest
501 and the job terminates
failedwithJOB_TYPE_NOT_IMPLEMENTED— the one pre-agreed exception shape, since the pack promises the stub). - Anything client-visible beyond the frozen surface. Named v1.1 candidates remain: cancellation, thinking-tiers, residency selection.
Job spine. Five states, terminal means terminal; the ledger starts where the warranty starts; failure ships clean and keeps the wreckage; the gate says precisely why in structure and nothing in semantics; the queue moves claim tickets, not cargo. Build against this; amend it by finding, not by drift.