Skip to content

ci(openapi): oasdiff breaking-change gate (Wave 1 contract-drift)#264

Merged
mastermanas805 merged 1 commit into
masterfrom
feat/wave1-oasdiff-breaking-gate
Jun 5, 2026
Merged

ci(openapi): oasdiff breaking-change gate (Wave 1 contract-drift)#264
mastermanas805 merged 1 commit into
masterfrom
feat/wave1-oasdiff-breaking-gate

Conversation

@mastermanas805

@mastermanas805 mastermanas805 commented Jun 5, 2026

Copy link
Copy Markdown
Member

Wave 1 contract-drift gate — docs/ci/01-CI-INTEGRATION-DESIGN.md (HIGHEST ROI).

Producer-side half of the UI↔API contract-drift gate. The consumer-side half is InstaNode-dev/instanode-web#191 (openapi-typescript codegen makes the same rename fail tsc).

What

.github/workflows/openapi-breaking.yml: on every PR touching openapi.snapshot.json, diff it against the base branch's committed snapshot with oasdiff (pinned v1.11.7) and fail the PR on a breaking change. Pure additions pass.

Why --fail-on WARN (not ERR)

The api emits response fields as optional (no required array on response schemas). oasdiff classifies removing an optional response property as WARN, not ERR — yet that removal is exactly the login-break class (UI reads me.tier, api drops it, UI gets undefined). --fail-on ERR would miss it; WARN catches it while still passing additive changes. Documented in api/docs/OPENAPI-CONTRACT-GATES.md.

Verified locally (oasdiff v1.11.7)

Edit to snapshot Result
remove AuthMeResponse.tier (optional response field) fails (WARN) ✓
rename ResourceItem.storage_bytes fails
add new required request field (ClaimRequest) fails (ERR) ✓
remove endpoint (/api/v1/resources) fails (ERR) ✓
add optional response field passes ✓
add new endpoint passes ✓
remove enum value from a response passes (info — not breaking for a consumer) ✓
no change passes ✓

Snapshot confirmed current vs cmd/openapi-snapshot at master HEAD.

Intentional breaking changes

The gate failing is the signal, not a wall (rule 22): land the api change + update the instanode-web consumer (regenerate generated.ts; its tsc reds at every UI site using the removed field) in a linked PR, merge via repo-admin. No in-workflow allowlist — every override is a recorded human decision.

🤖 Generated with Claude Code

Wave 1 contract-drift gate — docs/ci/01-CI-INTEGRATION-DESIGN.md.

Adds .github/workflows/openapi-breaking.yml: on every PR touching
openapi.snapshot.json, diff it against the base branch's committed
snapshot with oasdiff (pinned v1.11.7) and FAIL the PR on a breaking
change (removed/renamed/retyped response field, new required request
field, removed endpoint/response code). Pure additions pass.

Uses --fail-on WARN, not ERR: the api emits response fields as optional,
and oasdiff classifies removing an optional response property as WARN —
yet that removal is exactly the login-break class (UI reads me.tier, api
drops it, UI gets undefined). ERR would miss it; WARN catches it while
still passing additive changes. Verified locally against removed-field,
rename, new-required-request-field, removed-endpoint (all red) and
optional-field-add / new-endpoint / response-enum-removal (all green).

The companion consumer-side gate lives in instanode-web (openapi-typescript
codegen makes the same rename fail tsc in npm run gate). Together they move
contract drift from a runtime prod break to a compile/PR-time failure.

Docs: api/docs/OPENAPI-CONTRACT-GATES.md.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@mastermanas805 mastermanas805 enabled auto-merge (squash) June 5, 2026 20:13
@mastermanas805 mastermanas805 merged commit 006aa6b into master Jun 5, 2026
19 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant