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

A team's page lists the open pulls waiting on its review, oldest first #10171

Merged opened this

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

Merged

Each team's card on Settings › Teams now has a "Waiting on this team" list. It shows the open pulls that team still holds, oldest first, at most 50. Server-rendered. Tests and the type check pass, and so do the gate and design-lint. I didn't check 390px by eye or run the preview, and I couldn't run `keep_plan`, so the plan isn't on the issue.

shipped production is serving it deploy 3d07d6f

opened this

Each team's card on Settings › Teams now has a "Waiting on this team" list. It shows the open pulls that team still holds, oldest first, at most 50. Server-rendered. Tests and the type check pass, and so do the gate and design-lint. I didn't check 390px by eye or run the preview, and I couldn't run keep_plan, so the plan isn't on the issue.

What changed

  • Each row is owner/repo #n, linked to the pull, then the title (escaped) and its age. A team with nothing waiting says "Nothing is waiting on this team." in one muted line.
  • "Holds" means the same thing as the merge gate (teamsHolding). A pull counts only when it closes an issue naming the team, the team has a crew reviewer, and it has no current approval on the head. A team with nobody to ask therefore never lists anything.
  • Pulls the viewer can't read are left out. That covers private repos and sealed reports, and the 50 cap applies after that filter.
  • The query lives in src/core/team_queue.ts (pullsWaitingOn). It walks a new partial index, issue_open_pull_age, in age order, so there's no sort and no scan. The migration is 20261109160000-team-queue-indexes and its pin is committed.
  • The page times the lists as one queue phase in Server-Timing.

Tests (test/team-queue.test.ts)

  • Order, approved pulls dropped, stranger and signed-out viewers see nothing, and the title is escaped.
  • With 200 open pulls, 50 come back and the query takes under 50 ms. The test times the query function itself, and also covers a team that holds nothing, which has to walk all 200.
  • The query plan uses issue_open_pull_age with no temp b-tree.

Left out

  • There is no public per-team page. "The team page" is the admin-only /settings/teams, so only an admin viewer sees these lists. I kept it there rather than build a new page.
  • The live stream refreshes only on team.changed, so the lists update on reload, not when a review lands.
  • scripts/fast.ts called the diff too broad, so I ran the teams, crew-teams, repo-transfer, schema, migration and hot-reads tests instead of the full suite.

Try it: sign in as an admin and open /settings/teams.

Closes #10162

commented

Each team's card on Settings › Teams now has a "Waiting on this team" list. It shows the open pulls that team still holds, oldest first, at most 50. Server-rendered. Tests and the type check pass, and so do the gate and design-lint. I didn't check 390px by eye or run the preview, and I couldn't run keep_plan, so the plan isn't on the issue.

What changed

  • Each row is owner/repo #n, linked to the pull, then the title (escaped) and its age. A team with nothing waiting says "Nothing is waiting on this team." in one muted line.
  • "Holds" means the same thing as the merge gate (teamsHolding). A pull counts only when it closes an issue naming the team, the team has a crew reviewer, and it has no current approval on the head. A team with nobody to ask therefore never lists anything.
  • Pulls the viewer can't read are left out. That covers private repos and sealed reports, and the 50 cap applies after that filter.
  • The query lives in src/core/team_queue.ts (pullsWaitingOn). It walks a new partial index, issue_open_pull_age, in age order, so there's no sort and no scan. The migration is 20261109160000-team-queue-indexes and its pin is committed.
  • The page times the lists as one queue phase in Server-Timing.

Tests (test/team-queue.test.ts)

  • Order, approved pulls dropped, stranger and signed-out viewers see nothing, and the title is escaped.
  • With 200 open pulls, 50 come back and the query takes under 50 ms. The test times the query function itself, and also covers a team that holds nothing, which has to walk all 200.
  • The query plan uses issue_open_pull_age with no temp b-tree.

Left out

  • There is no public per-team page. "The team page" is the admin-only /settings/teams, so only an admin viewer sees these lists. I kept it there rather than build a new page.
  • The live stream refreshes only on team.changed, so the lists update on reload, not when a review lands.
  • scripts/fast.ts called the diff too broad, so I ran the teams, crew-teams, repo-transfer, schema, migration and hot-reads tests instead of the full suite.

Try it: sign in as an admin and open /settings/teams.

commented

Each team's card on Settings › Teams now has a "Waiting on this team" list. The check failed only because no commit had a Try: line, so I added an empty commit that carries one. I ran bun scripts/gate.ts locally after that and it printed nothing; I didn't re-run the tests, since the code is unchanged.

What changed

  • Each list shows the open pulls that team still holds, oldest first, at most 50.
  • A row is owner/repo #n, linked to the pull, then the title (escaped) and its age.
  • A team with nothing waiting says "Nothing is waiting on this team." in one muted line.
  • A pull counts as waiting when it closes an issue naming the team, the team has a crew reviewer, and it has no current approval on the head. That is the same rule the merge gate uses.
  • Pulls the viewer can't read, such as those in private repos or sealed reports, are left out before the 50 cap applies.
  • The query is pullsWaitingOn in src/core/team_queue.ts. It walks a new partial index, issue_open_pull_age, in age order, with no sort and no scan. The migration is 20261109160000-team-queue-indexes, and its pin is committed.
  • test/team-queue.test.ts covers order, approved pulls dropped, hidden private repos, and the escaped title. With 200 open pulls it checks the query takes under 50 ms and uses the index.

What I chose not to do

  • There is no new public per-team page. The list lives on /settings/teams, which is admin-only, so only an admin viewer sees it.
  • The live stream refreshes only when a team changes, so the lists update on reload, not when a review lands.
  • I didn't check 390px by eye.

Try it: sign in as an admin and open /settings/teams on the preview.

commented

The check's two failures on my test are fixed; the third failure is not mine to fix and I've left it. tsc, the gate and the related tests pass (87 pass, 0 fail). I haven't seen the forge's check result for this push.

  • Wall-clock budget: test/team-queue.test.ts no longer asserts on a stopwatch. It counts SQL statements with expectWork: at most 600 for a team with 50 pulls waiting, and at most 20 for a team with none.
  • Why the second budget is so low: counting statements showed the first version cost about 5 to 10 statements per open pull, 1000 to 2000 for a team with nothing waiting. pullsWaitingOn now first reads the issues that name the team and skips every pull that closes none of them. A team with nothing waiting costs a handful of statements instead of a walk over every open pull.
  • New migration: that read is indexed by 20261109170000-issue-team-by-slug (issue_team_slug), with its pin committed. The test checks the query plan uses it.
  • Third failure: "a repository name with markup in it is escaped in the select" (test/feed-composer-language.test.ts) is outside my change. It passes when run alone here. If it fails again on this push, it is likely a flake between shards.

Try it: sign in as an admin and open /settings/teams on the preview. Each team card has a "Waiting on this team" list, with at most 50 pulls, oldest first.

Changes requested — before the latest push

Core query, escaping, read-rights filtering and indexes look sound. Two things block: (1) no terminal door — the page lists a team queue but there is no src/tui/ screen or entry per AGENTS.md, and no docs/features/<name>.md how-to. Fix first: add the TUI view over pullsWaitingOn (same viewer gating) and the feature doc. (2) the list is hand-rolled markup under an h3 repeated per card; use the named row component from docs/DESIGN.md. I did not verify the preview by eye at 390px; the builder also did not. Also the lists do not refresh live when a review lands (only on team.changed); at least say so in the feature doc or publish the event.

the crew's run was interrupted: restart, resumes on the next pass — cancelled
commented

I've addressed the review on this branch except for asking for a timed query test. I disagree with that one and explain why below. tsc, the gate, design-lint and the teams, team-queue and timing tests all pass. The preview isn't checked at 360px or by eye, and I haven't seen the forge's check result for this push.

What changed

  • Terminal: the Crew teams screen now has w, "waiting on it". It opens src/tui/screens/teamqueue.ts, which lists the same pulls oldest first. It reads through a new readTeamQueue action (src/actions/teams.ts), and the web page reads through the same action. Anyone but a forge admin who is a person is refused on both, and unreadable pulls are dropped before the 50 cap.
  • Docs: docs/features/team-queue.md explains both doors, the gate, the meaning of "holds" and the query.
  • Page layout: the extra <h3> and bare ul are gone. Each card shows one muted line, "Waiting on this team, oldest first", then status-rows (dl) rows. The repo and pull number are the dt and the title with its age is the dd. A team with nothing waiting keeps the single "Nothing is waiting on this team." line.
  • Tests: each test now builds its own forge (beforeEach/afterEach), so none depends on the one before. The as never ctx cast is gone, and the page test fetches /settings/teams from a real server with a session cookie. A new terminal test checks that an admin sees the pull and readTeamQueue refuses a non-admin, a signed-out viewer and an unknown slug.
  • Work budget: the bounds are tighter: at most 20 statements per listed pull (1000 for a full 50) and 5 for a team nothing names.

Disagreed: I did not add a stopwatch assertion for the query time. The repo's test/helpers/work.ts says wall-clock is never judged in a pull, and my earlier timed test failed the check for that reason. The test counts SQL statements instead, and the query-plan checks assert the index use, with no temp b-tree.

Left out: the lists still refresh on reload, not live.

Try it: sign in as an admin and open /settings/teams on the preview. In the terminal, open Crew teams, move to a team and press w.

commented

I merged main into the branch and pushed; the conflict was in src/tui/screens/teams.ts. The gate, tsc, design-lint and the teams, team-queue, MCP-waiting and schema tests pass. I haven't seen the forge's check result for this push.

What the conflict was: #10173 landed on main and already gave the terminal a w key on the Crew teams screen, plus an MCP org_teams option, both reading pullsWaitingOn in src/actions/issue_teams.ts. My branch had added its own w screen, src/tui/screens/teamqueue.ts.

How I resolved it:

  • Terminal: I kept main's w screen, since it is already merged and tested, and deleted my teamqueue.ts. The terminal side of this pull is now the one on main.
  • Web page: the /settings/teams list stays as I built it. It reads through my indexed query in src/core/team_queue.ts and the readTeamQueue gate, which is admin-only.
  • Tests and docs: the terminal test in test/team-queue.test.ts now expects main's row format (garden#N title). docs/features/team-queue.md points at main's screen.

Two things to know:

  • Two functions with the same name: there are now two pullsWaitingOn. main's version in src/actions/issue_teams.ts feeds the terminal and MCP. Mine in src/core/team_queue.ts feeds the page.
  • Why I didn't unify them: main's does not use my index and sorts in memory, and its viewer rules differ from the page's admin-only gate. Merging them would change the MCP tool, so I left that for a follow-up.
Changes requested — before the latest push

Earlier asks: terminal door (met, by main's w screen), docs file (present, but now wrong, see below), design rows (met: status-rows dl, one muted line), tests per-test forge and work budget (met). One thing to fix first: docs/features/team-queue.md says both doors go through readTeamQueue and that the terminal is the same list, but after the merge the terminal reads main's separate pullsWaitingOn in src/actions/issue_teams.ts, with different viewer rules and no index. Either route the terminal screen through readTeamQueue/core pullsWaitingOn (preferred: one gate, one list, the two same-named functions collapse), or make the doc say plainly that the two doors read different code and rules. Non-blocking: the one-line comment edit in src/tui/screens/teams.ts is now hand-wrapped past 80 columns and describes a key this pull did not add; revert it. Not verified: the 390px look of long repo/title rows.

commented

Each Crew team's card on /settings/teams now lists the open pulls the team still holds, oldest first, at most 50. The terminal's w screen shows the same list behind the same admin-only gate (readTeamQueue). This push also merges the latest main. The gate, tsc and design-lint pass, as do the team-queue, terminal teams, MCP waiting, crew-teams and timing tests. I haven't seen the forge's check result for this push.

  • Terminal: the w screen that came from main (#10173) now reads readTeamQueue instead of the other pullsWaitingOn in src/actions/issue_teams.ts. Rows keep their owner/repo#n title and "since" format, so the MCP test's terminal case still passes.
  • Docs: docs/features/team-queue.md now matches the code. The page and terminal share one gate and one list, and the MCP tool still uses its own function.
  • Comment fix: I reverted my edit to the header comment in src/tui/screens/teams.ts and wrapped the screen's doc comment within 80 columns.

Left out: MCP org_teams with waiting: true still uses its own function, so two functions named pullsWaitingOn remain. That function needs an admin-scoped token, applies the viewer's read rules per pull, doesn't use the index, and sorts in memory. Merging the two would change the MCP tool's behaviour, so it belongs in its own pull. Still not checked: how long repo names and titles look at 390px.

Try it: sign in as an admin and open /settings/teams on the preview. In the terminal, open Crew teams, move to a team and press w.

🤖 Generated with Claude Code

Approved

Earlier asks are met: the terminal and the page share one gate (readTeamQueue), the how-to is in docs/features/team-queue.md and not README, rows are status-rows dl rows, the title is escaped, each test builds its own forge, and the work budget is counted in statements. The preview answers 200. I could not diff against 0ecf30ef2991 (the commit is not in my clone), so I judged the head as a whole. Next, in order: 1) Non-blocking: unify the two pullsWaitingOn functions (MCP and page) in a follow-up so the gate cannot drift. 2) Non-blocking: look at long owner/repo names and titles at 360-390px and add a wrap if the dd runs wide.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs