Skip to content

fix(openapi): one endpoint documented twice disabled the contract gate - #62

Merged
LMPrado-DZ23 merged 2 commits into
release/v3.8.55from
fix/openapi-duplicate-templated-paths
Sep 20, 2026
Merged

LMPrado-DZ23 merged 2 commits into
release/v3.8.55from
fix/openapi-duplicate-templated-paths

Conversation

@LMPrado-DZ23

Copy link
Copy Markdown
Owner

Found while reading why #53's Fast Quality Gates run was red. It is not #53's defect — it is on the base, and it has been quietly costing us the contract gate.

The defect

docs/openapi.yaml carried both spellings of the same two endpoints:

Spelling What it was
/api/tools/agent-bridge/agents/{agentId}/dns hand-written, full schemas and request bodies
/api/tools/agent-bridge/agents/{id}/dns generated stub — summary: "POST tools › agent bridge › agents › <id> › dns", 200 OK, nothing else

…and the same pair for /mappings. The generator emitted stubs for routes that were already documented by hand, because it matched the folder name [id] while the hand-written entries used {agentId}.

OpenAPI 3.x is explicit: templated paths differing only in the variable name are the same endpoint and must not coexist. The document was invalid.

Why it matters

The consequence is not cosmetic. oasdiff refuses the whole document:

Error: diff failed: duplicate endpoint (GET /api/tools/agent-bridge/agents/{id}/mappings)
found in /tmp/oasdiff-base-….yaml and /tmp/oasdiff-base-….yaml

check:openapi-breaking cannot diff anything, so it falls through to its graceful skip and exits 0. The gate meant to catch contract regressions has been reporting a green that measured nothing — on every PR, not just this one.

The fix

Merged into the route's real parameter name (the folder is [id]), keeping the hand-written schemas and carrying over the x-loopback-only: true the stubs were holding — check-openapi-security-tiers requires it on these prefixes, so dropping the stubs without it would have traded one silent hole for another.

Spec paths 720 → 718, operations 1048 → 1045. The three removed are the stub duplicates; every operation is still documented, under the surviving path.

Keeping it fixed

check-openapi-routes was blind to this by construction: it compares param-insensitively, so both spellings matched the one real route and both passed. That is the right comparison for its own job, so the detector is additive — it now also reports paths that collapse to the same endpoint.

Proven red on the spec before the fix:

[openapi-routes] 2 endpoint(s) documentado(s) mais de uma vez (mesmo path, nome de parâmetro diferente):
  ✗ /api/tools/agent-bridge/agents/{}/dns
      /api/tools/agent-bridge/agents/{agentId}/dns
      /api/tools/agent-bridge/agents/{id}/dns
  ✗ /api/tools/agent-bridge/agents/{}/mappings
      …
exit=1

tests/unit/openapi-duplicate-templated-paths.test.ts covers the detector on a synthetic collision, checks that genuinely distinct paths are not flagged (a false positive here would make the gate unusable), and pins the shipped spec.

Gates

Gate Result
check:openapi-routes OK — 718 paths, all with a real route
check:openapi-security-tiers PASS
check:openapi-coverage PASS — 100.0% (718/718)
check:api-governance PASS — 718 routes, 1045 operations
tests/unit/openapi-duplicate-templated-paths.test.ts 3 pass / 0 fail
prettier --check clean

docs/openapi.yaml is not prettier-clean on the base either, so it is left as it is rather than reformatted in a fix PR.

Note for whoever reads the breaking-change baseline next: metrics.openapiBreaking in config/quality/quality-baseline.json was frozen while the gate could not run. Once oasdiff diffs a valid document again, that number should be re-measured rather than trusted.

🤖 Generated with Claude Code

zodyprado-web and others added 2 commits September 19, 2026 23:14
The [3.8.55] section stopped at #47 while eight more PRs landed. A release
note written at tag time from memory is how work goes unrecorded, so this
catches the section up while the PRs are still fresh:

  #48 contract tests — governance debt 150 → 53 untested routes
  #50 Add API Key links to the page that issues the key
  #51 phantom operations zeroed; the policy CLI stops overclaiming
  #52 workspace → project → API key hierarchy with rolled-up budgets
  #55 dark theme meets AA without repainting the brand
  #57 11 more CLIs pointable at the gateway (catalog 36 → 47)
  #58 the key URL moved to the field the product actually reads
  #59 the new-code gates run on Windows again

Mirrored into the 41 localized changelogs by
`scripts/release/sync-changelog-i18n.mjs`.

  [changelog-integrity] OK — no base bullets lost
  [doc-links] PASS — 172 docs, 1044 internal links
  prettier --check CHANGELOG.md — clean

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
docs/openapi.yaml carried both spellings of the same two endpoints:

  /api/tools/agent-bridge/agents/{agentId}/dns       hand-written, full schemas
  /api/tools/agent-bridge/agents/{id}/dns            generated stub
  …and the same pair for /mappings

OpenAPI 3.x is explicit that templated paths differing only in the variable
name are the same endpoint and must not coexist. The consequence was not
cosmetic: oasdiff refuses the whole document with

  Error: diff failed: duplicate endpoint
  (GET /api/tools/agent-bridge/agents/{id}/mappings)

so check:openapi-breaking could not diff anything and fell through to its
graceful skip. The gate meant to catch contract regressions has been
reporting a green that measured nothing.

Merged into the route's real parameter name — the folder is `[id]` — keeping
the hand-written schemas and carrying over the `x-loopback-only: true` the
stubs were holding for check-openapi-security-tiers. Spec paths 720 → 718,
operations 1048 → 1045; the three removed are the stub duplicates, and every
operation is still documented under the surviving path.

check-openapi-routes was blind to this by construction: it compares
param-insensitively, so both spellings matched the one real route and both
passed. It now reports paths that collapse to the same endpoint — proven red
on the spec before the fix.

  [openapi-routes] 2 endpoint(s) documentado(s) mais de uma vez  exit=1
  [openapi-routes] OK — 718 paths na spec, todos com rota real  (after)
  [openapi-security-tiers] PASS  [openapi-coverage] PASS 100.0% (718/718)
  [api-governance] PASS — 718 routes, 1045 operations
  tests 3 / pass 3 / fail 0

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Sep 20, 2026

Copy link
Copy Markdown

Important

  • 🔍 Trigger review

This repository does not receive automatic reviews because it has fewer than 10 stars.

⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Advanced

Run ID: f73b07e4-463e-44e7-b17d-247c689c4708


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@LMPrado-DZ23
LMPrado-DZ23 merged commit 55c28b5 into release/v3.8.55 Sep 20, 2026
15 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.

2 participants