Declare x-lifecycle on the 36 preview operations; refresh contract-drift.json
User description
What this fixes
published-contract-drift has no green run in its recorded history. The cause is a single field.
The published contract at https://api.wave.online/openapi.json carries x-lifecycle: preview on
exactly 36 of its 255 operations. openapi.yaml declared x-lifecycle nowhere — zero
occurrences. published-drift-compare.mjs diffs the union of each shared operation's keys, so all
36 surfaced as shared-drift and the gate exited 2, every run, for months.
This is the exact debt the CHANGELOG already recorded under the composer entry — "Not modelled here:
64 pre-existing x-lifecycle-field shared-drift findings" — deferred as out of scope at the time.
It is 36 today. This PR pays it off.
Why the declaration is the side that was wrong
The alternative reading is that x-lifecycle is gateway enrichment and the normalizer should strip
it. It is not, and the repo's own normalizer states the test it would have to pass:
strip op['x-version'] and op['x-deprecation-policy'], which the service assigns onto every
operation
That is the enrichment predicate: applied mechanically and uniformly. x-lifecycle fails it. It
appears on a selective 36 of 255, and the selection is semantically coherent — whole product
families (Editor, Collab, Podcast, Studio AI, Sentiment, Realtime, Engine, Gpu, AES67, Agents,
Bridge, Crest, NDI, OMT, SRT, ST2110, Vision). That is editorial information, not a serve-time
artifact, so normalizing it away would discard real signal and make the gate blind to a genuine
lifecycle change. published-drift.mjs is explicit that the remedy for shared-drift lives in this
repo: "the published contract is this repo's OUTPUT … the remedy edits a file that lives here."
It is also not a synonym for the existing x-schema-status: draft, so this is not a redundant
annotation: only 9 of the 36 preview operations are also declared draft. The two are orthogonal
axes — x-schema-status says whether the schema below is yet a promise to consumers;
x-lifecycle says where the product sits in its rollout.
Every value is copied verbatim from the published contract by a mechanical pass keyed on
path+method. No lifecycle is invented, and none is asserted for an operation the gateway does not
already publish one for. The insertion is line-based rather than a js-yaml round-trip, deliberately:
re-emitting a 14,533-line spec would have buried a 36-line semantic change under thousands of lines
of cosmetic churn. The diff is exactly 36 added lines.
The stranded receipt
contract-drift.json is regenerated in the same commit, because it was independently red. It was
generated 2026-09-06 and records 229 paths / 254 operations against a spec that now has
232 / 257 — the offline freshness job was failing on a receipt that no longer described the
spec sitting beside it. Regenerating is the correct fix here and the one the job's own error message
prescribes; it is not being used to paper over a verdict, since the drift verdict it now records is
a genuine clean run.
Proof — run locally, before and after
CI is currently unmeasured org-wide (an account-level billing lock; those jobs never started, so
they say nothing about this code). All evidence below is from local runs on this branch.
| check | before | after |
|---|---|---|
published-drift.mjs | exit 2 — 36 shared-drift | exit 0 — 0 findings |
published-drift-freshness.mjs | exit 2 STALE | exit 0 FRESH |
node --test .github/scripts/*.test.mjs | 93/93 pass | 93/93 pass |
node --test scripts/ga/**/*.test.mjs | 35/35 pass | 35/35 pass |
redocly lint openapi.yaml | valid | valid |
openapi-typescript → generated/ | — | byte-identical (vendor extensions don't reach codegen) |
No gate was weakened to get here: nothing was deleted, skipped, loosened, allowlisted or baselined.
No allowlist entry was added — the allowlist count is unchanged at 11.
Deliberately not fixed here
Both are real, both are separate root causes, and neither is silenced by this PR:
- CONTRACT-001
content-digeststill fails. It is a different comparison from the one this PR
clears: it folds resolved$refcontent into the digest, so it sees schema-level differences the
operation-level comparator cannot. 17 shared operations still differ under it — and 11 of those
17 are precisely the allowlisted ones, becausecontent-digestbuilds its digests over all 255
shared operations without subtracting the allowlist that its siblingoperation-paritycriterion
honours. That looks like a genuine defect in the criterion rather than a spec gap, but correcting
it reduces a failure count and so deserves its own reviewed change rather than a quiet rider here.
The remaining ~6 are real content differences needing per-operation judgement. live-route-driftstill fails. Its classifier rests on a premise that does not hold on the
current gateway, which needs its own PR and its own scrutiny.
🤖 Generated with Claude Code
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.
[!NOTE]
Low Risk
Spec-only metadata aligned with the published gateway contract; no request/response or auth behavior changes, and drift gates are tightened rather than relaxed.
Overview
Adds x-lifecycle: preview to 36 operations in openapi.yaml so the repo matches the gateway-published contract, which already tags those routes (Editor, Collab, Podcast, Studio AI, Sentiment, Realtime, Engine/GPU, and selected skills-index surfaces). Values are copied verbatim from the live OpenAPI document; this is separate from x-schema-status: draft.
contract-drift.json is regenerated in the same commit so operation counts and sharedDrift: 0 reflect the spec—fixing both the published-contract-drift gate (previously 36 shared-drift failures) and a stale offline freshness receipt. CHANGELOG documents the fix and notes that content-digest and live-route-drift are unchanged and still failing elsewhere.
Reviewed by Cursor Bugbot for commit 1f875d37eca52f637eb43e6ced82eb67636f7269. Bugbot is set up for automated code reviews on this repo. Configure here.
Summary by Sourcery
Align the OpenAPI specification with the published contract’s preview lifecycle metadata and refresh its drift receipt.
Bug Fixes:
- Declare the published contract’s 36 preview operations with
x-lifecycle: previewto eliminate operation-level contract drift. - Regenerate
contract-drift.jsonso the recorded receipt matches the current OpenAPI specification and passes freshness validation.
Documentation:
- Document the lifecycle declarations, regenerated drift receipt, and remaining out-of-scope drift checks in the changelog.
CodeAnt-AI Description
Align the API contract with published preview operations
What Changed
- Marks the 36 operations published as preview with
x-lifecycle: preview, including Editor, Collab, Podcast, Studio AI, Realtime, and other product APIs. - Refreshes the contract-drift record to match the current 232 paths and 257 operations.
- Records that content-digest and live-route drift remain separate unresolved checks.
Impact
✅ Published contract drift check passes
✅ Preview lifecycle status is visible in API documentation
✅ Contract freshness receipt matches the current API specification
💡 Usage Guide
Checking Your Pull Request
Every time you make a pull request, our system automatically looks through it. We check for security issues, mistakes in how you're setting up your infrastructure, and common code problems. We do this to make sure your changes are solid and won't cause any trouble later.
Talking to CodeAnt AI
Got a question or need a hand with something in your pull request? You can easily get in touch with CodeAnt AI right here. Just type the following in a comment on your pull request, and replace "Your question here" with whatever you want to ask:
@codeant-ai ask: Your question here
This lets you have a chat with CodeAnt AI about your pull request, making it easier to understand and improve your code.
Example
@codeant-ai ask: Can you suggest a safer alternative to storing this secret?
Preserve Org Learnings with CodeAnt
You can record team preferences so CodeAnt AI applies them in future reviews. Reply directly to the specific CodeAnt AI suggestion (in the same thread) and replace "Your feedback here" with your input:
@codeant-ai: Your feedback here
This helps CodeAnt AI learn and adapt to your team's coding style and standards.
Example
@codeant-ai: Do not flag unused imports.
Retrigger review
Ask CodeAnt AI to review the PR again, by typing:
@codeant-ai: review
Check Your Repository Health
To analyze the health of your code repository, visit our dashboard at https://app.codeant.ai. This tool helps you identify potential issues and areas for improvement in your codebase, ensuring your repository maintains high standards of code health.