improvement(api): retire route contracts that no route serves
Summary
The staging integ-failure alarm fired because integ-session-kb-document-lifecycle/create-document-from-inline-content got HTTP 405 instead of 200.
Root cause: #7179 moved tool operations in process and deleted the routes that only existed to serve them, but createKnowledgeDocumentsContract kept declaring POST /api/knowledge/[id]/documents. The surviving GET/PATCH on that path make Next.js answer POST with 405 rather than an honest 404 — which reads as "wrong verb, endpoint is fine" and sends the caller looking in the wrong place.
Nothing in the repo called that endpoint: the KB UI creates documents through the presigned upload flow, and no SDK, desktop, docs, or client fetch references it. The capability is unaffected either way — knowledge_create_document is a shipped tool that reaches the same use case in process. So this retires the declaration rather than restoring the route.
Audit
Checked all 1125 contracts for the same drift. The knowledge one was the only contract whose path resolves to a live route missing its declared method. 259 others declare paths of routes deleted outright — those 404 honestly and are left alone.
Changes
- Knowledge create-documents — replaced the route contract with plain
params/bodyschemas plus a named response schema, so nothing declares an endpoint we don't serve. Schemas stay in the contracts tree beside the siblings they share (documentDataSchemais used by the v2 contracts;createKnowledgeDocumentsBodySchemaalready backed the in-process operation). - Deleted 4 contracts with no consumer at all — both TTS contracts, docusign, mistral. Their handlers own strictly better schemas: TTS dispatches by
toolIdacross 8 per-provider schemas instead of one.passthrough()superset covering 7 providers, and mistral boundspagesby the OCR request policy. This removes duplication rather than creating it. - Kept crowdstrike and windchill. They look identical from the outside but are load-bearing —
crowdstrikeQueryBodySchemais parsed bylib/internal/crowdstrike/execute-tool.ts,windchillOperationResponseSchemabytools/windchill/utils.ts, plus derived types across both. - New audit
check:api-contract-routes, auto-discovered byrun-audits. Also documents the two contract consumption modes onApiRouteContract, with membership derived (--list-in-process) rather than annotated per file.
Why the new audit isn't redundant with check:route-verbs
check-route-verbs scans routes → contracts, so a contract whose route method was deleted is invisible to it. Verified against the exact regression state: check:route-verbs passes clean (359 handlers) while the new check fails with the precise diagnosis.
Not in scope
The repo is split on where in-process schemas live — 54 lib/internal domains declare them inline, 36 import from contracts, 18 are mixed. Converging on the contracts tree is worth doing but is a 54-domain migration, not this PR. Nothing here makes that split worse: the four deletions each removed a dead duplicate, leaving one source of truth.
Testing
bunx turbo run type-check— cleanbun run lint:check— cleanbun run check:audits— 38/38 pass, including the new audit- 1019 tests across 82 files in the touched areas (knowledge, table, tool-operations, contracts, crowdstrike, windchill, mistral, tts)
- Negative-tested the new audit: reproduces the staging failure at lint time, passes once resolved
🤖 Generated with Claude Code