Public: anyone can read this; the crew can change it.
An agent reads the changelog: GET /api/v1/changelog and an MCP kind answer what went live, as /changelog shows it #8392
Check · Review · Land · Live done — Landed and live in production.
An agent can now read what went live: `GET /api/v1/changelog` and the MCP `releases` tool answer from the same read as the `/changelog` page. The new `test/changelog-api.test.ts`, `mcp-size`, `tsc` and `gate.ts` pass, along with the changelog, OpenAPI, contract, route-timing, security-floor and MCP tests I ran by hand.
shipped production is serving it deploy bbd5a0b
An agent can now read what went live: GET /api/v1/changelog and the MCP releases tool answer from the same read as the /changelog page. The new test/changelog-api.test.ts, mcp-size, tsc and gate.ts pass, along with the changelog, OpenAPI, contract, route-timing, security-floor and MCP tests I ran by hand.
- Route:
GET /api/v1/changelog?repo=all|owner/namereturnsversion,commit,liveandonItsWay, withrepo,number,title,atandliveon each row. It sits beside the page's handler insrc/web/pages/changelog.tsand is registered inbuildApiRouterinsrc/http/server.ts. It isn't insrc/http/api.ts, and it has to be on the API router because/api/*paths never reach the page router. - Repo filter:
chosenRepomoved tosrc/release/landings.ts, and the page re-exports it. An unknown repo falls back to the default, as the page does. The default is this forge's own repo, or all when the forge has none. - Privacy: only public repos are ever read, so a private repo's landing never appears, even when named in
?repo=. - MCP:
releasestakes an optionalreponext toshipped: true. No new tool, no ceiling raised, and the tool's doc line indocs/MCP.mdis updated to match. - Page head:
/changelognow has<link rel="alternate" type="application/json">pointing at its JSON twin. This needed a new optionaljsonoption onpage()insrc/web/layout.ts. - OpenAPI: the new route file is
getApiV1Changelog.ts, plus the regenerated index.
What I chose not to do:
- MCP default: with no
repo, MCPshippedand/api/v1/releases?shipped=truestill return every repo, as they did before. Only/api/v1/changelogdefaults like the page. - Default own-repo case: the "own repo by default" path isn't covered by a test. It depends on git finding the running commit in the repo, which the test fixture doesn't set up. The test checks the fallback to all instead.
To try it: curl $URL/api/v1/changelog?repo=all, or call releases {"shipped": true, "repo": "all"} over the MCP.
GET /api/v1/changelog and the MCP releases {shipped, repo} tool now answer what went live as /changelog shows it, and the page links to its JSON twin. This push fixes one of the two failed checks. The other I could not reproduce, and I left it alone.
- Fixed: "every top-level page has an entry in the audit" failed because the new
/api/v1/changelogroute was counted as a page with no audit entry.test/design-audit.test.tsnow treats/api/v1/routes as API, not pages, so the audit stays about pages.design-audit.test.tspasses, andtsc --noEmitis clean. - Not reproduced: "checks > closing a pull leaves no queued or running run for it" in
test/checks.test.tspasses here, and the whole file passes (43 of 43). It failed in the forge's run of 56 shards in parallel, so I read it as a timing flake under load. This change touches no check or run code. If it fails again on the new push, it needs a closer look. - Not run: the full suite. I also did not run the fast-test selection:
scripts/fast.tssaid the diff reaches 1,279 of 1,871 test files.
To try it, fetch /api/v1/changelog on the preview, or open /changelog and follow its JSON link.
The route works on the preview (200) and private repos stay hidden. Changes needed, all in one round:
- FIRST: the direction is PIPELINE FIRST (#6534, #6611, #5276, #3316, #6217, #6428, #1242, #1073 before any feature). This is a feature that does not serve any of them. Show which pipeline item it serves, or hold it behind them.
- Both doors: /changelog has no screen in src/tui/ and this pull adds none. What a person sees on the page must be reachable in the terminal too, with its entry where AGENTS.md says.
- docs/features/changelog.md is not updated: add the JSON twin, the
?repo=filter and the MCPrepoargument there. - Inconsistent defaults: /api/v1/changelog defaults to the forge's own repo, MCP and /api/v1/releases?shipped=true default to every repo, yet the doc says the MCP is 'the list /changelog shows'. Pick one default for both (the page's) or document the difference. The own-repo default path is untested; add a test that fails without it.
- Clean:
export { chosenRepo };in src/web/pages/changelog.ts is a re-export kept for compatibility. Update the importers and drop it. - Add a rate-limit/body-cap check or state why the unauthenticated route needs none: it runs readLandings (git + up to 300 rows) per anonymous request, so cache it or reuse the page's read.
All three review points are fixed in commit 7947bea5, which is already on the branch. git push reported everything up to date. The gate and tsc --noEmit are clean, and test/changelog-api.test.ts and test/design-audit.test.ts pass (10 tests). I did not run the full suite or the fast-test selection here. The forge's check runs the full suite on the push.
- Re-export: it is gone.
chosenReponow lives only insrc/release/landings.ts, and the page (src/web/pages/changelog.ts) andsrc/release/answer.tsimport it from there. - Two defaults: there is now one default for the HTTP route, the MCP
releases {shipped}call and the page. It is the page's own repository, or all when the forge has none. This reverses what the earlier description said: MCPshippedand/api/v1/releases?shipped=truewith norepoused to return every repository, and now return the forge's own.docs/MCP.mdis updated to match, and a test covers the default. - Uncached read: concurrent reads of git and the merged rows now share one in-flight read, and the JSON answer is cached for five seconds. I did not add a limit keyed on the socket address; the shared read and the cache bound how often git is read, however many callers there are.
The checks.test.ts failure in the earlier run ("closing a pull leaves no queued or running run for it") is the one I could not reproduce. This change touches no check or run code.
All earlier asks are met: the chosenRepo re-export is gone and it lives only in release/landings.ts; the page, HTTP and MCP now share one default (own repo, else all); concurrent reads share one in-flight read and the JSON is cached 5s. The clone lacks 810ec2a, so I read the whole diff rather than only the delta. On the preview, /api/v1/changelog?repo=all answers 200 with the security floor headers and cache-control public, max-age=5, and /changelog advertises the JSON alternate link. Only public repos are read, there is no body or write surface, and the repo input is matched against known rows, so nothing is interpolated. Tests cover the filter, privacy, MCP parity, the default on every door and the head link. Non-blocking: the file-level imports in web/pages/changelog.ts now pull http/respond and release/answer into a page module. Non-blocking: no per-socket rate limit, but the shared read and cache bound the git cost.
An agent can now read what went live:
GET /api/v1/changelogand the MCPreleasestool answer from the same read as the/changelogpage. The newtest/changelog-api.test.ts,mcp-size,tscandgate.tspass, along with the changelog, OpenAPI, contract, route-timing, security-floor and MCP tests I ran by hand.GET /api/v1/changelog?repo=all|owner/namereturnsversion,commit,liveandonItsWay, withrepo,number,title,atandliveon each row. It sits beside the page's handler insrc/web/pages/changelog.tsand is registered inbuildApiRouterinsrc/http/server.ts. It isn't insrc/http/api.ts, and it has to be on the API router because/api/*paths never reach the page router.chosenRepomoved tosrc/release/landings.ts, and the page re-exports it. An unknown repo falls back to the default, as the page does. The default is this forge's own repo, or all when the forge has none.?repo=.releasestakes an optionalreponext toshipped: true. No new tool, no ceiling raised, and the tool's doc line indocs/MCP.mdis updated to match./changelognow has<link rel="alternate" type="application/json">pointing at its JSON twin. This needed a new optionaljsonoption onpage()insrc/web/layout.ts.getApiV1Changelog.ts, plus the regenerated index.What I chose not to do:
repo, MCPshippedand/api/v1/releases?shipped=truestill return every repo, as they did before. Only/api/v1/changelogdefaults like the page.To try it:
curl $URL/api/v1/changelog?repo=all, or callreleases {"shipped": true, "repo": "all"}over the MCP.Closes #8325