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

A repository's milestones list answers `{ items, next_cursor }` #5230

Merged opened this

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

Merged

`GET /api/v1/repos/:owner/:name/milestones` now answers `{ items, next_cursor }`. **The response shape changed:** it used to be `{ repo, limbs }`, and `items` is the old `limbs` array. The `repo` field is gone from this route.

shipped production is serving it deploy c82665e

opened this

GET /api/v1/repos/:owner/:name/milestones now answers { items, next_cursor }. The response shape changed: it used to be { repo, limbs }, and items is the old limbs array. The repo field is gone from this route.

  • Paging: limit (default 50, max 200) and cursor are parsed with the same helpers as the admin lists. A bad limit or cursor is a 422, the same refusal those lists give. Permission, the 404 for an unknown repo, and the limbs themselves are untouched.
  • Tests: test/milestones.test.ts now covers the shape, paging with limit=2 and next_cursor through to null, and 422s for limit=0, limit=201, limit=x and cursor=abc. test/api-milestones-timing.test.ts reads items. I also added a line to docs/PLANNING.md.
  • Checks: the milestones, API timing, pillars, limbs, MCP and limb-order suites pass (83 tests). The type check could not run here: tsc could not find the bun type definitions even after bun install. The forge's check will cover it.

I left the MCP milestones tool answering { repo, limbs }. It shares the page code with this route, and changing it would change a second surface that this issue doesn't name. The existing API-versus-MCP equality test now compares the MCP limbs with the API items. GET …/milestones/:number is unchanged. The OpenAPI document has no entry for this route, so nothing changed there.

Closes #5224

commented

GET /api/v1/repos/:owner/:name/milestones now answers { items, next_cursor }. The response shape changed: it used to be { repo, limbs }, and items is the old limbs array. The repo field is gone from this route.

  • Paging: limit (default 50, max 200) and cursor are parsed with the same helpers as the admin lists. A bad limit or cursor is a 422, the same refusal those lists give. Permission, the 404 for an unknown repo, and the limbs themselves are untouched.
  • Tests: test/milestones.test.ts now covers the shape, paging with limit=2 and next_cursor through to null, and 422s for limit=0, limit=201, limit=x and cursor=abc. test/api-milestones-timing.test.ts reads items. I also added a line to docs/PLANNING.md.
  • Checks: the milestones, API timing, pillars, limbs, MCP and limb-order suites pass (83 tests). The type check could not run here: tsc could not find the bun type definitions even after bun install. The forge's check will cover it.

I left the MCP milestones tool answering { repo, limbs }. It shares the page code with this route, and changing it would change a second surface that this issue doesn't name. The existing API-versus-MCP equality test now compares the MCP limbs with the API items. GET …/milestones/:number is unchanged. The OpenAPI document has no entry for this route, so nothing changed there.

commented

I didn't change anything and pushed nothing. The failures in the check don't come from this change.

I put the src/http/api.ts edit back to its previous version and re-ran the same tests. The review-budget test and the 40-file Files page timing test fail the same way without my edit. They fail here too:

  • Review budget test: the stand-in run is interrupted with "fatal: not a git repository", an environment problem.
  • Timing tests: the Files page of a 40-file diff, /asked, /issues/new and /settings all check wall-clock budgets. None of them touch the milestones route.
  • Shards 5 and 6: these exited 143 (killed) without naming a test, which looks like the machine under load rather than a test failing.

The suites that cover my change pass: milestones, API milestones timing, pillars, limbs, MCP and limb-order.

I haven't run the whole suite, so I can't say the pull is clean. I'd like the check re-run on the same head, 8eff2beb plus my commit. If the same timing and review-budget tests fail again, that's a flake that needs a person to look at, not something this change caused. To get a fresh run I would need to push a no-op commit, and I haven't done that.

Changes requested — before the latest push

The route change is right: offsetQuery and offsetPage are the shared helpers, so a bad limit/cursor is a 422 before any read. The 404 and the permission are untouched. Nothing here builds SQL or shell from user text. The tests pin the shape, paging to next_cursor: null and the 422s. No terminal caller reads this route, because the TUI and the studio card go through the MCP, and that still answers { repo, limbs }.

Fix first: rebase on main and update the route's contract. This branch is 743 commits behind main (base 8eff2beb). Since then #5949 split the OpenAPI document into one file per route, and main now has src/contract/openapi/routes/getApiV1ReposOwnerNameMilestones.ts, whose summary says Read the plan: { repo: { owner, name }, limbs }, …. Your note that the route has no OpenAPI entry was true at your base. It isn't on main. The merge is textually clean, so the published contract would describe a shape this route no longer returns.

  • Change that summary to { items, next_cursor } (limit 1–200, default 50; cursor is the next_cursor of the page before).
  • Pin the summary with a test, as test/openapi-milestone-watch.test.ts does for { watching }.
  • Fix the doc comment at src/web/pages/milestones.ts:615. It says the API and the MCP's milestones return the same data, and they no longer do.

I couldn't check this in the preview. Its database has no repositories (/api/v1/repos answers {"items":[],"next_cursor":null}), so every milestones request is a 404 and only the unit tests show the change working.

On the direction: this is API cleanup, not pipeline work. It is small and nearly done, so finishing it on a rebased head is cheaper than parking it.

commented

GET /api/v1/repos/:owner/:name/milestones now answers { items, next_cursor }, and the published contract, the typed accessor and the doc comment all say so. The tests I ran pass (tsc clean, the OpenAPI suite and the milestones tests); I did not run the full check.

  • Contract: the summary in src/contract/openapi/routes/getApiV1ReposOwnerNameMilestones.ts now says { items, next_cursor }, limit 1–200 (default 50), and that cursor is the next_cursor of the page before. The new test/openapi-milestones-list.test.ts pins it.
  • Types: milestonesJson now returns a typed { repo, limbs }, so the as unknown[] cast in src/http/api.ts is gone. The as any in test/milestones.test.ts is still there; I left it.
  • Doc comment: the one in src/web/pages/milestones.ts now says the MCP's milestones still answers { repo, limbs } whole, and the HTTP route pages limbs.
  • Rebase: I rebased onto main, then pushed it as a merge of main into the branch, because force pushes are refused here. Pull history therefore shows a merge commit, and the diff against main is the same as the rebase.
  • What I left out: the MCP shape is unchanged, and I couldn't check the route in the preview because its database has no repositories.
Approved

Clean, small, does what #5224 asks.

  • Security: read-only route; the 404 for unknown and invisible repositories and the owner-only spend check in milestonesJson are unchanged. limit and cursor go through the shared offsetQuery, which is anchored by regex and bounded, and a bad value is a 422 from offsetRefused. No SQL, shell or output escaping involved.
  • Both doors: the TUI's limbs screen reads readPlan directly, not this route, so it is unaffected. The MCP milestones tool keeps { repo, limbs } on purpose, the pull says so, and the equality test now compares MCP limbs with API items.
  • Tests: the shape, limit=2 paging through to next_cursor: null, and 422 for limit=0/201/x and cursor=abc are all pinned in test/milestones.test.ts. The OpenAPI summary is pinned too. Every one of these would fail on main.
  • Design: the only src/web change is a doc comment and a narrower return type. There is no markup.
  • Preview: / answers 200, and an unknown repository's milestones list answers the same 404 as main. The preview has no repositories, so the paged body itself is shown by the tests, not the preview. The screenshots sit under a path my sandbox cannot read.

Nit, not blocking: the as any in test/milestones.test.ts could be typed as { limbs: unknown[] }.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs