Version policy
read as.md Semver promise: a change to
@ggui-ai/protocolis breaking if-and-only-if a consumer built against the prior version now fails the conformance kit.
This page defines “major”, “minor”, and “patch” on the ggui protocol, how deprecation windows work, which server ↔ client pairs are supported, and how a breaking change is migrated.
The canonical version constant is PROTOCOL_SCHEMA_VERSION (an alias of PROTOCOL_VERSION in @ggui-ai/protocol). Current value: draft-2026-06-12. Wire-level negotiation runs through the schema-version handshake; mismatches surface as the UPGRADE_REQUIRED error. The current server default is versionPolicy: 'reject' — on mismatch the server emits UPGRADE_REQUIRED and closes the connection; 'advisory' is a legacy opt-out that keeps it open.
1. Semver semantics applied to the protocol
Section titled “1. Semver semantics applied to the protocol”The protocol follows semver. What counts as each bump kind is defined by the conformance kit (packages/protocol-conformance), not by surface-syntax changes in the @ggui-ai/protocol package.
Major (N → N+1)
Section titled “Major (N → N+1)”A bump is major iff at least one conformance-kit fixture that passed against version N now fails against version N+1 when the only change is the protocol version.
Examples of changes that are major:
- Renaming a reserved channel (e.g.,
_ggui:preview→_ggui:assembly). - Removing a canonical live-channel error code that the kit asserts (e.g., deleting
CONTRACT_VIOLATION). - Narrowing an extensibly-closed union (e.g., collapsing
SubmitActionKindvariants so previously-recognized kinds now fail validation). - Changing the shape of an envelope (
ActionEnvelope,StreamEnvelope) in a way that invalidates fixtures emitted by prior-version producers — required-field additions, required-field renames, required-field type changes. - Tightening a
MUST/MUST NOTclause in the spec such that prior-conformant implementations become non-conformant. - Changing handshake semantics in a way that rejects a subscribe that would previously have succeeded (e.g., the
versionPolicydefault flipadvisory→reject— already shipped on 2026-04-24 as a pre-launch config-default change with its own release note; post-v1.0, a flip of this shape is major). - Removing or replacing a transport binding previously declared conformant.
Minor (N.x → N.(x+1))
Section titled “Minor (N.x → N.(x+1))”A bump is minor iff every conformance-kit fixture that passed against N.x still passes against N.(x+1), and the delta is additive.
Examples of changes that are minor:
- Adding a new
channel_errorcode literal (consumers that don’t recognize it degrade per the extensibly-closed union rule). - Adding a new observability event kind to the renderer’s
ggui:observepostMessage union (extensibly-closed — see the Bootstrap handshake page). - Adding an optional field on an envelope. Pre-existing consumers see
undefinedand MUST behave as before. - Adding a new reserved channel whose absence is not a failure mode for prior-version consumers.
- Adding a new canonical live-channel error code (the
codefield is typed open). - Adding a new transport binding (e.g., stdio MCP, HTTP long-poll) while keeping WebSocket canonical.
Patch (N.x.y → N.x.(y+1))
Section titled “Patch (N.x.y → N.x.(y+1))”A bump is patch iff the only changes are documentation clarifications, fixture additions that do not change assertions on prior-version consumers, or internal implementation adjustments to first-party packages that do not affect the wire or the kit.
Examples:
- Spec prose clarifications that close a reader-ambiguity without changing obligations.
- New conformance-kit fixtures that assert behavior already required by the prior version’s spec text.
- Bug fixes in first-party servers / clients that bring them back into conformance (the bar was always there — the code now honors it).
The draft- prefix
Section titled “The draft- prefix”Pre-v1 versions use a draft- prefix (e.g., draft-2026-06-12). While draft-, the semver rules above describe intent, not obligation — the protocol reserves the right to ship breaking changes without a migration doc until the first stable release tags v1.0.
Rule flip at v1.0: once PROTOCOL_SCHEMA_VERSION drops the draft- prefix, every major bump MUST have a migration doc (CI-enforced) and every deprecation MUST honor the window described in §3.
2. Breaking change definition — the kit is the arbiter
Section titled “2. Breaking change definition — the kit is the arbiter”A change is breaking if-and-only-if a consumer built against the prior version now fails the conformance kit.
Anchor every future “is this breaking?” debate to a kit run. Opinion, intent, and prose-level spec reading are NOT the arbiter — the kit is.
Operational consequences:
- If you’re unsure whether a PR is breaking, run the kit against the prior-version tag and against the PR branch. If fixtures that passed before now fail, the PR is major.
- If the kit passes but you “feel” the change is risky, add a fixture that captures the worry. Either the fixture passes (the change is not breaking) or it fails (the change is breaking and you just found out) — both outcomes are productive.
- If you think a change is major but the kit disagrees, the kit has a gap. Patch the kit in the same PR; the patched kit decides.
This anchor means every change’s breaking-ness is observable and reproducible, not a matter of taste.
3. Deprecation timeline
Section titled “3. Deprecation timeline”Deprecation is the only graceful path from a minor-version ship to a major-version removal.
The window
Section titled “The window”- v(N) — the version in which a surface is first tagged
@deprecated(in TSDoc) AND documented in the release notes as deprecated. The surface MUST continue to work identically to the prior version;@deprecatedis a signal to consumers, not a behavior change. - v(N+1) — deprecated surface still works. Callers SHOULD migrate in this window. Release notes repeat the deprecation warning.
- v(N+2) — removal is allowed here at the earliest. Removal is a major bump, so this version is v(N+2) = major. The two-minor window guarantees consumers saw at least one full minor cycle with the
@deprecatedwarning before removal.
Minimum
Section titled “Minimum”At least two minor versions MUST elapse between @deprecated and removal. If N is the first deprecated version and N+2 is the major that removes it, the intermediate v(N+1) minor MUST ship. Shorter windows (v(N) deprecated → v(N+1) removed) are NOT allowed for non-security changes.
Security-fix escape hatch
Section titled “Security-fix escape hatch”A critical security fix MAY skip the window — shipping a breaking change without a prior @deprecated minor — if and only if:
- The release note explicitly names the CVE or security class.
- The release note explains why the window was skipped.
- A migration doc per §5 ships alongside the release.
Non-security urgency (e.g., “this mistake is embarrassing”) is NOT grounds for skipping the window.
What “deprecated” means on the wire
Section titled “What “deprecated” means on the wire”The protocol does not have a wire-level “deprecated field” marker. Deprecation lives in TSDoc on the @ggui-ai/protocol types and in the release notes. Consumers parsing the wire cannot detect a field is @deprecated from the frame alone — they learn it from the package changelog.
Policy-default flips count as breaking
Section titled “Policy-default flips count as breaking”Changing a default that the wire handshake depends on is breaking under §2 because consumers that relied on the prior default observably fail against the new default. The canonical example already happened pre-launch: the versionPolicy default flip advisory → reject shipped on 2026-04-24 as a config-default change with its own release note. Post-v1.0, a flip of that shape MUST use the same window: ship the new value as an opt-in in v(N), document the flip in the release notes, then flip the default in v(N+2).
4. Version support matrix
Section titled “4. Version support matrix”Which server versions support which client versions. Populated at launch (v1.0); the row below is illustrative — the protocol is pre-v1 (draft-2026-06-12) today:
Server PROTOCOL_SCHEMA_VERSION | Min client | Max client | Status | EOL date |
|---|---|---|---|---|
1.0.x | 1.0.0 | 1.0.x | current (illustrative) | — |
Column meanings
Section titled “Column meanings”- Server version — the
PROTOCOL_SCHEMA_VERSIONthe server emits in the subscribe-ack payload’sserverVersion. - Min client / Max client — the lowest / highest entries in the client’s
CLIENT_SUPPORTED_VERSIONSset guaranteed to subscribe successfully. - Status — one of:
current— actively maintained; all fixes land here.security-only— no new features; only security patches.EOL— no longer maintained. Servers on EOL versions MAY refuse to start.
- EOL date — the date
currenttransitions tosecurity-only, orsecurity-onlytransitions to EOL.
Matrix update cadence
Section titled “Matrix update cadence”- New minor ship → append a row; prior row may update Max client.
- New major ship → append a new row; prior-major row transitions to
security-onlyfor at least 6 months before EOL. - Security-only → EOL → at least 6 additional months.
Out-of-matrix clients
Section titled “Out-of-matrix clients”If a client targets a version outside the matrix, the server emits UPGRADE_REQUIRED. Whether the connection stays open depends on the server’s versionPolicy at the time.
5. Migration-guide requirement
Section titled “5. Migration-guide requirement”Every major bump MUST ship a migration doc at docs/protocol/migrations/v<N>-to-v<N+1>.md. (Current pre-v1 drafts use date-named migration docs — e.g. 2026-06-05-gguisession-reintroduction.md; the v<N>-to-v<N+1>.md pattern applies from v1.0.) The CI check (§7) enforces the file’s existence; content follows a standard template with required sections:
- What changed — 1–2 paragraph summary.
- Why — motivation (conformance-kit failure mode that drove the change).
- Breaking-change summary — bullet list of renamed / removed / tightened surfaces.
- Migration steps per consumer kind — fixture authors, ConformanceHost implementers, agent builders, SDK consumers.
- Timeline — dates for v(N) deprecation ship, v(N+1) soft cut, v(N+2) removal.
- Rollback procedure — how to pin to prior version if a consumer can’t migrate fast enough.
6. Release cadence expectations
Section titled “6. Release cadence expectations”These are targets, not promises:
- Minor (N.x → N.(x+1)): roughly monthly. Additive adjustments driven by conformance-kit fixture gaps, new observability events, or new canonical error codes tend to accumulate on this cadence. Empty minor cycles are skipped rather than force-shipped.
- Major (N → N+1): annual or slower. Rare and pre-announced. A major ship is a disruption event for every downstream — the protocol leans hard on minor + deprecation windows to defer majors.
- Patch (N.x.y → N.x.(y+1)): ad hoc. Doc corrections, fixture additions that tighten existing assertions, and first-party impl bug fixes ship when ready.
Announcements ship in the version history maintained in @ggui-ai/protocol’s version.ts and in the public protocol release notes.
7. CI enforcement
Section titled “7. CI enforcement”Two workflows guard this policy:
- Spec drift — ensures spec envelope code blocks match the
@ggui-ai/protocoltypes. Catches structural drift that would otherwise ship as silent breakage. - Version migration — reads
PROTOCOL_SCHEMA_VERSIONon the PR head and on the base ref; if the major component changed, asserts the migration doc exists in the PR diff.
The second check is the policy’s teeth: you cannot ship a major bump without the migration doc the policy requires.
Appendix: Worked examples
Section titled “Appendix: Worked examples”A.1 Adding a new live-channel error code
Section titled “A.1 Adding a new live-channel error code”Scenario: a new failure mode — TOOL_DENIED — needs to land alongside the existing canonical literals on the WS error / channel_error frame.
- Bump kind: minor. Prior-version consumers that don’t know
TOOL_DENIEDsee an extensibly-closed (code: string) value and MUST degrade gracefully. No kit fixture asserting the prior closed set should fail. - Required work: add the constant to
@ggui-ai/protocol, document it as a canonical live-channel error-code literal, add a conformance-kit fixture asserting the new code round-trips. - Migration doc: none needed (not a major bump).
A.2 Removing a canonical error-code literal
Section titled “A.2 Removing a canonical error-code literal”Scenario: SCHEMA_MISMATCH_ERROR is merged into CONTRACT_VIOLATION with a structured causedBy.
- Bump kind: major. A consumer that pattern-matches on
SCHEMA_MISMATCH_ERRORnow never sees it, fails its fixture. - Required work: first ship
@deprecated SCHEMA_MISMATCH_ERRORin v(N), continue emitting bothSCHEMA_MISMATCH_ERRORand the new shape during v(N+1), then removeSCHEMA_MISMATCH_ERRORemission in v(N+2). - Migration doc:
vN-to-v(N+1).md(enforced by CI on the major-bump PR). Covers thecausedByshape, fixture changes, and consumer-side pattern-match migration.
A.3 Renaming a reserved channel
Section titled “A.3 Renaming a reserved channel”Scenario: _ggui:preview → _ggui:assembly for parity with transport-layer conventions.
- Bump kind: major. Every consumer that subscribes to the old name now fails. Every fixture that references the old name needs updating.
- Required work: can NOT ship via deprecation (reserved-channel names are matched verbatim). Must bundle with other breaking changes into the next major. Migration doc is non-trivial — every producer AND consumer changes.
- Operational note: changes of this shape are exactly why the protocol prefers extensibly-closed unions + additive fields + separate channels over in-place renames.
A.4 Clarifying a spec MUST
Section titled “A.4 Clarifying a spec MUST”Scenario: the spec’s refresh semantics paragraph has two readings; the kit’s fixture matched only one.
- Bump kind: patch IF the kit’s existing assertion already covered the intended reading (prose clarifies code); minor IF the clarification surfaces a new required behavior that prior-version impls weren’t necessarily honoring (new assertion); major IF the clarification tightens behavior such that previously-conformant impls now fail.
- Test: run the kit against the prior-version tag. If it passes, the clarification is patch/minor. If it fails, it was major all along — the original spec text was ambiguous and shipping the clarification is a breaking change.
See also
Section titled “See also”- Protocol overview — three-channel topology and reference implementation.
- Conformance kit — the test suite this policy treats as the arbiter.
- Conformance — the 4-criteria contract bar + 6-criteria protocol bar this policy gates.