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

GET /api/v1/releases answers the kept releases, and ?shipped=true what went live, as MCP `releases` does #8026

Merged opened this

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

Merged

`GET /api/v1/releases` now answers over HTTP what MCP `releases` answers. I ran `bun x tsc --noEmit` and a hand-picked set of 38 OpenAPI, API and MCP test files, and all pass, including the new `test/api-releases.test.ts` and `test/mcp-size.test.ts`. `bun scripts/gate.ts` printed nothing that looked like a failure, but I only grepped its output for "fail", "red" and "error"; I didn't check its exi…

shipped production is serving it deploy 119f4d2

opened this

GET /api/v1/releases now answers over HTTP what MCP releases answers. I ran bun x tsc --noEmit and a hand-picked set of 38 OpenAPI, API and MCP test files, and all pass, including the new test/api-releases.test.ts and test/mcp-size.test.ts. bun scripts/gate.ts printed nothing that looked like a failure, but I only grepped its output for "fail", "red" and "error"; I didn't check its exit status. scripts/fast.ts named 1144 test files for this diff, so I didn't run them. The forge's check runs the full suite on this push.

What changed

  • src/release/answer.ts (new): it holds shippedAnswer(app) and releasesAnswer(app, user), moved out of the MCP tool with the same row shape and the same refusal text.
  • src/http/mcp/tools/releases.ts: the tool now only calls those two functions and answers as before.
  • src/http/api/releases.ts (new): it registers GET /api/v1/releases.

- ?shipped=true answers for anyone, with no token. - Without shipped, the list goes to whoever mayRead allows; everyone else, including anonymous callers, gets 403 forbidden with the release-history message. - Any other shipped value, including false and an empty one, is 422 invalid_shipped.

  • src/http/api.ts: the import sits directly after the forgeRoutes import, and the call directly after forgeRoutes(router);.
  • OpenAPI: getApiV1Releases.ts describes the route with auth: false, the shipped query and both refusals. It is exported from routes/index.ts before getApiV1Repos.
  • test/api-releases.test.ts (new): it covers the five cases below, each of which fails without the route.

- ?shipped=true returns 200 with no token. - An admin gets the list with the live row marked. - A stranger and an anonymous caller get 403. - maybe, false and an empty value get 422. - The route is in the OpenAPI document.

Not done: there is no rollback over this route. It stays a person's act on the page.

To try it: curl $FORGE_URL/api/v1/releases?shipped=true needs no token. curl -H "authorization: Bearer …" $FORGE_URL/api/v1/releases lists the kept releases for an administrator.

Closes #8020

commented

GET /api/v1/releases now answers over HTTP what MCP releases answers. I ran bun x tsc --noEmit and a hand-picked set of 38 OpenAPI, API and MCP test files, and all pass, including the new test/api-releases.test.ts and test/mcp-size.test.ts. bun scripts/gate.ts printed nothing that looked like a failure, but I only grepped its output for "fail", "red" and "error"; I didn't check its exit status. scripts/fast.ts named 1144 test files for this diff, so I didn't run them. The forge's check runs the full suite on this push.

What changed

  • src/release/answer.ts (new): it holds shippedAnswer(app) and releasesAnswer(app, user), moved out of the MCP tool with the same row shape and the same refusal text.
  • src/http/mcp/tools/releases.ts: the tool now only calls those two functions and answers as before.
  • src/http/api/releases.ts (new): it registers GET /api/v1/releases.

- ?shipped=true answers for anyone, with no token. - Without shipped, the list goes to whoever mayRead allows; everyone else, including anonymous callers, gets 403 forbidden with the release-history message. - Any other shipped value, including false and an empty one, is 422 invalid_shipped.

  • src/http/api.ts: the import sits directly after the forgeRoutes import, and the call directly after forgeRoutes(router);.
  • OpenAPI: getApiV1Releases.ts describes the route with auth: false, the shipped query and both refusals. It is exported from routes/index.ts before getApiV1Repos.
  • test/api-releases.test.ts (new): it covers the five cases below, each of which fails without the route.

- ?shipped=true returns 200 with no token. - An admin gets the list with the live row marked. - A stranger and an anonymous caller get 403. - maybe, false and an empty value get 422. - The route is in the OpenAPI document.

Not done: there is no rollback over this route. It stays a person's act on the page.

To try it: curl $FORGE_URL/api/v1/releases?shipped=true needs no token. curl -H "authorization: Bearer …" $FORGE_URL/api/v1/releases lists the kept releases for an administrator.

commented

GET /api/v1/releases now reports a session phase in the route timing, which fixes the "no GET route answers 200 without a session phase" failure. The fix is in src/http/api/releases.ts, and it follows the other API routes. I pushed it.

test/route-timing-coverage.test.ts and test/api-releases.test.ts pass here (7 tests), and bun x tsc --noEmit is clean. I did not run bun scripts/gate.ts or the rest of the suite.

I did not touch the second failure, "live previews > a fresh account's second preview is refused…". That test ran 121 seconds before failing, which looks like a timeout rather than anything this change affects. I did not run it here. If it fails again on this push, it needs a separate look.

This pull makes GET /api/v1/releases answer the kept releases, and ?shipped=true what went live. The MCP releases tool now shares those answers.

Approved

Clean. The shared answer module keeps MCP and HTTP identical. The gate is the page's own mayRead, taken from the verified session or token and not from anything the client sends. shipped is validated strictly (422), and the 403 text matches MCP. The new tests fail without the route, and the OpenAPI entry is present. In the preview, ?shipped=true answers with no token, and the list is refused with 403 in words for a caller who may not read it. There is no new dependency, no secret and no write path.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs