# Versioning

> How OCP versions work, what stays stable, and how older documents are upgraded.

## The versioning promise

1. The `ocp` field is a scalar version string of the form `0.x`.
2. A reader accepts every published version (0.1, 0.2 and 0.3) and MUST ignore fields it does not know at any level of the document: the top level, and the run, node, attempt, artifact, edge, event and `ext` objects.
3. 0.3 is a strict superset of 0.2: every field 0.3 adds is optional, so a valid 0.2 document stays valid when its `ocp` value is changed to `0.3`, unless it uses a name that 0.3 defines as an unknown field of its own (for example a string in `run.task`, which 0.3 defines as an object). 0.2 is not a superset of 0.1: 0.2 requires `tier` on every edge, so 0.1 documents need `loopmath ocp migrate`, which writes the 0.3 form.
4. A field is never removed without a deprecation notice that names its replacement and stays in the specification for at least two minor versions.
5. Extension namespaces use reverse-domain identifiers. The specification's own namespace begins `io.orchestrationcontextprotocol.`; see [Extensions](https://orchestrationcontextprotocol.io/specification/0.3/extensions).

## Versions and schemas

Each version has its own JSON Schema, and a document is validated against the schema its `ocp` value names.

| Version | Schema | Status |
|---|---|---|
| 0.3 | [`ocp-v0.3.schema.json`](https://github.com/loopmath/loopmath/blob/main/spec/ocp-v0.3.schema.json), id `urn:ocp:run:0.3` | Current |
| 0.2 | [`ocp-v0.2.schema.json`](https://github.com/loopmath/loopmath/blob/main/spec/ocp-v0.2.schema.json), id `urn:ocp:run:0.2` | Read by every 0.3 reader |
| 0.1 | [`ocp-v0.schema.json`](https://github.com/loopmath/loopmath/blob/main/spec/ocp-v0.schema.json), id `urn:ocp:run:0.1` | Read by every 0.3 reader |

A reader MUST accept 0.1, 0.2 and 0.3 documents, and reads each one by the schema and rules of the version it declares. Rules added in a version apply to documents at that version or later: the numbered 0.3 rules do not apply to a 0.2 document.

Accepting an older document does not mean keeping it in its old form. loopmath's run store, for example, keeps only 0.3 documents: `loopmath run import` migrates a 0.1 or 0.2 document before storing it, and a document that reaches the store in an older version any other way is refused with E005, a store rule rather than an OCP rule.

## Migration

`loopmath ocp migrate FILE... [--out DIR]` upgrades 0.1 and 0.2 documents to 0.3. Raising the `ocp` value by hand is not a migration for a 0.1 document, which fails once its edges lack a `tier`. The command never changes or overwrites its input, and running it on its own output returns that output unchanged. It adds a field only when the field is absent:

- `ocp` becomes `0.3`, and `run.ext["dev.loopmath.migration"]` records `{"from": <version>}`.
- A 0.1 edge without a tier gets `reported`.
- `run.task` comes from the run's `task`, `type` and `repo` labels (the id from the `task` label, else the run id), marked `{how: inferred, tier: heuristic}`.
- `node.vertex` comes from its attempts' `role.value` when they agree on one value.
- `run.provenance` becomes `{kind: logged, chooser: habit}`, the migration's default for a run whose choice is not recorded.
- `run.configuration` becomes `{source: habit}`, plus the inferred workflow and settings when loopmath can infer them, with the confidence in `ext["dev.loopmath.inferred"]` at tier `heuristic`.

Extension keys are kept as they are, so older keys keep drawing their warnings until their producer writes the new namespace.

## Namespace changes

| Prefix | Status |
|---|---|
| `dev.loopmath.` | loopmath's namespace from 0.3 |
| `dev.dagr.` | loopmath's namespace before its rename. Read with warning [W182](https://orchestrationcontextprotocol.io/specification/0.3/conformance#w182) through 0.4 (two minor versions) |
| `dagr.` | Retired. Error [E181](https://orchestrationcontextprotocol.io/specification/0.3/conformance#e181), whose message names `dev.loopmath.` |
