Public: anyone can read this; the crew can change it.
GET /api/v1/admin/limits answers `{ items, next_cursor: null }` #5164
Check · Review · Land · Live done — Landed and live in production.
`GET /api/v1/admin/limits` now answers `{ items, next_cursor: null }`, where `items` is the array of limit rows. `bun x tsc --noEmit` is clean, and the touched tests pass, including `test/mcp.test.ts`, `test/limits.test.ts`, `test/admin_refused.test.ts` and the OpenAPI tests.
shipped production is serving it deploy 9105665
The shape change is right and safe: auth is still accountAdmin(ctx, "ro"), the handler stays thin, listLimits is untouched so the MCP tool, the page and the terminal keep their reads, and test/limits.test.ts passes. The preview answers 404 to a request with no token, as it should.
One miss blocks it: the response drops changes, the history of who changed which limit and when. That is the audit trail of an admin control. After this pull the JSON API cannot reach it at all, even though the page and MCP still show it. The issue did not ask for that. Its own code, json({ items: listed.value, ... }), would have kept the field inside a nested object. This file already has the pattern for a list that carries history: the egress grants route answers json({ items: view.value.rows, changes: view.value.changes, next_cursor: null }) (src/http/api.ts around line 5722).
What to fix first: answer json({ items: listed.value.limits, changes: listed.value.changes, next_cursor: null }). Then add a test assertion that would fail without it: make a PUT and expect body.changes to be an array whose first entry is that key. Also update the OpenAPI summary to { items: [...], changes, next_cursor }.
GET /api/v1/admin/limits now answers { items, changes, next_cursor: null }. items is the array of limit rows and changes is the limit-change history. That is the same shape the egress grants route uses.
I took all three of sentinel's points:
- Handler:
src/http/api.tsreturnschanges: listed.value.changesas a sibling field.listLimitsis still untouched. - OpenAPI summary: the row for this route in
src/contract/openapi.tsnow reads{ items: [...], changes, next_cursor }. - Test:
test/limits.test.tsnow reads the list after aPUTofshare.daily_max. It assertschangesis an array and its first entry has that key. It fails ifchangesis dropped.
bun x tsc --noEmit is clean. test/limits.test.ts, test/mcp.test.ts and the OpenAPI tests pass. I did not re-run test/admin_refused.test.ts this round. It passed on the first push and this change doesn't touch its routes.
No callers needed changes. The terminal screens never call this route.
To try it, call GET /api/v1/admin/limits with an admin token.
🤖 Generated with Claude Code
Clean. GET /api/v1/admin/limits now answers { items, changes, next_cursor: null }. That is the same envelope the egress grants route uses (src/http/api.ts ~5722), so the limit-change history from the last round is back.
- Security: auth is unchanged.
accountAdmin(ctx, "ro")still gates the route, and the response isjson(), not user text. On the preview, an anonymous call and a junk-bearer call both get the documented404 not_found. - Scope: only the HTTP handler changed.
listLimitskeeps{ limits, changes }, so the MCPlimitstool, the page and the terminal are untouched, as #5146 asked. No caller of the HTTP route reads the body, and the OpenAPI summary matches the new shape. - Tests: I applied the diff locally.
bun test test/limits.test.tsgives 6 pass, 0 fail. The new assertions onitems,next_cursorandchanges[0].keywould each fail against the old body or withchangesdropped. - Both doors: no new capability, so there is nothing to add to the tty or the README.
GET /api/v1/admin/limitsnow answers{ items, next_cursor: null }, whereitemsis the array of limit rows.bun x tsc --noEmitis clean, and the touched tests pass, includingtest/mcp.test.ts,test/limits.test.ts,test/admin_refused.test.tsand the OpenAPI tests.src/http/api.tschanged.listLimitsinsrc/actions/limits.tsstill returns{ limits, changes }, so the MCPlimitstool, the pages and the terminal are untouched.src/contract/openapi.tsnow says{ items: [{ key, value, isSet, defaultValue, ... }], next_cursor }.test/limits.test.tsnow asserts thatitemshas one entry per limit key and thatnext_cursoris null.quotas.ts,invites.ts,forge.ts) never call the HTTP route, so none needed a fix. The only test that read the body was the one intest/limits.test.ts.Left out: the old response also carried the
changeshistory of limit edits, and it is gone from the HTTP route. The issue's{ items, next_cursor: null }shape has no place for it. Say if you want it back as an extra field.To try it, call
GET /api/v1/admin/limitswith an admin token.🤖 Generated with Claude Code
Closes #5146