The schema is JSON Schema draft 2020-12. Download it at ocp-v0.3.schema.json. The schema and the meaning its description strings give each field are normative. With the numbered rules JSON Schema cannot express, they define OCP.

Top-level fields

The fields of the document object itself.
"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.
required
required
required
Optional named visual/organizational scopes, such as projects. Grouping is orthogonal to dependency structure.
required
The units of work. A gate is a node whose kind is gate.
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.
Flat list of tries, each pointing at its node. Attempts are records, not counters; a retry is a new attempt.
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.
Optional append-only provenance log, ascending in time.

Document

Producer

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.
string
At most 100 characters.
string
The orchestration framework the run executed under, e.g. my-orchestrator, claude-code, langgraph, crewai.At most 200 characters.
string
At most 100 characters.
string
Upstream native format this document was derived from, e.g. my-orchestrator/1.2.At most 200 characters.
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.

Privacy

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.

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

string
required
Producer-scoped stable run id.At most 200 characters.
string
Opaque project/workspace locator for task-mix stratification (a name or hash, not necessarily a path).At most 500 characters.
Verdicts, scores and late events, each timestamped.

Group

string
required
At most 200 characters.
string
Another group id; parent edges must be acyclic.At most 200 characters.

Node

string
required
Stable identity independent of any attempt. Never a runtime locator such as a pane id.At most 200 characters.
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.
string
Optional group id; omitted means the run root.At most 200 characters.
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.
string
The workflow piece id this node executes.At most 200 characters.
object
Present when the node is a gate.

Edge

string
required
Source node id (the prerequisite / input / spawner / launcher / producer’s node).At most 200 characters.
string
required
Target node id (the dependent / gate / spawned node / launched node / consumer’s node).At most 200 characters.
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.
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.
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.
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.
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.
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.

Attempts

Attempt

string
required
Globally unique within the document.At most 200 characters.
string
required
The node this attempt tries.At most 200 characters.
integer
1-based ordinal within the node.
string
Logical agent/role that ran the attempt (not a volatile runtime locator).At most 200 characters.
string
The agent harness that ran the attempt, e.g. claude-code, codex.At most 100 characters.
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.
How this attempt came to run: which scanned attempt launched it through a CLI, in which workspace, or that an unscanned program did.
What part the attempt played in the run (lead, planner, dev, reviewer, …), with the tier and evidence the label rests on.
When in the run’s life the attempt ran (build, post, external), with tier and evidence.
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.
string
Opaque correlate to the underlying harness session/transcript id, for producers that can join the two. Never transcript content.At most 500 characters.
string
The workflow piece id this attempt executes.At most 200 characters.
integer
Repair round, 1-based.
Only when it differs from the configuration’s setting for the vertex.
string
Working folder, opaque.At most 1000 characters.

ModelRef

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.
string
Canonical provider model id when known, e.g. claude-fable-5.At most 200 characters.
string
Coarse family label for grouping attempts by model family, e.g. fable, opus5, sol5.6.At most 100 characters.
string
e.g. anthropic, openai.At most 100 characters.
How the model label is known: verified from per-request usage records, reported from a harness field, heuristic from a name pattern.

Cause

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.
string
The earlier attempt id this cause points back to.At most 200 characters.
string
Who triggered it (reviewer, operator, policy rule).At most 200 characters.

Outcome

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.
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.
string
Node id of the gate or review that produced this acceptance signal, when one did.At most 200 characters.

Origin

How the attempt came to exist. Every field is present; an unknown value is an explicit null, never an absent key.
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.
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.
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.
ShortText | null
required
The launch mechanism, e.g. codex exec, claude -p, via_script: tools/review.sh; null when unknown.
required

Role

What the attempt did in the run. All three fields are present; evidence is null when there is nothing to cite.
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.
required
ShortText | null
required
What the label rests on, e.g. “declared type Plan” or launch command mentions review; null when nothing can be cited.

Phase

When in the run the attempt happened. All three fields are present; evidence is null when there is nothing to cite.
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.
required
ShortText | null
required
What the label rests on; null when nothing can be cited.

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.
integer
Uncached input tokens.
integer
Input tokens served from cache (cache reads).
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.
integer
Cache-creation tokens in the 5-minute retention bucket.
integer
Cache-creation tokens in the 1-hour retention bucket, priced higher than the 5-minute bucket.
integer
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.
integer
Number of model API requests aggregated into this record.
number
Billed cost in US dollars when the producer knows it. Consumers should prefer recomputing from tokens and a dated price table.
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.

Tariff

The price table used to compute usd.
string
Price table version: the prices.toml sha256 prefix.At most 200 characters.
string
The tariff date used, YYYY-MM-DD.At most 40 characters.
string
Where the prices came from.At most 500 characters.

Artifacts

Artifact

string
required
Unique within the document. Producers may use the path itself or a hash of it.At most 200 characters.
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.
object
required
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.
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.
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).
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.
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.
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.
string
The workflow artifact id this artifact instantiates.At most 200 characters.
integer
Version of the artifact, from 1.
string
The artifact id this version replaces.At most 200 characters.
map of object
Attempt id to the tokens that attempt spent reading this artifact, when known.

Events

Event

required
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.
string
At most 200 characters.
string
At most 200 characters.
string
At most 200 characters.

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).
required
Stable task id. Both members of a pair carry the same task.id.
Short title. loopmath share drops it under metadata_only export.
string
Top-level task type.Recommended values (open set): bug_fix, feature, refactor, tests, docs, research, infra, data.At most 100 characters.
string
User subcategory; a slash-separated path is allowed, e.g. payments/webhooks.At most 500 characters.
string
Repository name or opaque id. loopmath share replaces it with a salted hash.At most 500 characters.
string
Organization id. Defaults to the store’s org config.At most 200 characters.
The task chain, root first: org, type, repo, subtype, extra user levels. Derived from the fields above when absent.
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.
string
Commit the run started from. Pair members must match (E194).At most 200 characters.
object
object

TaskGroup

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.
string
required
The group’s key at that level.At most 500 characters.

SourceRef

Where a record came from.
string
required
At most 100 characters.
string
Opaque reference within that source (an issue id, a CI run id, a file).At most 500 characters.

Configuration

Configuration

What ran: a workflow and a setting per piece vertex.
string
cfg_ plus the first 12 hex digits of SHA-256 over the canonical JSON of {workflow, settings} (see Configuration id): keys sorted, no whitespace, workflow id, version, title and ext excluded. Same shape and settings, same id, everywhere (E190).
Inline, or a catalog reference such as {“ref”: “plan_implement_review”, “version”: 1}.
map of Setting
Piece vertex id to setting. Every piece vertex has one (E191).
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.
string
The recommendation id (rec_…) this choice came from.At most 200 characters.

Workflow

A workflow shape: piece and artifact vertices, typed edges, and the control policy.
string
Catalog or user workflow id. Excluded from the configuration id hash.At most 200 characters.
integer
Excluded from the configuration id hash.
Excluded from the configuration id hash.
required
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.

WorkflowRef

A workflow by reference instead of inline.
string
required
A catalog or user workflow id, e.g. plan_implement_review.At most 200 characters.
integer

Piece

A piece vertex: one unit of agent (or command) work in the workflow.
required
Vertex id, unique across the workflow’s pieces and artifacts.
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.
integer
Parallel copies of the piece (best of n). 1 when absent.
A nested workflow. A piece may have a role, a nested workflow, both or neither.

WorkflowArtifact

An artifact vertex: what pieces consume and produce.
required
Vertex id, unique across the workflow’s pieces and artifacts.
string
What the artifact is.Recommended values (open set): issue, repo, plan, spec, diff, review, verdict, test_record, report, other.At most 100 characters.

Control

The control policy: gates, the repair map, the repair budget and the rescue.
string[]
Piece ids whose output is a verdict (E193: each names a piece).
map of string
Gate id to the piece that reruns when the gate rejects (E193: keys are gates, values are pieces).
integer
Rounds allowed, including the first. 1 means no repair; a missing or 0 budget reads as 1.
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

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.
string
At most 200 characters.
number

Setting

How one piece runs: harness, model, reasoning effort, context policy and harness options.
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.
string
Reasoning-effort label as configured (low, medium, high, xhigh, max, default); harness-specific values kept verbatim.At most 100 characters.
string
Open string; fresh when absent.Recommended values (open set): fresh, inherit, summary, default.At most 100 characters.
object
Harness options, e.g. {“command”: “pytest -q”} for a command gate.

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).
required
enum
required
One of: verdict, score, event.
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.
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.
string
Unit of a score.At most 100 characters.
enum
Which direction of a score is better.One of: higher, lower.
number
Optional threshold that turns a score into pass or fail.
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.
string
The attempt after which the verdict applies; it locates the attempt at which the run was accepted.At most 200 characters.
required
When the signal was observed. Late events are appended with their own time.
object
required

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.
string
required
For example tests+referee or perf>=2400.At most 200 characters.
required
For people: tests pass and referee accepts.
string[]
Verdict names that must pass.
A score target. With requires empty, success is score reaches target; with both, success needs both.
string[]
Events that turn success into failure inside the window. [“revert”, “incident”] when absent.
integer
14 when absent.

ScoreTarget

string
required
A score signal name (E195: never a verdict or event name).At most 200 characters.
number
required
enum
required
One of: higher, lower.
enum
One of: linear, log, fraction.

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).
required
string[]
required
Run ids.
string
At most 200 characters.
boolean
Members ran in separate worktrees or sandboxes.
boolean
Judges did not know which member ran which configuration.

Preference

A pairwise judgement between slate members. Copied into every member’s file; readers deduplicate by id.
required
string
required
At most 200 characters.
string
required
A member run id, or tie (E194).At most 200 characters.
string[]
object

Provenance, rescue and receipt

Provenance

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.
string
Who chose the configuration.Recommended values (open set): habit, loopmath, user, planner.At most 100 characters.

RunRescue

Present when the run ended without acceptance and a fallback was used.
string
required
Recommended values (open set): configuration, person, none.At most 100 characters.
string
At most 200 characters.
number
enum
measured: the fallback ran and was priced. expected: an estimate.One of: measured, expected.

Receipt

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

Interval

A predicted quantity: mean and an 80 percent interval unless level says otherwise.
number
required
number
number
number
Central coverage of [lo, hi]; 0.8 when absent.

Privacy