Skip to content

fix(cli): prevent docs-scraper endpoint-name collisions from dropping routes#3789

Draft
krMaynard wants to merge 1 commit into
mvanhorn:mainfrom
krMaynard:fix/docspec-name-collision
Draft

fix(cli): prevent docs-scraper endpoint-name collisions from dropping routes#3789
krMaynard wants to merge 1 commit into
mvanhorn:mainfrom
krMaynard:fix/docspec-name-collision

Conversation

@krMaynard

@krMaynard krMaynard commented Jul 25, 2026

Copy link
Copy Markdown

Intent

The docs scraper's endpoint-name disambiguation could assign the same generated name to two different documented routes. Because endpoints are stored in a map, the later assignment silently overwrote the earlier one — a real API operation disappeared from the generated spec with no error. It happened when a generated suffix (e.g. get_users_2 for a second GET /users) collided with another route's natural name (e.g. GET /users/users_2).

Issue: Fixes #3786

Approach

In groupByResource, allocate each endpoint name against the set of names already used in the resource, incrementing the numeric suffix until an unused name is found — instead of counting collisions only by the original derived name. Iterating in documentation order keeps naming deterministic; ordinary _2/_3 disambiguation is unchanged; and a natural name that already ends in a numeric suffix can no longer be clobbered.

Scope

Primary area: internal/docspec (documentation-derived spec builder).

Why this belongs in this repo: machine code — endpoint-name derivation in the docs-to-spec path, shared by every doc-sourced generation.

Risk

Low. The only behavioral change is in the previously-broken collision case (a route that used to be dropped is now kept under a unique name). Ordinary disambiguation output is identical.

Output Contract

Endpoint-name derivation can affect generated command names, so this is output-relevant. scripts/golden.sh verify passes all 32 cases unchanged — no existing fixture exercises the pathological collision, and ordinary suffixing is byte-identical, so the fix only adds a previously-missing endpoint in the collision case. Regression coverage is added at the unit level in internal/docspec rather than as a new golden fixture.

Verification

  • go test ./internal/docspec/ — passes, incl. new cases: three-route collision (none dropped) and ordinary deterministic _2/_3 suffixing.

  • scripts/golden.sh verify — 32/32 cases pass (no generated-output drift).

  • go build ./..., go vet ./internal/docspec/, gofmt -l — clean.

  • Generator/template change: verified generated output via scripts/golden.sh verify (32 cases) plus unit regression tests on the naming logic

  • Generator/template change: covered the collision variant and ordinary-suffix variant, not only happy path

  • Generator/template change: change is localized to name allocation in groupByResource; no emitted definition/call-site gates involved

AI / Automation Disclosure

  • Human-reviewed: AI or automation was used, and a human reviewed the work for intent, fit, and obvious issues before submission

… routes

groupByResource disambiguated colliding endpoint names with a counter
keyed on the original derived name: the second GET /users became
get_users_2. But that generated suffix could equal the *natural* name of
another documented route (e.g. GET /users/users_2), and because endpoints
are stored in a map, the later assignment silently overwrote the earlier
one — a real API operation vanished from the generated spec with no error.

Allocate each name against the set of names already used in the resource,
incrementing the suffix until an unused name is found. Naming stays
deterministic in documentation order, ordinary _2/_3 disambiguation is
unchanged, and a natural name that already ends in a numeric suffix can no
longer be clobbered.

Adds regression coverage for the collision case (three routes, none
dropped) and for ordinary deterministic suffixing.

Refs #11
@mergify

mergify Bot commented Jul 25, 2026

Copy link
Copy Markdown
Contributor

Merge Protections

🔴 2 of 2 protections blocking · waiting on 🤖 CI and 🙋 you

Protection Waiting on
🔴 require-ready-label-and-ci 🤖 CI and 🙋 you
🔴 🚦 Auto-queue 🙋 you

🔴 require-ready-label-and-ci

Waiting for

  • any of:
    • label = ready-to-merge
    • title ~= ^chore\(main\): release
  • any of:
    • check-neutral = Greptile Review
    • check-skipped = Greptile Review
    • check-success = Greptile Review
    • label = queued
    • title ~= ^chore\(main\): release
This rule is failing.
  • any of:
    • label = ready-to-merge
    • all of:
      • head = release-please--branches--main
      • title ~= ^chore\(main\): release
  • any of:
    • check-neutral = Greptile Review
    • check-skipped = Greptile Review
    • check-success = Greptile Review
    • head ~= ^mergify/merge-queue/
    • label = queued
    • all of:
      • head = release-please--branches--main
      • title ~= ^chore\(main\): release
  • #changes-requested-reviews-by = 0
  • #review-threads-unresolved = 0
  • check-success = build-and-test
  • check-success = generated-test
  • check-success = go-lint
  • check-success = golden
  • check-success = pr-title
  • check-success = test
  • any of:
    • -files ~= ^(\.github/workflows/|\.github/scripts/|scripts/|\.github/CODEOWNERS$)
    • approved-reviews-by = mvanhorn
    • approved-reviews-by = tmchow
    • author = mvanhorn
    • author = tmchow

🔴 🚦 Auto-queue

Waiting for

  • label = ready-to-merge
This rule is failing.

When all merge protections are satisfied and these conditions match, this pull request will be queued automatically.

  • label = ready-to-merge

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.

fix(cli): docs scraper endpoint-name collision silently drops routes

1 participant