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.
Optional named visual/organizational scopes, such as projects. Grouping is orthogonal to dependency structure.
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.
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.
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.
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.
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.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
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
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 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
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).
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}.
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.
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.
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.
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).
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.
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.string[]
Verdict names that must pass.
ScoreTarget | null
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).
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.
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.