# Schema reference

> Every object and field of OCP v0.3, generated from the JSON Schema.

> **Note:** The schema is JSON Schema draft 2020-12. Download it at [`ocp-v0.3.schema.json`](https://github.com/loopmath/loopmath/blob/main/spec/ocp-v0.3.schema.json). The schema and the meaning its description strings give each field are normative. With the [numbered rules](https://orchestrationcontextprotocol.io/specification/0.3/conformance) JSON Schema cannot express, they define OCP.

## Top-level fields

The fields of the document object itself.

- `ocp` (\"0.3\", required): Protocol version. This schema validates exactly version 0.3; v0.2 documents validate against ocp-v0.2.schema.json and v0.1 documents against ocp-v0.schema.json. A v0.3 reader accepts all three.

- `producer` ([Producer](#producer), required)

- `privacy` ([Privacy](#privacy), required)

- `run` ([Run](#run), required)

- `groups` ([Group](#group)[]): Optional named visual/organizational scopes, such as projects. Grouping is orthogonal to dependency structure.

- `nodes` ([Node](#node)[], required): The units of work. A gate is a node whose kind is `gate`.

- `edges` ([Edge](#edge)[]): Directed edges over node ids. `dep` and `fan_in` edges together must form a DAG; `spawn`, `launch` and `artifact` edges record discovered lineage and may form cycles.

- `attempts` ([Attempt](#attempt)[]): Flat list of tries, each pointing at its node. Attempts are records, not counters; a retry is a new attempt.

- `artifacts` ([Artifact](#artifact)[]): Files (or other addressable outputs) that one attempt produced and other attempts consumed. The handoff objects between attempts; the run records who wrote and who read them, never their content.

- `events` ([Event](#event)[]): Optional append-only provenance log, ascending in time.

- `ext` ([Ext](#ext))

## Document

### `Producer`

- `name` (string, required): The emitting software (plugin or converter), e.g. `my-orchestrator-ocp`. loopmath's log extractor names itself `loopmath` (`dagr` in documents written before its rename), optionally suffixed after a non-word character, e.g. `loopmath/graph`; that name switches on the extractor-only rule E171 (cost.basis `measured`).

  At most 200 characters.

- `version` (string): At most 100 characters.

- `framework` (string): The orchestration framework the run executed under, e.g. `my-orchestrator`, `claude-code`, `langgraph`, `crewai`.

  At most 200 characters.

- `framework_version` (string): At most 100 characters.

- `source_contract` (string): Upstream native format this document was derived from, e.g. `my-orchestrator/1.2`.

  At most 200 characters.

- `emitted_at` ([Timestamp](#timestamp))

- `capabilities` (object): Elements this producer can emit. A false value promises that the corresponding element is absent from this document. Unknown boolean keys are producer extensions.

  *properties*

  - `groups` (boolean)

  - `events` (boolean)

  - `artifacts` (boolean)

  - `edges_dep` (boolean)

  - `edges_spawn` (boolean)

  - `edges_launch` (boolean)

  - `edges_artifact` (boolean)

  - `cost_usd` (boolean)

  - `cost_tokens` (boolean)

  - `outcome_evidence` (boolean)

  - `task` (boolean)

  - `configuration` (boolean)

  - `signals` (boolean)

  - `slate` (boolean)

  - `receipt` (boolean)

- `ext` ([Ext](#ext))

### `Privacy`

- `profile` (enum, required): metadata_only: no prompt, completion, or transcript text anywhere in the document; free-text fields hold only short producer-authored labels. full: text may appear in ext namespaces (never in core fields).

  One of: `metadata_only`, `full`.

- `note` ([ShortText](#shorttext))

- `ext` ([Ext](#ext))

### `Ext`

Producer-specific extension data. Keys use reverse-domain namespaces, e.g. `dev.loopmath.graph`, `com.example.thread_id`. loopmath's namespace is `dev.loopmath.`; the pre-rename `dev.dagr.` stays readable in v0.3 and v0.4 with warning W182. Consumers must ignore namespaces they do not understand.

**Type:** object.

### `Labels`

Free-form string key/value labels for stratification (e.g. project family, model family).

**Type:** object.

### `Timestamp`

ISO-8601 / RFC-3339 timestamp with explicit offset.

**Type:** string.

### `ShortText`

Short producer-authored label. Under the metadata_only profile this must never contain prompt or transcript text.

**Type:** string, at most 500 characters.

### `Id`

An identifier of at most 200 characters.

**Type:** string, at most 200 characters.

## Run graph

### `Run`

- `id` (string, required): Producer-scoped stable run id.

  At most 200 characters.

- `title` ([ShortText](#shorttext))

- `started_at` ([Timestamp](#timestamp))

- `ended_at` ([Timestamp](#timestamp))

- `workspace` (string): Opaque project/workspace locator for task-mix stratification (a name or hash, not necessarily a path).

  At most 500 characters.

- `labels` ([Labels](#labels))

- `task` ([Task](#task))

- `configuration` ([Configuration](#configuration))

- `provenance` ([Provenance](#provenance))

- `acceptance_rule` ([AcceptanceRule](#acceptancerule))

- `signals` ([Signal](#signal)[]): Verdicts, scores and late events, each timestamped.

- `slate` ([Slate](#slate))

- `preferences` ([Preference](#preference)[])

- `rescue` ([RunRescue](#runrescue))

- `receipt` ([Receipt](#receipt))

- `ext` ([Ext](#ext))

### `Group`

- `id` (string, required): At most 200 characters.

- `title` ([ShortText](#shorttext))

- `parent` (string): Another group id; parent edges must be acyclic.

  At most 200 characters.

- `ext` ([Ext](#ext))

### `Node`

- `id` (string, required): Stable identity independent of any attempt. Never a runtime locator such as a pane id.

  At most 200 characters.

- `kind` (string, required): Open set. Recommended vocabulary: impl, review, test, gate, question, docs, ship, plan, ops, task, unknown. `gate` marks an acceptance fan-in point. `task` is a generic task; `unknown` means the producer cannot determine a more specific kind.

  At most 100 characters.

- `title` ([ShortText](#shorttext))

- `group` (string): Optional group id; omitted means the run root.

  At most 200 characters.

- `state` (string): Producer's current projection over the node's attempts. Recommended vocabulary: queued, working, review, blocked, done, failed, rejected, canceled, settled_unverified. Consumers learning from settled runs should trust attempt outcomes over this field.

  At most 100 characters.

- `labels` ([Labels](#labels))

- `ext` ([Ext](#ext))

- `vertex` (string): The workflow piece id this node executes.

  At most 200 characters.

- `gate` (object): Present when the node is a gate.

  *properties*

  - `rule` (string): The acceptance rule this gate implements, when it is one.

    Recommended values (open set): `referee`, `tests`, `merge`, `ci`.

    At most 100 characters.

  - `disposition` (object): Opaque references to how the gate judges.

    *properties*

    - `rubric` (string): At most 500 characters.

    - `severity_map` (string): At most 500 characters.

    - `grammar` (string): At most 500 characters.

    - `ext` ([Ext](#ext))

  - `command` (string): The command a mechanical gate runs.

    At most 1000 characters.

  - `ext` ([Ext](#ext))

### `Edge`

- `from` (string, required): Source node id (the prerequisite / input / spawner / launcher / producer's node).

  At most 200 characters.

- `to` (string, required): Target node id (the dependent / gate / spawned node / launched node / consumer's node).

  At most 200 characters.

- `kind` (string, default dep): dep: `to` cannot start before `from` settles. fan_in: `from` is a member of gate `to`'s acceptance input set. spawn: node `to` was dynamically created by work on `from` inside the same harness (a Claude Code subagent through the Task tool). launch: work on `from` started node `to` as a separate harness session through its CLI (a Bash call running `codex exec` or `claude -p`). artifact: an attempt at `from` wrote the artifact named by `artifact` and an attempt at `to` later read it; from_attempt and to_attempt name the two attempts. Other values are accepted, but only dep and fan_in participate in the acyclicity rule.

  Recommended values (open set): `dep`, `fan_in`, `spawn`, `launch`, `artifact`.

- `tier` ([EvidenceTier](#evidencetier), required): How the edge is known. Required on every v0.2 edge, whoever the producer is: an edge without an evidence tier does not conform.

- `artifact` (string): For kind `artifact` (required there): the id of the artifact that flowed along this edge. Must name an entry in `artifacts`.

  At most 200 characters.

- `from_attempt` (string): For kind `artifact` (required there, optional elsewhere): the producing attempt id. Must be an attempt at node `from` and equal to the artifact's producer.

  At most 200 characters.

- `to_attempt` (string): For kind `artifact` (required there, optional elsewhere): the consuming attempt id. Must be an attempt at node `to` and among the artifact's consumers.

  At most 200 characters.

- `evidence` ([ShortText](#shorttext)): Short producer-authored note on what established the edge, e.g. `Task tool_use id matched subagent meta` or `codex session began 3 s after codex exec in the same workspace`.

- `ext` ([Ext](#ext))

## Attempts

### `Attempt`

- `id` (string, required): Globally unique within the document.

  At most 200 characters.

- `node` (string, required): The node this attempt tries.

  At most 200 characters.

- `n` (integer): 1-based ordinal within the node.

- `actor` (string): Logical agent/role that ran the attempt (not a volatile runtime locator).

  At most 200 characters.

- `harness` (string): The agent harness that ran the attempt, e.g. `claude-code`, `codex`.

  At most 100 characters.

- `model` ([ModelRef](#modelref))

- `effort` (string): Reasoning-effort label as configured, e.g. low, medium, high, xhigh, max. Open set; producers must not fold it into the model label.

  At most 100 characters.

- `cause` ([Cause](#cause))

- `origin` ([Origin](#origin)): How this attempt came to run: which scanned attempt launched it through a CLI, in which workspace, or that an unscanned program did.

- `role` ([Role](#role)): What part the attempt played in the run (lead, planner, dev, reviewer, ...), with the tier and evidence the label rests on.

- `phase` ([Phase](#phase)): When in the run's life the attempt ran (build, post, external), with tier and evidence.

- `status` (enum, required): Current or terminal status. queued: created, not started yet. working: running now. The terminal statuses are done, failed, rejected, canceled, settled_unverified and lost, each carrying the meaning outcome.result gives it. When terminal, an outcome record should be present and outcome.result must equal status.

  One of: `queued`, `working`, `done`, `failed`, `rejected`, `canceled`, `settled_unverified`, `lost`.

- `started_at` ([Timestamp](#timestamp))

- `ended_at` ([Timestamp](#timestamp))

- `outcome` ([Outcome](#outcome))

- `cost` ([Cost](#cost))

- `session` (string): Opaque correlate to the underlying harness session/transcript id, for producers that can join the two. Never transcript content.

  At most 500 characters.

- `labels` ([Labels](#labels))

- `ext` ([Ext](#ext))

- `vertex` (string): The workflow piece id this attempt executes.

  At most 200 characters.

- `round` (integer): Repair round, 1-based.

- `setting` ([Setting](#setting)): Only when it differs from the configuration's setting for the vertex.

- `cwd` (string): Working folder, opaque.

  At most 1000 characters.

### `ModelRef`

- `raw` (string): The label exactly as the producer knew it, always preserved (e.g. `fable·xhigh`, `opus5.xhigh`). Canonicalization is the consumer's job; the raw label is the ground truth.

  At most 200 characters.

- `id` (string): Canonical provider model id when known, e.g. `claude-fable-5`.

  At most 200 characters.

- `family` (string): Coarse family label for grouping attempts by model family, e.g. `fable`, `opus5`, `sol5.6`.

  At most 100 characters.

- `provider` (string): e.g. `anthropic`, `openai`.

  At most 100 characters.

- `tier` ([EvidenceTier](#evidencetier)): How the model label is known: verified from per-request usage records, reported from a harness field, heuristic from a name pattern.

- `ext` ([Ext](#ext))

### `Cause`

- `type` (string, required): Why this attempt exists. initial: the first attempt for its node. sent_back: a review rejected earlier work. gate_failed: an acceptance gate failed. followup: continuation of earlier work. superseded: replaces an earlier attempt. other: a trigger that fits none of the named values.

  Recommended values (open set): `initial`, `sent_back`, `gate_failed`, `followup`, `superseded`, `other`.

- `ref` (string): The earlier attempt id this cause points back to.

  At most 200 characters.

- `by` (string): Who triggered it (reviewer, operator, policy rule).

  At most 200 characters.

- `reason` ([ShortText](#shorttext))

- `ext` ([Ext](#ext))

### `Outcome`

- `result` (enum, required): Terminal result. done: accepted. failed: attempted and did not succeed. rejected: refused by a review or a gate. canceled: stopped before it settled on its own. settled_unverified: settled without a parseable acceptance signal. lost: the runtime vanished.

  One of: `done`, `failed`, `rejected`, `canceled`, `settled_unverified`, `lost`.

- `evidence` (string): How the result is known. verified: mechanically checked (tests, provenance receipt). reported: the actor asserted it through a typed envelope. heuristic: inferred. asserted: bare claim. Missing evidence must be treated as `asserted`, never upgraded.

  Recommended values (open set): `verified`, `reported`, `heuristic`, `asserted`.

- `via` (string): Node id of the gate or review that produced this acceptance signal, when one did.

  At most 200 characters.

- `receipt` ([ShortText](#shorttext))

- `reason` ([ShortText](#shorttext))

- `ext` ([Ext](#ext))

### `Origin`

How the attempt came to exist. Every field is present; an unknown value is an explicit null, never an absent key.

- `launched_by` (string | null, required): Attempt id of the launcher, when a scanned attempt started this one through a CLI. A `launch` edge from the launcher's node to this attempt's node must exist. null when no scanned attempt is the launcher (top-level, unknown, or external). Must be null when `external` is true: the two contradict each other.

  At most 200 characters.

- `workspace` (string | null, required): Workspace the launched harness reported as its working directory (an opaque locator, as run.workspace); null when unknown.

  At most 500 characters.

- `external` (boolean | null, required): true when the harness reports it was launched by another program (e.g. codex originator `codex_exec`) but no scanned attempt is the launcher: launched by an unscanned session, which is not the same as not launched. false when the attempt is known not to have been launched that way (a top-level session, or one whose launcher is in `launched_by`). null when the harness gives no signal.

- `how` ([ShortText](#shorttext) | null, required): The launch mechanism, e.g. `codex exec`, `claude -p`, `via_script: tools/review.sh`; null when unknown.

- `tier` ([EvidenceTier](#evidencetier), required)

- `evidence` ([ShortText](#shorttext))

- `ext` ([Ext](#ext))

### `Role`

What the attempt did in the run. All three fields are present; evidence is null when there is nothing to cite.

- `value` (string | null, required): Open set. Recommended vocabulary: lead (top-level session that spawned or launched others), solo (top-level session that did not), planner, dev, reviewer, cli (launched through a CLI with no role signal), subagent. null when the role is unknown; the key is always present and the tier stays required beside it.

  At most 100 characters.

- `tier` ([EvidenceTier](#evidencetier), required)

- `evidence` ([ShortText](#shorttext) | null, required): What the label rests on, e.g. "declared type `Plan`" or `launch command mentions review`; null when nothing can be cited.

- `ext` ([Ext](#ext))

### `Phase`

When in the run the attempt happened. All three fields are present; evidence is null when there is nothing to cite.

- `value` (string | null, required): Open set. Recommended vocabulary: build (during the lead's activity), post (after the lead ended, e.g. a post-build review), external (outside the run's workspace). null when the phase is unknown; the key is always present and the tier stays required beside it.

  At most 100 characters.

- `tier` ([EvidenceTier](#evidencetier), required)

- `evidence` ([ShortText](#shorttext) | null, required): What the label rests on; null when nothing can be cited.

- `ext` ([Ext](#ext))

### `EvidenceTier`

How an inferred label or edge is known. verified: mechanically established from the logs (ids match, both events present). heuristic: a rule inferred it (timing, text pattern, path containment). reported: a session, harness or model asserted it and the producer took it at face value. A producer must never upgrade a tier without new mechanical evidence.

One of: `verified`, `heuristic`, `reported`.

### `OutcomeTier`

How a signal or judgement is known. verified: mechanically checked (tests, CI, a provenance receipt). reported: a typed report from the orchestrator, user or tool. heuristic: inferred by a rule. asserted: a bare claim. Tiers are never upgraded without new mechanical evidence.

One of: `verified`, `reported`, `heuristic`, `asserted`.

## Cost

### `Cost`

Token and money cost of one attempt, summed over all API requests the attempt made. Cached input is separated from uncached because the two are priced differently and blur cost comparisons when merged; cache creation is split by retention bucket for the same reason.

- `input_tokens` (integer): Uncached input tokens.

- `cached_input_tokens` (integer): Input tokens served from cache (cache reads).

- `cache_creation_tokens` (integer): Input tokens written to cache, all retention buckets together, when the provider bills them separately. When the two bucket fields are also present their sum must equal this.

- `cache_creation_5m_tokens` (integer): Cache-creation tokens in the 5-minute retention bucket.

- `cache_creation_1h_tokens` (integer): Cache-creation tokens in the 1-hour retention bucket, priced higher than the 5-minute bucket.

- `output_tokens` (integer)

- `reasoning_tokens` (integer): Reasoning tokens, as a breakdown of output_tokens: output_tokens includes them, as OpenAI and Anthropic report them. A producer whose source reports reasoning separately adds it into output_tokens.

- `requests` (integer): Number of model API requests aggregated into this record.

- `usd` (number): Billed cost in US dollars when the producer knows it. Consumers should prefer recomputing from tokens and a dated price table.

- `basis` (string): How the counts were attributed to this attempt. measured: summed from usage records that identify this attempt exactly (a per-session or per-agent transcript, or per-request usage tied to the attempt). allocated: apportioned from a coarser aggregate (per-day or per-worktree usage) by a heuristic such as a time-window join; consumers should down-weight accordingly. Absent means unknown. Required, and required to be `measured`, when the producer is the extractor (rule E171); from v0.3 a document whose producer is loopmath may give `allocated` when cost.ext[`dev.loopmath.logmatch`] marks a heuristic log match or a shared session.

  Recommended values (open set): `measured`, `allocated`.

- `ext` ([Ext](#ext))

- `tariff` ([Tariff](#tariff))

### `Tariff`

The price table used to compute usd.

- `id` (string): Price table version: the prices.toml sha256 prefix.

  At most 200 characters.

- `date` (string): The tariff date used, YYYY-MM-DD.

  At most 40 characters.

- `source` (string): Where the prices came from.

  At most 500 characters.

- `ext` ([Ext](#ext))

## Artifacts

### `Artifact`

- `id` (string, required): Unique within the document. Producers may use the path itself or a hash of it.

  At most 200 characters.

- `path` (string, required): The path the writers and readers used, as the producer knew it (absolute or workspace-relative). A locator, never content.

  At most 1000 characters.

- `kind` (object, required): *properties*

  - `value` (string | null, required): Open set. Recommended vocabulary: plan, review, spec, report, code, test, config, doc, data, log, commit, merge, issue, repo, diff, verdict, test_record, other (commit and merge from v0.3: a late event is matched to its run by its commit; issue, repo, diff, verdict and test_record from v0.3: the artifact kinds of workflow pieces). null when the kind is unknown; the key is always present and the tier stays required beside it.

    At most 100 characters.

  - `tier` ([EvidenceTier](#evidencetier), required)

  - `evidence` ([ShortText](#shorttext)): What the kind was inferred from, e.g. `path pattern` or `heredoc first line`.

  - `ext` ([Ext](#ext))

- `producer` (string, required): Attempt id of the first writer. Must name an existing attempt and must equal writers[0]; anything else is a conformance error.

  At most 200 characters.

- `writers` (string[], required): Attempt ids that wrote the artifact, in first-write order, each listed once. Never empty: an artifact exists because something wrote it, and writers[0] is the producer.

- `consumers` (string[], required): Attempt ids that read the artifact after a write by another attempt, in first-read order, each listed once. May be empty (written, never read by another attempt).

- `first_write_at` ([Timestamp](#timestamp) | null, required): Timestamp of the first write event; null when the producer cannot compute it. The key is always present: unknown is an explicit null, never an absent key.

- `n_writes` (integer | null, required): Number of write events across all writers; null when the producer cannot count them. A known count must be at least the number of writers; a smaller count undercounts and is a conformance error. A null count is unknown: it neither satisfies nor fails that check.

- `n_reads` (integer | null, required): Number of read events across all consumers; null when the producer cannot count them. A known count must be at least the number of consumers; a smaller count undercounts and is a conformance error. A null count is unknown: it neither satisfies nor fails that check.

- `labels` ([Labels](#labels))

- `ext` ([Ext](#ext))

- `vertex` (string): The workflow artifact id this artifact instantiates.

  At most 200 characters.

- `version` (integer): Version of the artifact, from 1.

- `supersedes` (string): The artifact id this version replaces.

  At most 200 characters.

- `transfer_tokens` (map of object): Attempt id to the tokens that attempt spent reading this artifact, when known.

  *properties*

  - `input` (integer)

  - `cache_read` (integer)

## Events

### `Event`

- `at` ([Timestamp](#timestamp), required)

- `type` (string, required): Open set. Recommended vocabulary: attempt_started, attempt_settled, promoted, directive, note, launched, artifact_written, artifact_read, signal_observed, receipt_written, run_finished.

  At most 100 characters.

- `node` (string): At most 200 characters.

- `attempt` (string): At most 200 characters.

- `actor` (string): At most 200 characters.

- `detail` ([ShortText](#shorttext))

- `ext` ([Ext](#ext))

## Task

### `Task`

The task this run attempted, with its features and its groups. Both members of a pair carry the same task.id and base_commit (E194).

- `id` ([Id](#id), required): Stable task id. Both members of a pair carry the same task.id.

- `title` ([ShortText](#shorttext)): Short title. `loopmath share` drops it under metadata_only export.

- `type` (string): Top-level task type.

  Recommended values (open set): `bug_fix`, `feature`, `refactor`, `tests`, `docs`, `research`, `infra`, `data`.

  At most 100 characters.

- `subtype` (string): User subcategory; a slash-separated path is allowed, e.g. `payments/webhooks`.

  At most 500 characters.

- `repo` (string): Repository name or opaque id. `loopmath share` replaces it with a salted hash.

  At most 500 characters.

- `org` (string): Organization id. Defaults to the store's `org` config.

  At most 200 characters.

- `groups` ([TaskGroup](#taskgroup)[]): The task chain, root first: org, type, repo, subtype, extra user levels. Derived from the fields above when absent.

- `features` (map of boolean | number | string): Task features, key to boolean, number or string. Recommended keys: size (xs, s, m, l, xl), lang (primary language, lowercase), has_tests, spec_clarity (clear, partial, vague), needs_design, touches (1, 2-3, 4+). Filled by the orchestrator.

- `base_commit` (string): Commit the run started from. Pair members must match (E194).

  At most 200 characters.

- `source` (object): *properties*

  - `kind` (string, required): Where the task came from. live: a user's task as it happens. backlog: a planned task list. benchmark: a benchmark item. history: a logged past session.

    Recommended values (open set): `live`, `backlog`, `benchmark`, `history`.

    At most 100 characters.

  - `ref` (string): Opaque reference within that source.

    At most 500 characters.

  - `ext` ([Ext](#ext))

- `labeled_by` (object): *properties*

  - `how` (string, required): Who assigned type, subtype and features.

    Recommended values (open set): `orchestrator`, `user`, `labeler`, `inferred`.

    At most 100 characters.

  - `model` (string): The labelling model's id when `how` is labeler.

    At most 200 characters.

  - `tier` ([OutcomeTier](#outcometier))

  - `ext` ([Ext](#ext))

- `ext` ([Ext](#ext))

### `TaskGroup`

- `level` (string, required): Grouping level, root first: org, type, repo, subtype, then extra user levels.

  Recommended values (open set): `org`, `type`, `repo`, `subtype`.

  At most 100 characters.

- `id` (string, required): The group's key at that level.

  At most 500 characters.

### `SourceRef`

Where a record came from.

- `kind` (string, required): At most 100 characters.

- `ref` (string): Opaque reference within that source (an issue id, a CI run id, a file).

  At most 500 characters.

- `ext` ([Ext](#ext))

## Configuration

### `Configuration`

What ran: a workflow and a setting per piece vertex.

- `id` (string): `cfg_` plus the first 12 hex digits of SHA-256 over the canonical JSON of &#123;workflow, settings&#125; (see Configuration id): keys sorted, no whitespace, workflow id, version, title and ext excluded. Same shape and settings, same id, everywhere (E190).

- `workflow` ([Workflow](#workflow) | [WorkflowRef](#workflowref)): Inline, or a catalog reference such as &#123;"ref": "plan_implement_review", "version": 1&#125;.

- `settings` (map of [Setting](#setting)): Piece vertex id to setting. Every piece vertex has one (E191).

- `source` (enum): Why this configuration ran. usual: the user's usual workflow. alternative: an alternative loopmath offered. exploration: an exploration pick. user_edit: the user edited a suggestion. habit: a logged run, chosen by habit. designed: chosen by a planner such as `loopmath plan`.

  One of: `usual`, `alternative`, `exploration`, `user_edit`, `habit`, `designed`.

- `rec` (string): The recommendation id (rec_...) this choice came from.

  At most 200 characters.

- `ext` ([Ext](#ext))

### `Workflow`

A workflow shape: piece and artifact vertices, typed edges, and the control policy.

- `id` (string): Catalog or user workflow id. Excluded from the configuration id hash.

  At most 200 characters.

- `version` (integer): Excluded from the configuration id hash.

- `title` ([ShortText](#shorttext)): Excluded from the configuration id hash.

- `pieces` ([Piece](#piece)[], required)

- `artifacts` ([WorkflowArtifact](#workflowartifact)[])

- `edges` (array[]): Pairs [from, to]. Piece to artifact means produces; artifact to piece means consumes. Every edge joins a piece and an artifact and the graph is acyclic (E192); the repair loop lives in control.repair, not in an edge. Artifacts with no producer are inputs; with no consumer, outputs.

- `control` ([Control](#control))

- `ext` ([Ext](#ext))

### `WorkflowRef`

A workflow by reference instead of inline.

- `ref` (string, required): A catalog or user workflow id, e.g. `plan_implement_review`.

  At most 200 characters.

- `version` (integer)

- `ext` ([Ext](#ext))

### `Piece`

A piece vertex: one unit of agent (or command) work in the workflow.

- `id` ([Id](#id), required): Vertex id, unique across the workflow's pieces and artifacts.

- `role` (string): What the piece does. loopmath writes planner, implementer, reviewer, tester, referee, worker; readers never rewrite roles.

  Recommended values (open set): `planner`, `implementer`, `reviewer`, `tester`, `referee`, `worker`, `plan`, `implement`, `review`, `test`, `integrate`, `select`, `research`, `docs`.

  At most 100 characters.

- `width` (integer): Parallel copies of the piece (best of n). 1 when absent.

- `workflow` ([Workflow](#workflow) | [WorkflowRef](#workflowref)): A nested workflow. A piece may have a role, a nested workflow, both or neither.

- `ext` ([Ext](#ext))

### `WorkflowArtifact`

An artifact vertex: what pieces consume and produce.

- `id` ([Id](#id), required): Vertex id, unique across the workflow's pieces and artifacts.

- `kind` (string): What the artifact is.

  Recommended values (open set): `issue`, `repo`, `plan`, `spec`, `diff`, `review`, `verdict`, `test_record`, `report`, `other`.

  At most 100 characters.

- `ext` ([Ext](#ext))

### `Control`

The control policy: gates, the repair map, the repair budget and the rescue.

- `gates` (string[]): Piece ids whose output is a verdict (E193: each names a piece).

- `repair` (map of string): Gate id to the piece that reruns when the gate rejects (E193: keys are gates, values are pieces).

- `budget` (integer): Rounds allowed, including the first. 1 means no repair; a missing or 0 budget reads as 1.

- `rescue` ([Rescue](#rescue))

- `ext` ([Ext](#ext)): Extension data. The key `dev.loopmath.gate_rules` (piece id to a gate rule other than the role's default) is part of the configuration id.

### `Rescue`

- `kind` (string, required): The fallback when the workflow ends without acceptance. configuration: redo with the configuration in `ref` (usually the user's usual one). person: a person does it. none: no fallback.

  Recommended values (open set): `configuration`, `person`, `none`.

  At most 100 characters.

- `ref` (string): At most 200 characters.

- `cost_usd` (number)

- `ext` ([Ext](#ext))

### `Setting`

How one piece runs: harness, model, reasoning effort, context policy and harness options.

- `harness` (string): The harness that runs the piece. `command` marks a mechanical gate whose command is in options.command.

  Recommended values (open set): `claude-code`, `codex`, `command`.

  At most 100 characters.

- `model` ([ModelRef](#modelref))

- `effort` (string): Reasoning-effort label as configured (low, medium, high, xhigh, max, default); harness-specific values kept verbatim.

  At most 100 characters.

- `context_policy` (string): Open string; `fresh` when absent.

  Recommended values (open set): `fresh`, `inherit`, `summary`, `default`.

  At most 100 characters.

- `options` (object): Harness options, e.g. &#123;"command": "pytest -q"&#125; for a command gate.

- `ext` ([Ext](#ext))

## Signals and acceptance

### `Signal`

One timestamped observation about the run's result: a verdict, a score or a late event. Value types follow the kind (E195).

- `id` ([Id](#id), required)

- `kind` (enum, required): One of: `verdict`, `score`, `event`.

- `name` (string, required): Recommended: verdicts tests, referee, pair_referee, merge, ci; scores bench_score, runtime_s, quality; events issue, incident, revert, hotfix.

  At most 200 characters.

- `value` (string | number | null, required): verdict: one of accept, reject, pass, fail, error. score: a number, or null for a declared score not measured yet. event: a reference string. Only a score may be null (E195).

  At most 500 characters.

- `unit` (string): Unit of a score.

  At most 100 characters.

- `better` (enum): Which direction of a score is better.

  One of: `higher`, `lower`.

- `target` (number): Optional threshold that turns a score into pass or fail.

- `scale` (enum): How the model treats a score. linear (default); log for positive ratio-like quantities (runtime, memory, latency); fraction for values in [0, 1] (pass rates; E195).

  One of: `linear`, `log`, `fraction`.

- `at_attempt` (string): The attempt after which the verdict applies; it locates the attempt at which the run was accepted.

  At most 200 characters.

- `observed_at` ([Timestamp](#timestamp), required): When the signal was observed. Late events are appended with their own time.

- `source` (object): *properties*

  - `kind` (string, required): Who observed it.

    Recommended values (open set): `orchestrator`, `user`, `ci`, `tracker`, `judge`, `inferred`.

    At most 100 characters.

  - `ref` (string): At most 500 characters.

  - `ext` ([Ext](#ext))

- `tier` ([OutcomeTier](#outcometier), required)

- `ext` ([Ext](#ext))

### `AcceptanceRule`

The user's acceptance rule in effect when the run was recorded. The outcome function reads success through it. A rule with only `score` expresses runtime targets, benchmark scores and quality bars; a binary rule is the special case with score null.

- `name` (string, required): For example `tests+referee` or `perf>=2400`.

  At most 200 characters.

- `definition` ([ShortText](#shorttext), required): For people: `tests pass and referee accepts`.

- `requires` (string[]): Verdict names that must pass.

- `score` ([ScoreTarget](#scoretarget) | null): A score target. With `requires` empty, success is `score reaches target`; with both, success needs both.

- `excludes_events` (string[]): Events that turn success into failure inside the window. ["revert", "incident"] when absent.

- `window_days` (integer): 14 when absent.

- `ext` ([Ext](#ext))

### `ScoreTarget`

- `name` (string, required): A score signal name (E195: never a verdict or event name).

  At most 200 characters.

- `target` (number, required)

- `better` (enum, required): One of: `higher`, `lower`.

- `scale` (enum): One of: `linear`, `log`, `fraction`.

- `ext` ([Ext](#ext))

## Slates and preferences

### `Slate`

Runs started together on one task to be compared (a pair is a slate of two). Every member's file carries the same object (E194).

- `id` ([Id](#id), required)

- `members` (string[], required): Run ids.

- `base_commit` (string): At most 200 characters.

- `isolated` (boolean): Members ran in separate worktrees or sandboxes.

- `blinded` (boolean): Judges did not know which member ran which configuration.

- `ext` ([Ext](#ext))

### `Preference`

A pairwise judgement between slate members. Copied into every member's file; readers deduplicate by id.

- `id` ([Id](#id), required)

- `slate` (string, required): At most 200 characters.

- `winner` (string, required): A member run id, or `tie` (E194).

  At most 200 characters.

- `members` (string[])

- `judge` (object): *properties*

  - `kind` (string): Recommended values (open set): `referee`, `user`, `model`.

    At most 100 characters.

  - `model` (string): At most 200 characters.

  - `blinded` (boolean)

  - `ext` ([Ext](#ext))

- `observed_at` ([Timestamp](#timestamp))

- `tier` ([OutcomeTier](#outcometier))

- `ext` ([Ext](#ext))

## Provenance, rescue and receipt

### `Provenance`

- `kind` (enum, required): logged: the run was observed as it happened, configuration chosen by the user's habit. designed: the configuration was chosen for the run (by loopmath, the user or a planner).

  One of: `logged`, `designed`.

- `chooser` (string): Who chose the configuration.

  Recommended values (open set): `habit`, `loopmath`, `user`, `planner`.

  At most 100 characters.

- `ext` ([Ext](#ext))

### `RunRescue`

Present when the run ended without acceptance and a fallback was used.

- `kind` (string, required): Recommended values (open set): `configuration`, `person`, `none`.

  At most 100 characters.

- `ref` (string): At most 200 characters.

- `cost_usd` (number)

- `basis` (enum): measured: the fallback ran and was priced. expected: an estimate.

  One of: `measured`, `expected`.

- `ext` ([Ext](#ext))

### `Receipt`

Predicted against actual. Written only by loopmath (W196 when another producer writes one).

- `before` (object): *properties*

  - `rec` (string): The recommendation id.

    At most 200 characters.

  - `fit` (string): The fit the prediction came from.

    At most 200 characters.

  - `predicted` (object): *properties*

    - `p_success` ([Interval](#interval))

    - `cost_usd` ([Interval](#interval))

    - `tokens` ([Interval](#interval))

  - `gain_per_run` (any): Per-run gain of an exploration pick: &#123;usd, success_pp, cost_pct, score&#125;, or null.

  - `price_usd` (number | null)

  - `payback_runs` (number | null)

  - `ext` ([Ext](#ext))

- `after` (object): *properties*

  - `cost_usd` (number | null)

  - `tokens` (integer | null)

  - `signals` (array): The signals the result was scored on (ids or copies).

  - `moved` (any): How the fit moved after this run (written by loopmath).

  - `fit_after` (string | null): At most 200 characters.

  - `ext` ([Ext](#ext))

- `ext` ([Ext](#ext))

### `Interval`

A predicted quantity: mean and an 80 percent interval unless `level` says otherwise.

- `mean` (number, required)

- `lo` (number)

- `hi` (number)

- `level` (number): Central coverage of [lo, hi]; 0.8 when absent.
