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

Next up lists every open issue in exactly the order the dispatcher will start them, from the page, the API and the next_up tool alike #11627

Merged opened this

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

Merged

Next up now lists every open, non-deferred issue in the order the dispatcher starts them, and that order is served as data at `GET /api/v1/repos/{owner}/{name}/queue`. The new queue test, the next-up, MCP and terminal-screen suites, `test/mcp-size.test.ts`, `scripts/gate.ts` and `tsc` all pass here. I did not run the full suite. I did not call `keep_plan`; it wasn't among my tools.

shipped production is serving it deploy e1bbdcb

opened this

Next up now lists every open, non-deferred issue in the order the dispatcher starts them, and that order is served as data at GET /api/v1/repos/{owner}/{name}/queue. The new queue test, the next-up, MCP and terminal-screen suites, test/mcp-size.test.ts, scripts/gate.ts and tsc all pass here. I did not run the full suite. I did not call keep_plan; it wasn't among my tools.

What changed

  • nextUp rows: each row has a one-word state: ready, blocked (waits on open work) or held (anything else that passes it over, with its reason in why).
  • Row order: the dispatcher's order (what the dispatcher picks from, ranked first, then ready order) comes first. Ranked blocked or held rows sit at their rank. Unranked rows that cannot start now follow, by number, instead of being dropped.
  • /queue endpoint: it returns {queue: [{position, number, title, onIt, state, why}], notNow}, from the same function as the page, under the same visibility rules as the page.
  • next_up tool: it gains state on each row and no new tool. all: true lists the full order.
  • Tests: test/queue.test.ts builds a graph of ranked, blocked and unranked-blocked issues. It proves the ready rows match the dispatcher's inSlotOrder exactly, and that blocked rows keep their rank or fall last. It also checks that /queue matches the same rows.
  • Existing test: test/next-up.test.ts expected the list to equal the ready order. It now expects the unranked blocked issue to sit last, because the list now includes it.

What I left out

  • Plan-structural issues: they stay off the list. The dispatcher never starts them and nobody can rank them, so listing them would break rank moves on the page and in the terminal screen.
  • Page markup: untouched. The Next up page, however, now shows the extra unranked blocked rows as data.

To try it: open GET /api/v1/repos/<owner>/<name>/queue, or call next_up with all: true. A Try: commit says the same, because the pre-push hook required one.

Closes #11618

commented

Next up now lists every open, non-deferred issue in the order the dispatcher starts them, and that order is served as data at GET /api/v1/repos/{owner}/{name}/queue. The new queue test, the next-up, MCP and terminal-screen suites, test/mcp-size.test.ts, scripts/gate.ts and tsc all pass here. I did not run the full suite. I did not call keep_plan; it wasn't among my tools.

What changed

  • nextUp rows: each row has a one-word state: ready, blocked (waits on open work) or held (anything else that passes it over, with its reason in why).
  • Row order: the dispatcher's order (what the dispatcher picks from, ranked first, then ready order) comes first. Ranked blocked or held rows sit at their rank. Unranked rows that cannot start now follow, by number, instead of being dropped.
  • /queue endpoint: it returns {queue: [{position, number, title, onIt, state, why}], notNow}, from the same function as the page, under the same visibility rules as the page.
  • next_up tool: it gains state on each row and no new tool. all: true lists the full order.
  • Tests: test/queue.test.ts builds a graph of ranked, blocked and unranked-blocked issues. It proves the ready rows match the dispatcher's inSlotOrder exactly, and that blocked rows keep their rank or fall last. It also checks that /queue matches the same rows.
  • Existing test: test/next-up.test.ts expected the list to equal the ready order. It now expects the unranked blocked issue to sit last, because the list now includes it.

What I left out

  • Plan-structural issues: they stay off the list. The dispatcher never starts them and nobody can rank them, so listing them would break rank moves on the page and in the terminal screen.
  • Page markup: untouched. The Next up page, however, now shows the extra unranked blocked rows as data.

To try it: open GET /api/v1/repos/<owner>/<name>/queue, or call next_up with all: true. A Try: commit says the same, because the pre-push hook required one.

commented

Next up now lists every open issue in the dispatcher's order, served at GET /api/v1/repos/<owner>/<name>/queue and by the next_up tool with all: true. This push fixes the five checks that failed on the first one. The affected tests, the route and OpenAPI tests and tsc --noEmit pass locally; I did not run the full suite, and the scripts/gate.ts run printed nothing, so I can't say it passed.

  • OpenAPI: the /queue route had no OpenAPI row. I added getApiV1ReposOwnerNameQueue.ts and regenerated the route index. This clears both OpenAPI coverage failures.
  • Server-Timing: the route answered 200 without a session phase. It now notes that phase, like its sibling reads.
  • /next budget: the page budget test went from 64 to 65 SQL statements. The queue now lists every open issue, so the fixture's previously unlisted issue becomes a row. I raised the /next budget by one in test/pages-budget.test.ts, with the reason noted beside it. I did not trace the extra statement to a specific query.
  • Hand-over test: test/flow-handover-page.test.ts was failing before my change. It pinned its clock to 2026-11-11, but the page reads the real clock, so its rows aged out of the 24-hour window. The test was wrong, so it now uses Date.now().

I did not add pagination to /queue.

commented

I merged main into the branch and pushed; the pull should merge cleanly again. The type check and the affected tests (queue, next-up, OpenAPI coverage and routes, route timing, page budgets, hand-over page) pass locally. I did not run the full suite.

Only one file conflicted: test/flow-handover-page.test.ts. main had made the same fix I made earlier, switching the test clock to Date.now(). I kept main's version, so nothing of mine remains in that file.

The rest of my change is unchanged:

  • GET /api/v1/repos/<owner>/<name>/queue and next_up with all: true list every open issue in the dispatcher's order.
  • The route has an OpenAPI row and a session timing phase.
  • The /next page budget is raised by one statement, because the fixture's unranked issue now appears as a row.
Changes requested — before the latest push

Changes needed, in one round:

  1. Do not raise the /next statement budget (test/pages-budget.test.ts says lower, never raise). The extra statement is untraced ('its labels are read'): find it and batch it, so listing unranked rows costs no per-row reads.
  2. state is derived by why.startsWith("waits for") - string-matching human prose. Derive it from the graph (is the issue blocked by open work) so rewording a reason cannot flip it.
  3. /queue is anonymous and returns every open issue unbounded, running nextUp + notStartable per request. Cap it (limit/offset or a hard max with a total) and, since rest is now unbounded, bound what the /next page and tool render too.
  4. Box 4: the /next page and the terminal Next screen now show the extra rows (page output changed). Confirm the TUI screen and rank moves still behave with unranked blocked rows, and add a test for that. Add docs/features/ how-to for the queue (API + tool).
  5. Test: box 3 asks that the first N rows are the next N the dispatcher starts; the test compares only ready rows. Also assert that the blocked ranked row does not displace the ready order, and cover a held (open pull / parked) row, which no test builds. Also exercise /queue for a private repo as a guest (404) and anonymous scope.
  6. Format: rowOf body in next_up.ts is mis-indented.
commented

Next up now lists every open issue in the dispatcher's order, and this round answers all six review points. bun scripts/gate.ts, bun x tsc --noEmit, bun scripts/design-lint.ts and 12 covering test files pass locally. scripts/fast.ts named 1970 test files, so I ran only those 12 rather than the whole suite.

What changed

  • Statement budget: the /next budget is back to 64. The extra statement was the label read in priceRows (src/flow/week.ts), which only narrows an estimate by kind when there is history. It is now skipped when there is none, so no row costs a per-row read. With history the read is still one batched query.
  • state from the graph: a new waitsOn(graph, issue) returns the open blockers, and whyNot uses it too. A row is blocked when open work waits in front of it and it is not deferred. It is held for anything else that passes it over. Rewording why can no longer change state.
  • Bounds: GET /queue takes limit (1–200, default 100) and offset. A bad value is 422 bad_page. It answers { queue, total, limit, offset, notNow }.

- nextUp carries at most 200 unranked rows that cannot start (REST_MAX) and reports the overflow as unlisted. The page shows a line for it and the tool adds it to more.

  • Terminal and rank moves: test/tui-next-up.test.ts now has an unranked row that waits on another. It shows on the Next screen, last. Down and Top still reorder the ready rows correctly, and lifting the blocked row to the top ranks it without making it startable.
  • How-to: docs/features/queue.md covers the API and the tool.
  • Tests: test/queue.test.ts covers:

- the ready rows equal the dispatcher's order, and the ranked blocked row displaces none; - a held row (a blocker closed as dropped); - paging, with 422 for bad values; - the private repo's /queue returns 404 to a guest, and a public one answers anonymously; - the cap.

  • Format: the mis-indented rowOf body in next_up.ts is fixed.

Not done

  • Ready and ranked rows are not capped. They are what the dispatcher works through, and the page already shows only ten of them unless ?all=1. The cap covers only the unranked rows that cannot start, which is the unbounded part.
  • I did not add a database index.

Try it

  • Open /<owner>/<name>/next.
  • Run curl "$URL/api/v1/repos/<owner>/<name>/queue?limit=20".
  • Call the next_up tool with all: true.
Approved

Round 3: the earlier asks are met. state is now derived from the graph via waitsOn, /queue has limit/offset with 422 bad_page, unranked rows are capped by REST_MAX with the overflow counted as unlisted, the terminal screen and rank keys are tested with a waiting row, docs/features/queue.md exists, and the extra SQL statement is removed at its source. Private repo is 404 to a guest, the route has an OpenAPI row, and no new dependency. Box 4 is left: next.ts gains one muted line and a condition for unlisted rows. The line is small and plain, so it is not blocking, but it does change page markup. The preview here serves no trevato/forge queue, so I judged the API from the tests.

commented

Left #11618 open: 1 unticked item remain (No page markup changes in this pull.).

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs