Skip to content

feat(api-wiring): closest-route suggestions + surface removed endpoints hiding in the baseline (#12738) - #12893

Merged
mrveiss merged 1 commit into
Dev_new_guifrom
issue-12738
Jul 28, 2026
Merged

mrveiss merged 1 commit into
Dev_new_guifrom
issue-12738

Conversation

@mrveiss

@mrveiss mrveiss commented Jul 28, 2026

Copy link
Copy Markdown
Owner

Thinking Path

Gap 1 asked for a closest-match suggestion. The obvious implementation — difflib.get_close_matches over the OpenAPI path set — ranks by character overlap, which favours long unrelated routes over the short renamed sibling that is usually the right answer. So the scoring is segment-aware, and weighted toward agreement on the leading segments: a rename almost always changes the last segment and keeps the namespace.

That weighting turned out to matter for a second reason. Running against static mode first produced suggestions like /api/analytics/bug-prediction/cached → /voice/bug-prediction/cached, because static mode's route table combines every registry prefix with every route, so /voice/<x> and /ai-stack/<x> shadow the real /<x>. Positional agreement alone ranks those highly; leading agreement rejects them.

Gap 2 needed a sharper definition than the issue's wording. "Baseline entries whose target is absent from the current OpenAPI" describes every entry — that is what being baselined means. The distinction that actually matters is whether a close surviving route exists: if it does, the endpoint was renamed and the call wants rewiring, not suppression. That plus two hygiene buckets (entries that now resolve, entries nobody calls any more) is what the baseline could never tell apart.

To check this against real data rather than static-mode heuristics I built the authoritative route table locally (--dump-openapi, 2161 paths + 281 websocket). It confirmed the gap 2 hypothesis exactly: 32 of the 35 baselined calls point at endpoints that were renamed, not endpoints that were never built.

What Changed

scripts/audit_api_wiring.py

  • suggest_routes() — segment-aware closest match, scored 0.45 · positional + 0.35 · leading + 0.20 · difflib, floored so a weak match prints nothing rather than sending someone to rewire against the wrong route. Requires a shared concrete segment. Scores both the call and its /api-stripped variant, matching what matches() already does for static mode.
  • audit_baseline() — classifies baseline entries into rematch (close surviving route → renamed, fix it), resolved (now matches a real route → prune) and absent (no caller left → prune).
  • Findings print their suggestions; a new non-gating BASELINE HEALTH section reports the classification. The gate itself is unchanged — same exit codes, same failing set.

scripts/api_wiring_baseline.txt — pruned the 3 entries the authoritative run proved were dead weight (2 resolved, 1 no longer called). Re-running after the prune reports 0 resolved, 0 no-longer-called and the same 5 gating findings, so the gate did not move.

~/.claude/skills/api-wiring-audit/SKILL.md — documents the suggestion output and the three baseline-health classifications, including the warning to trust them only in authoritative mode.

Verification

$ python3 -m pytest scripts/audit_api_wiring_suggestions_test.py -q
12 passed in 0.15s

Authoritative run (route table built locally from this branch's source):

$ python3 scripts/audit_api_wiring.py --dump-openapi openapi.json
[dump-openapi] wrote 2161 paths (+281 websocket)

$ python3 scripts/audit_api_wiring.py --openapi openapi.json --baseline scripts/api_wiring_baseline.txt
mode: AUTHORITATIVE (openapi)
== TRACKED UNWIRED CALLS (baselined, non-gating): 35 ==
== UNWIRED FRONTEND CALLS: 5 ==
== BASELINE HEALTH: 32 rematch, 0 resolved, 0 no-longer-called ==
  REMOVED-ENDPOINT DRIFT  /api/browser/navigate
      ?  did you mean /api/browser/mcp/navigate
  REMOVED-ENDPOINT DRIFT  /api/browser/screenshot
      ?  did you mean /api/browser/mcp/screenshot
  REMOVED-ENDPOINT DRIFT  /api/browser/click
      ?  did you mean /api/browser/mcp/click
  ...

The browser endpoints moved under /mcp/ and the baseline absorbed every caller — dead buttons, gate green. That is exactly the escape route gap 2 predicted, now visible.

Suggestion quality before/after the leading-segment weighting, on the same static-mode run: 90+ suggestions including nonsense like /voice/bug-prediction/cached, down to 31 with the noise gone.

Black and flake8 clean.

Scope note

Acceptance criterion 3 — rewiring the flagged callers — is not in this PR. It is 32 frontend call sites across composables and repositories, a different risk profile from tooling changes, and several need confirming that the target is genuinely the replacement rather than a same-named neighbour. Filed separately with the full evidence so it is not lost.

Closes #12738 (gaps 1 and 2, plus docs). Gap 3 (reconciling the two detectors) depends on #12853, which cannot be verified without the live instance.

Model Used

claude-opus-5

@github-actions

Copy link
Copy Markdown
Contributor

✅ SSOT Configuration Compliance: Passing

🎉 No hardcoded values detected that have SSOT config equivalents!

@mrveiss
mrveiss merged commit 35705cf into Dev_new_gui Jul 28, 2026
29 checks passed
@mrveiss
mrveiss deleted the issue-12738 branch July 28, 2026 15:13
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