OpenWOP openwop.dev

Status: v1 — applies to the locked v1 contract and all subsequent v1.x releases.

This document defines what openwop guarantees about backward compatibility, when those guarantees can be relaxed, and how implementers should pin against the spec.

The TL;DR: v1.x is additive-only, with one explicit exception for safety and security fixes. Everything else that would break a v1 conformance pass goes to v2.

1. Versioning model

The v1 wire contract froze on 2026-05-08. That is the date this document's additive-only guarantee starts running from, and until now it was stated nowhere in this file — a downstream consumer citing the freeze had to source it from a CHANGELOG.md release entry, which is why openwop.dev carried the date for months with no citable anchor and a sibling session could not verify it.

Three dates sit close together and name different events; conflating them is easy and each is load-bearing somewhere:

DateEventRecorded in
2026-05-08The wire contract froze. 26 prose specs at FINAL v1, 17 first-class JSON Schemas, OpenAPI 3.1 + AsyncAPI 3.1. Everything after this is additive per §2, or a safety-fix per §3.CHANGELOG.md [1.0] — 2026-05-08
2026-05-11First publication of v1.0.0 to the registries.CHANGELOG.md [1.0.0]
2026-05-12The v1.0 close-out release — what README.md's status banner calls "v1.0 core locked".CHANGELOG.md [1.1.0] — 2026-05-12

The freeze is the one that governs this document. A reader asking "since when has openwop promised additive-only evolution?" wants 2026-05-08, not the close-out date the README banner shows — the banner is describing when the corpus stopped moving, which happened four days later.

openwop uses three independent version axes:

AxisRangeBump rule
Spec corpus version (e.g. v1, v1.1)Major.MinorMajor = breaking; Minor = additive
Conformance suite version (@openwop/openwop-conformance)Major.Minor.PatchMajor tracks spec major; Minor adds scenarios for the same spec major; Patch yanks/fixes scenarios
SDK versions (@openwop/openwop, openwop-client, Go SDK)Major.Minor.PatchMajor tracks spec major; Minor adds methods or fixes types; Patch is bug fixes

A host advertises:

  • The spec major it implements (via protocolVersion in /.well-known/openwop).
  • The conformance suite version it passes (per ROADMAP.md's suite-expansion record).
  • Optionally, the profile set it advertises (per RFCS/-bound profile definitions; see §3 of spec/v1/profiles.md once that lands).

Clients and SDKs pin to the spec major. Within a spec major, clients are guaranteed forward compatibility per §2.

2. v1.x compatibility guarantees

For any release v1.x (where x ≥ 0):

2.1 Additive only

  • New optional fields MAY appear in request and response bodies, event payloads, and the discovery document.
  • New event types MAY appear in event streams. Clients MUST ignore unknown event types.
  • New SHOULD recommendations MAY be introduced. Hosts that don't follow new SHOULDs remain conformant against the suite version they pass; later suite versions may not pass.
  • New optional capabilities MAY be added to /.well-known/openwop. Hosts that don't advertise them remain v1-compliant.
  • New endpoints MAY be added under /v1/. Existing endpoints MUST continue to work as documented.
  • New conformance scenarios MAY be added in suite minor releases. Hosts that pass 1.0 are not required to pass 1.x.0; they advertise the suite version they pass.

Schema closure (RFC 0094)

How a published JSON Schema treats undeclared properties is a compatibility decision, and the rule differs by direction of travel:

  • Client-submitted shapes are closed. Schemas validating client-submitted request bodies MUST be closed at the outermost compositionadditionalProperties: false on a standalone object schema, or unevaluatedProperties: false at the composition site when the request shape is an allOf composition (JSON Schema 2020-12) — so client typos fail fast instead of being silently dropped. Closure MUST NOT be placed inside individual allOf branches (that composition is unsatisfiable).
  • Server-emitted shapes are open. Schemas describing server-emitted documents (events, snapshots, discovery payloads) MUST NOT be closed, so a v1.x host can add optional fields per §2.1 without breaking schema-validating clients.

RFC 0094 applies this policy to the central server-emitted shape that violated it — schemas/run-event.schema.json is now open, with an anyOf vendor-event branch on type — and to the createRun request composition (closed via unevaluatedProperties: false). The full sweep of the remaining server-emitted schemas is a named follow-up (RFC 0094 §Unresolved questions), not silently included.

Cross-file $ref in a published schema (2026-08-16)

Adding a new cross-file $ref to a published schema (e.g. workflow-definition.schema.json gaining "$ref": "compensation-policy.schema.json") is additive on the wire and a suite-minor change for validators: any consumer that pre-registers a fixed list of peer schemas before compile() will throw at compile time until it registers the new peer, and a downstream runner that resolves the sibling corpus while pinning an older suite will fail for a change it did not make. Rules:

  • A schema author adding a cross-file $ref MUST bump the conformance suite minor and MUST list the new peer in the CHANGELOG entry (as #1009 did for compensation-policy.schema.json).
  • Consumers SHOULD register schemas by enumerating the schema directory rather than by a fixed list; the suite's own validators do (fixtures-valid.test.ts, workflow-primary-output-annotation.test.ts, since 1.112.0). A fixed list is a claim about the schema graph that nothing keeps true.
  • Where a schema must stay self-contained for downstream fixed-list validators (registry-side manifest validation), prefer an inline byte-mirror with a conformance leg asserting equality to the source over a $ref — RFC 0157 did this for the chain manifest.

2.2 Never within v1.x

  • Existing required fields MUST NOT become optional, MUST NOT be removed, MUST NOT change type.
  • Existing optional fields MUST NOT change type.
  • Existing event types MUST NOT change shape.
  • Existing endpoints MUST NOT change request or response contracts (additive optional fields aside).
  • Existing MUST requirements MUST NOT be relaxed.
  • Existing error codes and HTTP status codes MUST NOT change meaning.

2.3 Suite vs. spec compatibility

A new conformance scenario that fails on a host previously passing 1.x.0 does NOT mean the spec broke. It means the suite found a previously-untested gap. The host's 1.x.0 pass is preserved; the host has the option to fix and pass 1.(x+1).0.

The suite is the test instrument; the spec is the contract. Suite changes MAY be more strict than spec text about edge cases, but MUST NOT be more strict about wire shape than the spec defines.

Measurement vs. claim (RFC 0148, 2026-08-16). A recorded 1.x.0 pass is a measurement and is preserved as such. A certification claim is governed by RFC 0148: certification bundle v2 (suite ≥ 1.114.0) records one of five dispositions per requirement with a witnessed assertion count, and bundle v1 — whose passed list counted an early-returning test as a pass and could not tell skipped from inapplicable from blocked — remains parseable but ceases to substantiate a new certification after the 90-day migration window that began 2026-08-12 (RFC 0148 §Compatibility, classified safety-fix under §3 below). Suites before 1.114.0 are still valid instruments for what they measured; they are not evidence of what they could not observe.

3. The safety-fix exception

The §2.2 list above has one explicit exception: safety and security fixes.

A change MAY break v1.x if all of:

  • It is necessary to fix a CVE-class vulnerability or a correctness bug that prevents the protocol from being used safely.
  • The fix cannot be expressed as additive (e.g., a new optional field) without leaving the original surface insecure or incorrect.
  • The fix is published with one of:

- A 90-day public RFC window (per RFCS/0001-rfc-process.md) before merge; OR - An embargoed coordinated-disclosure window per SECURITY.md. The RFC is published when the embargo lifts. Embargo MUST NOT exceed 90 days unless implementers operating production deployments need more time and explicitly request the extension.

Safety-fix breaks ship with:

  • An RFC documenting the change, the threat model, and the migration path.
  • A version-negotiation.md runbook section describing how implementers detect the change and migrate.
  • Migration tooling where mechanically possible (codemods, schema migrators, conformance scenarios that detect the old surface).
  • A CHANGELOG.md entry under a ### Security heading citing the advisory ID per SECURITY.md.

The spec major does not bump for safety-fix changes. The spec minor bumps. The suite minor bumps with new scenarios that detect both the vulnerable shape and the fixed shape.

A safety-fix change is the only category that can break v1.x. Everything else goes to v2.

Editorial correction on record (2026-08-16, S22 — the error-envelope "schism"): the HTTP error envelope is and was FLAT (error string + message + details, error-envelope.schema.json, additionalProperties: false); a nested { error: { code, retriable } } shape that appeared in three rest-endpoints.md code-list entries, four test-seam contracts and ~15 scenarios between 2026-06 and 2026-08 was corrected back to flat with details.retriable. Not a safety-fix and not a wire change — no conforming host emitted the nested shape on a canonical endpoint; test seams that did get a tolerance window. Re-shaping error to an object would have been a §2.2 break, which is why the schema won.

Conformance-affecting correction on record (2026-08-18, RFC 0057 §D — the replay re-emission note). RFC 0057 §D carried a non-normative implementation note that read as permission for a replay fork to suppress recorded-fact events rather than re-emit them, and conceded in its own words that it satisfied only the "MUST NOT regenerate" half of the rule. replay.md §"Determinism guarantees" caveat 5 has required re-emission since v1.2 and did not change; the note is retired. Recorded here because the change is conformance-affecting even though no requirement moved — W3C Process Class 3 ("corrections that MAY affect conformance, but add no new features", including one that "clears up an ambiguity ... in such a way that an agent whose conformance was once unclear becomes clearly conforming or non-conforming"), not Class 2 editorial. A suppressing host is now clearly non-conforming where its status was previously arguable; it was never conforming, but the corpus now says so unambiguously. Not a §2.2 break (no shape, code, or MUST changed) and not a §3 safety-fix (nothing that was conforming stops being so).

Conformance-affecting correction on record (2026-08-25, a2a-push-egress-ssrf — the missing scheme arm). RFC 0100 §4, a2a-integration.md §D.6, and the a2a-push-egress-ssrf invariant all required a caller-registered pushConfig.url to "pass the RFC 0093 webhook-egress SSRF guard", then abbreviated that guard — in every one of its statements — as "no private/loopback/link-local target". The guard it names is the webhooks.md §"SSRF protection" list, whose first entry is "Non-https:// protocols". A reader could take the parenthetical as the definition rather than as shorthand for the referenced list, and a host refusing http://10.0.0.5/push on the address while accepting http://push.example.com/ then read as conforming. W3C Process Class 3, on the same reasoning as the RFC 0057 §D entry above — a correction that "clears up an ambiguity … in such a way that an agent whose conformance was once unclear becomes clearly conforming or non-conforming". No MUST moved, no shape changed, and nothing that was conforming stops being so: a host accepting a plaintext push target was never conforming, because the guard it was told to apply always included that arm. Not a §2.2 break and not a §3 safety-fix. The conformance leg was also two-sided in the same change: the single http://10.0.0.5 probe is refused by either arm alone, so it witnessed neither — an assertion that passes on half an implementation. Reported by a tier-1 host that found the identical structural defect in its own A2A path after fixing it on the webhook path, and declined to fix it on the grounds that the obligation reached A2A only by inference. That judgement was correct and is what made this a corpus defect rather than a host one.

Conformance-affecting correction on record (2026-09-06, the fork-prefix bracket — [0, fromSeq][0, fromSeq)). The replay byte-equivalence clause stated its range with a closed upper bracket in both spec/v1/replay.md §Determinism guarantees and spec/v2/core/replay.md §"Byte-equivalence of the prefix", making the event AT fromSeq part of the range that MUST be byte-equivalent between source and replay. Every other statement of the boundary in the corpus is half-open: replay.md §The surface and its host algorithm, runs.md §Fork, RFC 0036 §E ("the projected event log up to fromSeq"), and — decisively — the clause's own section heading, which scopes it to the prefix, a term the same document defines 23 lines earlier as sequence < fromSeq. The closed bracket was never jointly satisfiable with §Divergence, which requires a host to continue and emit replay.diverged when a replayed node produces an event different from the source at the same sequence: the inclusive reading demanded byte-equivalence of an event the very next section permits to differ. W3C Process Class 3, on the same reasoning as the RFC 0057 §D and a2a-push-egress-ssrf entries above. No MUST moved, no shape, code or status changed, and nothing that was conforming stops being so — a host inheriting [0, fromSeq] contradicted four other statements and was never conforming; the corpus now says so unambiguously. Not a §2.2 break and not a §3 safety-fix. Origin: the bracket enters the corpus at RFC 0041 §C and propagated from there to both spec documents and to RFC 0068 §"Replay determinism through a consolidation pass"; the RFCs are left as the historical record of their decisions and are not retroactively edited. A tier-1 host shipped sequence <= fromSeq — exactly what the closed bracket licensed — and passed every unaided major-2 scenario, finding it only when a second defect (1-based sequence numbering) stopped cancelling it. Two errors cancelling is why an instrument, and not a review, is what closes this class; the instrument is v2-run-fork-prefix (suite 2.0.0-rc.60), which asserts the half-open boundary directly. Known residue, recorded rather than closed: that scenario compares inherited rows by sequence and type only, so the byte-equivalence this clause requires is still unwitnessed at the payload level, and clause 2's per-index snapshot claim has no unaided witness at all — GET /runs/{runId} serves the snapshot at head only, and diffRun's stateDiff is terminal state computed by the host under test.

Safety-fix classifications on record (2026-08-16): RFC 0148 (certification evidence — bundle v1 sunset, unclassified returns rejected; the wire contract is untouched), RFC 0149 §A/§C (machine-contract defects — the /v1/v1 base-path duplication and the protocolVersion grammar, both repairs of generators/validators rather than moves of any endpoint), and RFC 0150 §B (retiring the attempt-bearing idempotency identity). None of the three moved a wire shape a conforming host emitted; each is recorded here because it changed what a validator accepts, which §2.2 treats as a break.

4. Behavior-only changes

Some changes don't touch wire shapes but change observable behavior:

ChangeAllowed in v1.x?
New optional capability advertised, off by defaultYes — additive
Existing optional capability becomes default-on (changes observed behavior on hosts that didn't advertise it)Only via safety-fix process
Performance improvement that changes observed timingYes — outside the scope of compatibility (timing is not a normative wire surface unless scale-profiles.md documents it)
Stricter validation rejecting input that previously succeededOnly via safety-fix process
Looser validation accepting input that previously failedYes — additive (clients that sent invalid input were already broken)
New normative requirement on a previously-undefined behaviorYes — additive (the spec was previously silent)

When in doubt, file an RFC and let the comment window surface compatibility concerns.

5. v2 plan

The v1 contract is locked. Any change that:

  • Removes or renames an existing required field; or
  • Changes an existing field's type, semantics, or required/optional status; or
  • Changes an existing event type's shape; or
  • Removes or changes an existing endpoint's contract beyond additive fields; or
  • Deprecates an existing capability such that clients pinned to v1.x cannot continue to operate

ships as part of the v2 spec major. A v2 RFC must include:

  • A migration plan for v1.x implementers.
  • A coexistence plan: how v1 and v2 servers/openwops interoperate during the transition (typically a discovery field that advertises support for both).
  • A deprecation timeline for v1, computed by the host-inventory rule below rather than by a fixed calendar.
  • An updated conformance suite major (@openwop/openwop-conformance@2.0.0).

v1.x and v2 ship as parallel tracks. v1.x continues to receive additive and safety-fix releases until the v1 deprecation date.

Host-inventory rule for the v1 deprecation date (2026-09-02). v1 support ends at the later of:

1. Every host listed in INTEROP-MATRIX.md has published a non-vacuous v2 certification bundle (a bundle whose claimed profiles are witnessed by executed assertions on a deployed origin, per RFC 0148), plus 90 days; and 2. 18 months from the v2 release, if and only if the matrix lists at least one host operated by an organization other than the steward at the time v2 is released.

The rule replaces the earlier "typically 18–24 months" guidance. The project is currently operated by a single steward whose two hosts are the only conforming implementations (GOVERNANCE.md §"Sole-steward operation"); a calendar window written for external implementers who do not exist would only delay v2 without protecting anyone. Clause 2 exists so that the day an independent host appears, the calendar floor protects it without a further amendment. The v2 RFC restates this rule normatively; until then it is release policy under GOVERNANCE.md §"Amendments".

6. Pinning recommendations

For host implementers

  • Advertise the highest spec minor your host passes. Don't hide additive capabilities to "stay compatible" — additive capabilities ARE the compatibility model.
  • Pin the conformance suite version you pass in your README. Re-run the suite per release; if a suite minor breaks your host, decide whether to fix or to keep pinning to the older suite version.
  • Subscribe to RFCS/ for normative additions before they ship.

For client implementers

  • Pin SDK to the spec major you target (@openwop/openwop@^1.0 for v1.x).
  • Treat unknown fields and unknown event types as forward-compat extensions (ignore them).
  • Read CHANGELOG.md between SDK upgrades for any safety-fix advisories.

For application authors building on a host

  • Pin to the host's advertised conformance suite version, not to the spec version directly. The host knows what it implements.
  • Use the host's discovery document (/.well-known/openwop) to detect optional capabilities. Don't assume capabilities the host hasn't advertised.

7. Deprecation policy

The §2.2 prohibitions apply to deprecation as well: an existing surface MAY be marked deprecated in spec text and SDK output, but MUST continue to behave as documented through the v1 lifecycle. Deprecation flags signal "this will be gone in v2"; they don't trigger v1.x removal.

A deprecation in v1.x requires:

  • An RFC explaining the planned v2 replacement.
  • A spec annotation (> Deprecated: …) that points to the RFC.
  • An SDK warning (where the SDK can detect use of the deprecated surface).
  • A CHANGELOG.md entry under ### Deprecated.

Deprecated surfaces continue to pass conformance. The Deprecated: annotation is informational, not normative.

Deprecation register (2026-09-02). Every surface that an RFC or a spec annotation has deprecated is listed, with its authority, replacement, and scheduled removal version, in spec/v1/deprecations.json (schema: spec/v1/deprecations.schema.json; gate: scripts/check-deprecations.mjs, run by npm run openwop:check). In v1.x the register is an index, not a source of obligations: it records deprecations the RFC process has already made and never creates one. Entries carry status: "deprecated" only when an RFC or annotation exists; surfaces the v2 program proposes to deprecate carry status: "proposed" with deprecatedIn: null, so a reader can tell the two apart. The v2 major makes the register normative (removal dates enforced at merge); see the v2 charter program item C.11.

8. What this document doesn't cover

  • Implementation-internal contracts. A host's storage format, internal API, or RPC shape is the host's call. Compatibility within a host's implementation is the host's responsibility.
  • Non-normative spec text. "Why this exists," examples, reference notes — these may change freely. Compatibility applies only to normative requirements (MUST/SHOULD/MAY).
  • Conformance fixture wording. Fixture names and human-readable descriptions are not part of the wire contract.

9. References

  • GOVERNANCE.md — decision rules; this document tells maintainers what counts as additive vs. breaking.
  • RFCS/0001-rfc-process.md — the RFC mechanism through which compatibility-affecting changes ship.
  • SECURITY.md — embargoed disclosure process referenced by the §3 safety-fix exception.
  • ROADMAP.md — what's planned for v1.x and post-v1; references this document for the change-class definitions.
  • MAINTAINERS.md — who has authority to merge changes that affect this commitment.