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.

Versions and schemas

Each version has its own JSON Schema, and a document is validated against the schema its ocp value names. 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

Privacy