Declare x-lifecycle on the 36 preview operations; refresh contract-drift.json

YA
1mo ago
+70-7

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.

checkbeforeafter
published-drift.mjsexit 2 — 36 shared-driftexit 0 — 0 findings
published-drift-freshness.mjsexit 2 STALEexit 0 FRESH
node --test .github/scripts/*.test.mjs93/93 pass93/93 pass
node --test scripts/ga/**/*.test.mjs35/35 pass35/35 pass
redocly lint openapi.yamlvalidvalid
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-digest still fails. It is a different comparison from the one this PR
    clears: it folds resolved $ref content 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
    , because content-digest builds its digests over all 255
    shared operations without subtracting the allowlist that its sibling operation-parity criterion
    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-drift still 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


View with [code]smith Autofix with [code]smith
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: preview to eliminate operation-level contract drift.
  • Regenerate contract-drift.json so 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.