# Configuration

> What ran: a workflow of pieces and artifacts, a setting per piece, and an id derived from both.

> **Info:** Added in 0.3. The matching [producer capability](https://orchestrationcontextprotocol.io/specification/0.3/document#producer) is `configuration`.

`run.configuration` records what ran, separately from how it went. It has two parts: a **workflow**, the shape of the work (pieces of agent work, the artifacts they pass, and the control policy that gates and repairs), and a **setting** for each piece (harness, model, reasoning effort, context policy and options). Its `id` is computed from both, so the same workflow with the same settings has the same id in every tool, and runs of one configuration can be counted together. The id covers the declared configuration only: an attempt may override its piece's setting, so two runs with the same id can still differ in what individual attempts used.

```json
"configuration": {
  "id": "cfg_ab15440a2d9c",
  "workflow": {
    "id": "implement_review", "version": 1, "title": "Implement, then review",
    "pieces": [
      { "id": "implement", "role": "implementer" },
      { "id": "review", "role": "reviewer" }
    ],
    "artifacts": [
      { "id": "issue", "kind": "issue" }, { "id": "repo", "kind": "repo" },
      { "id": "diff", "kind": "diff" }, { "id": "verdict", "kind": "verdict" }
    ],
    "edges": [
      ["issue", "implement"], ["repo", "implement"], ["implement", "diff"],
      ["diff", "review"], ["issue", "review"], ["review", "verdict"]
    ],
    "control": {
      "gates": ["review"],
      "repair": { "review": "implement" },
      "budget": 3,
      "rescue": { "kind": "configuration", "ref": "usual" }
    }
  },
  "settings": {
    "implement": { "harness": "claude-code", "model": { "raw": "claude-opus-5-5", "id": "claude-opus-5-5" }, "effort": "high", "context_policy": "fresh", "options": {} },
    "review": { "harness": "codex", "model": { "raw": "gpt-6-astra", "id": "gpt-6-astra" }, "effort": "xhigh", "context_policy": "fresh", "options": {} }
  },
  "source": "exploration"
}
```

The workflow above, drawn. The dashed arrow is the repair: when the review rejects, `implement` runs again, up to three rounds in all.

```mermaid
flowchart LR
  issue[/issue/] --> implement
  repo[/repo/] --> implement
  implement --> diff[/diff/]
  diff --> review
  issue --> review
  review --> verdict[/verdict/]
  review -. repair .-> implement
```

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](https://orchestrationcontextprotocol.io/specification/0.3/schema#workflow) | [WorkflowRef](https://orchestrationcontextprotocol.io/specification/0.3/schema#workflowref)): Inline, or a catalog reference such as &#123;"ref": "plan_implement_review", "version": 1&#125;.

- `settings` (map of [Setting](https://orchestrationcontextprotocol.io/specification/0.3/schema#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](https://orchestrationcontextprotocol.io/specification/0.3/schema#ext))

## Workflow

A workflow is a graph with two kinds of vertex. A **piece** is one unit of agent (or command) work; an **artifact** is what pieces consume and produce. Edges only join a piece and an artifact.

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](https://orchestrationcontextprotocol.io/specification/0.3/schema#shorttext)): Excluded from the configuration id hash.

- `pieces` ([Piece](https://orchestrationcontextprotocol.io/specification/0.3/schema#piece)[], required)

- `artifacts` ([WorkflowArtifact](https://orchestrationcontextprotocol.io/specification/0.3/schema#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](https://orchestrationcontextprotocol.io/specification/0.3/schema#control))

- `ext` ([Ext](https://orchestrationcontextprotocol.io/specification/0.3/schema#ext))

- An edge `[piece, artifact]` means the piece produces the artifact; `[artifact, piece]` means the piece consumes it. An artifact with no producer is an input, one with no consumer an output.
- Piece and artifact ids share one namespace and MUST be unique in it, every edge MUST join a piece and an artifact that exist, and the edges MUST NOT form a cycle ([E192](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e192)). A repair loop is written in `control.repair`, never as an edge.
- A nested workflow is checked the same way.

A workflow MAY be given by reference instead of inline. A reader resolves the reference through its catalog; a reader that cannot resolve it skips [E190](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e190) and [E191](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e191).

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](https://orchestrationcontextprotocol.io/specification/0.3/schema#ext))

### Pieces

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

- `id` ([Id](https://orchestrationcontextprotocol.io/specification/0.3/schema#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](https://orchestrationcontextprotocol.io/specification/0.3/schema#workflow) | [WorkflowRef](https://orchestrationcontextprotocol.io/specification/0.3/schema#workflowref)): A nested workflow. A piece may have a role, a nested workflow, both or neither.

- `ext` ([Ext](https://orchestrationcontextprotocol.io/specification/0.3/schema#ext))

- `role` is an open string. loopmath writes `planner`, `implementer`, `reviewer`, `tester`, `referee` and `worker`. A reader MUST NOT rewrite a role into its own vocabulary; it compares roles as written.
- `width` above 1 runs that many copies of the piece in parallel, as in best of n.
- A piece may name a `role`, hold a nested `workflow`, do both, or do neither: the schema requires only `id`, and 0.3 allows all four. A piece that holds a nested workflow and has no role needs no setting ([E191](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e191)); every other top-level piece does.

### Artifacts

An artifact vertex: what pieces consume and produce.

- `id` ([Id](https://orchestrationcontextprotocol.io/specification/0.3/schema#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](https://orchestrationcontextprotocol.io/specification/0.3/schema#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](https://orchestrationcontextprotocol.io/specification/0.3/schema#rescue))

- `ext` ([Ext](https://orchestrationcontextprotocol.io/specification/0.3/schema#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.

- `budget` counts rounds including the first. A budget of 1 means no repair, and a missing or 0 budget reads as 1.
- Every `control.gates` entry MUST name a piece, every `control.repair` key MUST be a gate, and every `control.repair` value MUST be a piece ([E193](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e193)).
- The extension key `dev.loopmath.gate_rules` maps a gate to a rule other than its role's default. Because it changes what ran, it is part of the [configuration id](#configuration-id), the one `ext` key that is.

- `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](https://orchestrationcontextprotocol.io/specification/0.3/schema#ext))

## Settings

`settings` maps each piece id to the setting it ran with. Every top-level piece of the workflow MUST have an object in `settings` under its id, except a piece that holds a nested workflow and has no role ([E191](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e191)).

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](https://orchestrationcontextprotocol.io/specification/0.3/schema#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](https://orchestrationcontextprotocol.io/specification/0.3/schema#ext))

- A setting without `context_policy` reads as `fresh`.
- `harness: "command"` marks a mechanical gate, such as a test run, whose command is in `options.command`.
- An [attempt](https://orchestrationcontextprotocol.io/specification/0.3/attempts) carries its own `setting` only when it differs from the configuration's setting for its piece. Such overrides are not part of the configuration id. `node.vertex` and `attempt.vertex` name the piece a node or attempt executes, and `attempt.round` the repair round, from 1.

## Configuration id

`configuration.id` is `cfg_` followed by the first 12 hexadecimal digits of the SHA-256 digest of the canonical JSON of `{"workflow": W, "settings": S}`. The canonical form keeps the declared workflow and baseline settings and drops labels, so renaming a workflow or giving it a new version does not change the id, and changing a model or a budget does. Per-attempt setting overrides are outside the id. A document whose id does not match its workflow and settings fails [E190](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e190).

**1. Resolve the workflow**

If the workflow is a reference `{ref, version}` without `pieces`, replace it with the catalog workflow it names.

**2. Canonicalize the workflow (W)**

Keep exactly four keys, and drop the workflow's `id`, `version`, `title`, `ext` and any other field:

- `pieces`: each piece object as `{id, width}`, with `width` 1 when absent, plus `role` when present and `workflow` when present (a nested workflow is canonicalized the same way). Other piece fields are dropped. Sorted by `id`; `[]` when absent.
- `artifacts`: each artifact object as `{id}`, plus `kind` when present. Sorted by `id`; `[]` when absent.
- `edges`: the `[from, to]` pairs, sorted by the string forms of their two ends; `[]` when absent.
- `control`: `gates` sorted, `[]` when absent; `repair` as given, `{}` when absent; `budget` as max(1, budget) when it is an integer and 1 otherwise, so a missing budget and a number with a fraction part such as `2.0` both read as 1; `rescue` reduced to its `kind` and `ref`, only when present; and `gate_rules`, the value of `control.ext["dev.loopmath.gate_rules"]`, only when present and not empty. Other `ext` keys are dropped. An absent `control` reads as `{"budget": 1, "gates": [], "repair": {}}`.

**3. Canonicalize the settings (S)**

Map every key of `settings`, not only the piece ids, to `{harness, model, effort, context_policy, options}`: `harness` as given, else `null`; `model` is the modelRef's `id`, else its `raw`, else a bare string as given, else `null`; `effort` is `"default"` when absent, `context_policy` `"fresh"` and `options` `{}`. The setting's `ext` is dropped.

**4. Serialize**

Write `{"workflow": W, "settings": S}` as JSON with object keys sorted at every level, no whitespace (separators `,` and `:`), non-ASCII characters written as themselves, and encode it as UTF-8.

**5. Hash**

Take the SHA-256 digest of those bytes, in lowercase hexadecimal. The id is `cfg_` plus its first 12 digits.

### Worked example

A one-piece workflow, `solo`: one agent reads the issue and the repository and writes a diff.

```json Configuration
{
  "workflow": {
    "id": "solo", "version": 1, "title": "One agent implements",
    "pieces": [{ "id": "implement", "role": "implementer", "width": 1 }],
    "artifacts": [{ "id": "issue", "kind": "issue" }, { "id": "repo", "kind": "repo" }, { "id": "diff", "kind": "diff" }],
    "edges": [["issue", "implement"], ["repo", "implement"], ["implement", "diff"]],
    "control": { "gates": [], "repair": {}, "budget": 1, "rescue": { "kind": "configuration", "ref": "usual" } }
  },
  "settings": {
    "implement": { "harness": "claude-code", "model": { "raw": "claude-opus-5-5", "id": "claude-opus-5-5" }, "effort": "high", "context_policy": "fresh", "options": {} }
  }
}
```

```json Canonical JSON (one line)
{"settings":{"implement":{"context_policy":"fresh","effort":"high","harness":"claude-code","model":"claude-opus-5-5","options":{}}},"workflow":{"artifacts":[{"id":"diff","kind":"diff"},{"id":"issue","kind":"issue"},{"id":"repo","kind":"repo"}],"control":{"budget":1,"gates":[],"repair":{},"rescue":{"kind":"configuration","ref":"usual"}},"edges":[["implement","diff"],["issue","implement"],["repo","implement"]],"pieces":[{"id":"implement","role":"implementer","width":1}]}}
```

```text Id
cfg_261aaa36000b
```

### Try it

Paste a configuration, or a whole OCP document, to compute its id.

*On the web page, a calculator computes the id of a configuration or document you paste. It runs in the browser.*

loopmath's `loopmath.ocp.canonical.config_id` is the reference implementation, and `loopmath ocp validate` checks it as [E190](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e190). Numbers are written as the reference implementation's JSON encoder writes them, so `2.0` in `options` stays `2.0`. The calculator above was tested against the reference implementation on randomly generated configurations and on every example document. A producer that computes ids in another language should check its output the same way, for example on the documents in [`examples/v0.3/`](https://github.com/loopmath/loopmath/tree/main/spec/examples/v0.3), whose ids all verify.

## Why it ran

`source` says why this configuration ran: the user's `usual` workflow, an `alternative` a recommender offered, an `exploration` pick, a `user_edit` of a suggestion, `habit` for a logged run, or `designed` by a planner. `rec` names the recommendation the choice came from. [Provenance](https://orchestrationcontextprotocol.io/specification/0.3/receipt#provenance) says whether the run was logged or designed.

## Rules

| Rule | Checks |
|---|---|
| [E190](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e190) | `configuration.id` equals the canonical id of its workflow and settings. |
| [E191](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e191) | Every top-level piece has a setting, except one that holds a nested workflow and has no role. |
| [E192](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e192) | Unique vertex ids, edges that join an existing piece and artifact, no cycles; nested workflows too. |
| [E193](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e193) | Gates are pieces; the repair map runs from a gate to a piece. |
