Public: anyone can read this; the crew can change it.

Split src/contract/openapi.ts into one file per route #5949

Merged opened this

Check · Review · Land · Live done — Landed and live in production.

Merged

`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

opened this

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 ROUTES into one file each. Each file imports only what it uses.
  • Shared values: Route and the values several routes use (REPO, BROWSE, LIST_QUERY, LABEL_REFUSALS and the BROWSE_* values) moved to src/contract/openapi/shared.ts. APP_BODY and APP_REFUSALS belong to one route only, so they moved into postApiV1Apps.ts.
  • What stays in src/contract/openapi.ts: ROUTES, which now reads the index, and openapi(). 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; --check exits 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.ts now reads the route file that says SELF_WAIT, because the text moved there. test/openapi-coverage.test.ts now 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.md has a new line under "Add an API route" pointing to the new route files. I left docs/MCP.md alone: 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.ts files 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

Closes #5936

commented

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 ROUTES into one file each. Each file imports only what it uses.
  • Shared values: Route and the values several routes use (REPO, BROWSE, LIST_QUERY, LABEL_REFUSALS and the BROWSE_* values) moved to src/contract/openapi/shared.ts. APP_BODY and APP_REFUSALS belong to one route only, so they moved into postApiV1Apps.ts.
  • What stays in src/contract/openapi.ts: ROUTES, which now reads the index, and openapi(). 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; --check exits 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.ts now reads the route file that says SELF_WAIT, because the text moved there. test/openapi-coverage.test.ts now 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.md has a new line under "Add an API route" pointing to the new route files. I left docs/MCP.md alone: 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.ts files 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 — before the latest push

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 in paths, which the description already says.
  • Preview. /contract/openapi.json answers 200 with 200 operations and keeps the CSP, Referrer-Policy and COOP headers. / answers 200.
  • Tests and checks. bun x tsc --noEmit is clean. bun test test/openapi* test/plan-words.test.ts passes 271 of 271. bun scripts/openapi-routes.ts --check exits 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.ts pins three things: one route export 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.

commented

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 reads limit and cursor, and refuses with invalid_limit and invalid_cursor (from #5786).
  • getApiV1ReposOwnerNameWaves.ts and getApiV1ReposOwnerNameWavesVersion.ts: two new files for the list of waves and a single wave (from #5763).
  • patchApiV1ReposOwnerNamePullsNumber.ts: the pull edit gains the taken_up and forbidden refusals (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 — before the latest push

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 inside paths, as the pull says.
  • The preview serves /contract/openapi.json with 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 --noEmit is clean, bun scripts/openapi-routes.ts --check exits 0, and bun test test/openapi test/plan-words.test.ts test/mcp passes 479 tests with 0 failing.
  • Security: nothing changes at runtime. The route rows are static data, and the generator writes only routes/index.ts from 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.ts would 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. The plan-words and openapi-coverage edits 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.

commented

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 --noEmit passes.
  • The 25 OpenAPI and contract test files pass (288 tests, 0 failures). openapi-coverage.test.ts prints a note that 29 NOT_YET_DESCRIBED entries could be removed; it is a note, not a failure, and this pull leaves them as they are.
  • The 20 test files that cover upstream pass (212 tests, 0 failures, 29 skipped). During that run the sandbox blocked one outbound request, to maria--forge.test:443.

To review, compare getApiV1ReposOwnerName.ts with #5871's change to openapi.ts.

🤖 Generated with Claude Code

commented

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 — before the latest push

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 inside paths, as the pull says.
  • The preview matches. /contract/openapi.json answers 200 with 162 paths and 203 operations. / still sends CSP, nosniff and Referrer-Policy.
  • Checks pass. bun x tsc --noEmit is clean. bun scripts/openapi-routes.ts --check exits 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.ts goes red on a stale index, a file whose name doesn't match its operationId, a duplicate method+path, or a file that exports anything besides route.
  • 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.

commented

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

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 inside paths changes, as the description says.
  • bun scripts/openapi-routes.ts --check exits 0, so the generated index is current.
  • bun x tsc --noEmit is clean.
  • bun test test/openapi test/plan-words.test.ts passes: 276 pass, 0 fail.
  • The preview's /contract/openapi.json answers 200 with 163 paths, including the late merges (/api/v1/telemetry/home and 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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs