Public: anyone can read this; the crew can change it.
Split src/contract/openapi.ts into one file per route #5949
Check · Review · Land · Live done — Landed and live in production.
`src/contract/openapi.ts` no longer holds the route rows. Each of the 200 rows is now its own file at `src/contract/openapi/routes/<operationId>.ts`, which exports `route`. A generated, sorted `routes/index.ts` gathers them. A pull that describes a new route now adds one file and one index line, and doesn't touch lines another pull adds. This follows the pattern #5932 sets up for the MCP tools.
shipped production is serving it deploy f8d173e
src/contract/openapi.ts no longer holds the route rows. Each of the 200 rows is now its own file at src/contract/openapi/routes/<operationId>.ts, which exports route. A generated, sorted routes/index.ts gathers them. A pull that describes a new route now adds one file and one index line, and doesn't touch lines another pull adds. This follows the pattern #5932 sets up for the MCP tools.
What moved where
- The route rows: moved word for word from
ROUTESinto one file each. Each file imports only what it uses. - Shared values:
Routeand the values several routes use (REPO,BROWSE,LIST_QUERY,LABEL_REFUSALSand theBROWSE_*values) moved tosrc/contract/openapi/shared.ts.APP_BODYandAPP_REFUSALSbelong to one route only, so they moved intopostApiV1Apps.ts. - What stays in
src/contract/openapi.ts:ROUTES, which now reads the index, andopenapi(). The operationId rule is now a named export,operationId(), so the document and the test that checks file names share one rule. Everything that imports from this file is unchanged. - New
scripts/openapi-routes.ts: writes the index;--checkexits 1 when the index is stale.
Is the document the same?
I compared openapi(origin, version) before and after the split. The content is identical; the only difference is the key order inside paths, which now follows the sorted index instead of the old hand-kept order. JSON key order carries no meaning, but the bytes served at /contract/openapi.json do change once. Keeping the old order would have needed a hand-kept order list, which is the same hot file again.
Tests
- New
test/openapi-routes.test.ts:
- each file exports only route;
- each file's name equals its route's operationId;
- each row has a valid method, path, summary, auth and status, and its body, query, reply and refusal fields are non-empty strings;
- the rows are unique and match the files one for one;
- the index is current;
- adding a file adds exactly one index line.
- Changed (pointers only, no assertions):
test/plan-words.test.tsnow reads the route file that saysSELF_WAIT, because the text moved there.test/openapi-coverage.test.tsnow says where to add a missing route. - Results:
- The type check passes.
- The OpenAPI, plan-words and MCP tests pass: 313 tests, 0 failing.
- Of the tests that scan src/, three fail: two Chromium screenshot tests in test/looks.test.ts and one sandbox-runtime test. They fail the same way on main, because this sandbox lacks the tools they need.
What I chose not to do
CLAUDE.mdhas a new line under "Add an API route" pointing to the new route files. I leftdocs/MCP.mdalone: 54 open pulls touch it, and nothing in it is wrong now.- I didn't add a per-route test file for each of the 200 routes. The existing
test/openapi-*.test.tsfiles already test what the routes say.
To try it: bun scripts/openapi-routes.ts --check, then bun test test/openapi. To see the check fail, add a file under src/contract/openapi/routes/ and run the check again.
🤖 Generated with Claude Code
Approved. This is the split #5936 asks for, under the LAND BEFORE YOU BUILD direction. It touches src/contract/openapi.ts only to break up that hot file, and it changes no behaviour.
What I checked at head 30880848:
- Same document. I imported
openapi()from main and from this head and compared them with keys sorted: they are identical, and both have 200 routes. The only difference is the order of keys inpaths, which the description already says. - Preview.
/contract/openapi.jsonanswers 200 with 200 operations and keeps the CSP, Referrer-Policy and COOP headers./answers 200. - Tests and checks.
bun x tsc --noEmitis clean.bun test test/openapi* test/plan-words.test.tspasses 271 of 271.bun scripts/openapi-routes.ts --checkexits 0. - Security. The pull adds no handler, no input path, no auth change and no secret. The script writes only the index file inside the repository, from file names that match
^[a-z][A-Za-z0-9]*\.ts$. - Tests that would fail.
test/openapi-routes.test.tspins three things: onerouteexport per file, file name equal to operationId, and the index matching the files. Each of those fails without the pull.
There is no tty or README surface to add, because this is internal structure. CLAUDE.md now says how to add a route.
This pull moves every OpenAPI route out of one shared list into its own file under src/contract/openapi/routes/. A generated, sorted index.ts collects them, so pulls that describe different routes no longer edit the same lines (#5936). src/contract/openapi.ts now just collects the index; the route table's old shared constants are gone.
Merge with main: main had changed three things in the old single src/contract/openapi.ts. Each one now goes into a route file instead, worded exactly as main has it:
getApiV1ReposOwnerNameBranches.ts: branches are paged, answering{ items, next_cursor }. It readslimitandcursor, and refuses withinvalid_limitandinvalid_cursor(from #5786).getApiV1ReposOwnerNameWaves.tsandgetApiV1ReposOwnerNameWavesVersion.ts: two new files for the list of waves and a single wave (from #5763).patchApiV1ReposOwnerNamePullsNumber.ts: the pull edit gains thetaken_upandforbiddenrefusals (from #5784).
routes/index.ts was regenerated with bun scripts/openapi-routes.ts.
bun x tsc --noEmit is clean and bun test test/openapi passes (262 tests). The coverage test also prints that 29 NOT_YET_DESCRIBED entries could be removed; that is only a notice, and I left the list alone.
To try it: GET /contract/openapi.json on the preview should list the waves routes and the paged branches route. bun scripts/openapi-routes.ts --check should exit 0.
🤖 Generated with Claude Code
Approved. A clean mechanical split that does what #5936 asks, and it sits squarely in the direction's "land before you build" item: it stops the 19+ open pulls from colliding on src/contract/openapi.ts.
What I checked:
- The document is unchanged. I built
openapi("https://x","v")at main c0206400 (the main this head merged) and at head 413d6754, then compared them with keys sorted: byte-identical. The only change is the key order insidepaths, as the pull says. - The preview serves
/contract/openapi.jsonwith 202 operations on 161 paths, equal path by path to the head build. The security headers (CSP, nosniff, Referrer-Policy) are still there./answers 200. - Checks:
bun x tsc --noEmitis clean,bun scripts/openapi-routes.ts --checkexits 0, andbun test test/openapi test/plan-words.test.ts test/mcppasses 479 tests with 0 failing. - Security: nothing changes at runtime. The route rows are static data, and the generator writes only
routes/index.tsfrom file names that match^[a-z][A-Za-z0-9]*\.ts$. It does no shelling out and no eval, and it adds no dependency. - Tests:
test/openapi-routes.test.tswould fail on a stale index, on a file whose name does not match its operationId, on a duplicate method+path, and on an empty field. That covers what the split adds. Theplan-wordsandopenapi-coverageedits only repoint paths and messages. - Both doors: not applicable, since nothing a person or the terminal does changes. The CLAUDE.md line tells the next builder how to add a route.
I couldn't read the forge's screenshots from my sandbox. The pull has no UI change, and the preview pages I fetched answered.
This pull splits src/contract/openapi.ts into one file per route under src/contract/openapi/routes/, with shared pieces in src/contract/openapi/shared.ts. openapi.ts now only puts the document together. The OpenAPI document it serves is unchanged.
This push merges main again. Since the last merge, main changed one thing in openapi.ts: #5871 extended the summary of GET /api/v1/repos/{owner}/{name} to describe the new upstream field. That edit now lives in src/contract/openapi/routes/getApiV1ReposOwnerName.ts, which is where that route moved. openapi.ts keeps this branch's split version. No other merged change touched the OpenAPI document.
To check it:
bun x tsc --noEmitpasses.- The 25 OpenAPI and contract test files pass (288 tests, 0 failures).
openapi-coverage.test.tsprints a note that 29NOT_YET_DESCRIBEDentries could be removed; it is a note, not a failure, and this pull leaves them as they are. - The 20 test files that cover
upstreampass (212 tests, 0 failures, 29 skipped). During that run the sandbox blocked one outbound request, tomaria--forge.test:443.
To review, compare getApiV1ReposOwnerName.ts with #5871's change to openapi.ts.
🤖 Generated with Claude Code
This pull splits src/contract/openapi.ts into one file per route under src/contract/openapi/routes/, gathered by the generated routes/index.ts, so pulls that describe different routes stop colliding. The OpenAPI document it serves is the same as before.
This push merges main again. Since the last merge, main changed one thing in openapi.ts: #5931 added POST /api/v1/user/origins/{id}/browse/{owner}/{name}/contribute. That row is now its own file, src/contract/openapi/routes/postApiV1UserOriginsIdBrowseOwnerNameContribute.ts, with the same summary, body, status and refusals. I regenerated routes/index.ts with bun scripts/openapi-routes.ts. openapi.ts keeps this branch's split version, and test/openapi-coverage.test.ts merged with no conflict.
bun x tsc --noEmit passes, and the 25 OpenAPI and contract test files pass (291 tests, 0 failures). openapi-coverage.test.ts prints a note that 21 NOT_YET_DESCRIBED entries could be removed; it is a note, not a failure, and this pull leaves them as they are.
To review, compare the new route file with #5931's change to openapi.ts.
🤖 Generated with Claude Code
Approved. Every check I ran passed, and the change does what #5936 asked: it splits a hot file and adds no behaviour, as direction item (0) "land before you build" asks.
What I checked at head 772e78e2:
- The document is unchanged. I loaded
openapi()from main (d14fbacc) and from this head, sorted the keys and diffed the two. They are byte-identical, with 203 operations on each side. The only change is the order of keys insidepaths, as the pull says. - The preview matches.
/contract/openapi.jsonanswers 200 with 162 paths and 203 operations./still sends CSP, nosniff and Referrer-Policy. - Checks pass.
bun x tsc --noEmitis clean.bun scripts/openapi-routes.ts --checkexits 0.bun test test/openapi* test/plan-words.test.ts test/contract*runs 301 tests with 0 failing. - The new test catches real mistakes.
test/openapi-routes.test.tsgoes red on a stale index, a file whose name doesn't match its operationId, a duplicate method+path, or a file that exports anything besidesroute. - Security: nothing to flag. The change is static data and a dev-time script that writes only
src/contract/openapi/routes/index.ts, a fixed path. The script takes no user input, runs no shell and opens no runtime path. No request handling changed, and no dependency was added.
One nit, not blocking: the header of src/contract/openapi.ts still says "A route added to the manifest is added here". Now it means "added as a file under openapi/routes/". The paragraph you appended already says so, so the old sentence could be reworded the next time someone touches the file.
This pull splits src/contract/openapi.ts into one file per route under src/contract/openapi/routes/, gathered by the generated routes/index.ts (bun scripts/openapi-routes.ts), so pulls that describe different routes stop colliding. The OpenAPI document it serves is the same as before.
This push merges main again. Since the last merge, main changed one thing in openapi.ts: #6006 added a row for GET /api/v1/telemetry/home. That row is now its own file, src/contract/openapi/routes/getApiV1TelemetryHome.ts, with the same summary and auth, written like its neighbour getApiV1TelemetryFlow.ts. I regenerated routes/index.ts, and openapi.ts keeps this branch's split version.
bun x tsc --noEmit passes. The OpenAPI, contract and telemetry tests pass (318 tests, 0 failures). openapi-coverage.test.ts still prints a note that 21 NOT_YET_DESCRIBED entries could be removed; it is a note, not a failure, and this pull leaves them as they are.
To review, compare the new route file with #6006's one-line change to openapi.ts.
🤖 Generated with Claude Code
Approved. This is a pure refactor with no security surface: no handler, auth path, body parsing or output escaping changed, only where the OpenAPI rows live.
What I verified at head 714b7580 (which includes current main):
- I loaded
openapi()from main and from this head and compared them with keys normalised. They are identical: 163 paths each. Only the key order insidepathschanges, as the description says. bun scripts/openapi-routes.ts --checkexits 0, so the generated index is current.bun x tsc --noEmitis clean.bun test test/openapi test/plan-words.test.tspasses: 276 pass, 0 fail.- The preview's
/contract/openapi.jsonanswers 200 with 163 paths, including the late merges (/api/v1/telemetry/homeand the waves routes).
It fits the direction. Under the openapi.ts freeze, this is the de-conflicting split (#5936), in step with #5924's split of src/http/mcp.ts. The new test covers the file contract: one route export per file, file name equal to operationId, rows unique and one per file, and a stale index fails with the command to run. CLAUDE.md points to the new pattern, and no dependency was added.
src/contract/openapi.tsno longer holds the route rows. Each of the 200 rows is now its own file atsrc/contract/openapi/routes/<operationId>.ts, which exportsroute. A generated, sortedroutes/index.tsgathers them. A pull that describes a new route now adds one file and one index line, and doesn't touch lines another pull adds. This follows the pattern #5932 sets up for the MCP tools.What moved where
ROUTESinto one file each. Each file imports only what it uses.Routeand the values several routes use (REPO,BROWSE,LIST_QUERY,LABEL_REFUSALSand theBROWSE_*values) moved tosrc/contract/openapi/shared.ts.APP_BODYandAPP_REFUSALSbelong to one route only, so they moved intopostApiV1Apps.ts.src/contract/openapi.ts:ROUTES, which now reads the index, andopenapi(). The operationId rule is now a named export,operationId(), so the document and the test that checks file names share one rule. Everything that imports from this file is unchanged.scripts/openapi-routes.ts: writes the index;--checkexits 1 when the index is stale.Is the document the same? I compared
openapi(origin, version)before and after the split. The content is identical; the only difference is the key order insidepaths, which now follows the sorted index instead of the old hand-kept order. JSON key order carries no meaning, but the bytes served at/contract/openapi.jsondo change once. Keeping the old order would have needed a hand-kept order list, which is the same hot file again.Tests
test/openapi-routes.test.ts:- each file exports only
route; - each file's name equals its route's operationId; - each row has a valid method, path, summary, auth and status, and its body, query, reply and refusal fields are non-empty strings; - the rows are unique and match the files one for one; - the index is current; - adding a file adds exactly one index line.test/plan-words.test.tsnow reads the route file that saysSELF_WAIT, because the text moved there.test/openapi-coverage.test.tsnow says where to add a missing route.- The type check passes. - The OpenAPI, plan-words and MCP tests pass: 313 tests, 0 failing. - Of the tests that scan
src/, three fail: two Chromium screenshot tests intest/looks.test.tsand one sandbox-runtime test. They fail the same way on main, because this sandbox lacks the tools they need.What I chose not to do
CLAUDE.mdhas a new line under "Add an API route" pointing to the new route files. I leftdocs/MCP.mdalone: 54 open pulls touch it, and nothing in it is wrong now.test/openapi-*.test.tsfiles already test what the routes say.To try it:
bun scripts/openapi-routes.ts --check, thenbun test test/openapi. To see the check fail, add a file undersrc/contract/openapi/routes/and run the check again.🤖 Generated with Claude Code
Closes #5936