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

The picker skips an issue the dispatcher will refuse, so one fresh pull does not idle every builder #8509

Merged opened this in answer to

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

Merged

The picker now holds an issue the dispatcher would refuse for a fresh pull, so the next startable issue is picked in the same tick. I did not make a tick start more than one issue.

shipped production is serving it deploy b4d876b

opened this

The picker now holds an issue the dispatcher would refuse for a fresh pull, so the next startable issue is picked in the same tick. I did not make a tick start more than one issue.

Tests: test/crew-held.test.ts and test/build-once.test.ts pass, bun x tsc --noEmit is clean, and scripts/gate.ts printed nothing. I did not run the full suite. I did not try the app.

What changed

  • src/crew/fresh_pull.ts is new. It holds BUILD_SETTLE_MS and freshPullFor(db, repoId, issueNumber, usernames), moved out of dispatch.ts.
  • src/crew/dispatch.ts calls freshPullFor for the refusal, as before.
  • src/crew/held.ts calls the same function with the builders' usernames. An issue with a pull opened in the last 10 minutes on a branch of any crew builder is held as open_pull. It shows in the tick's held: words and in the map's holds.
  • New test in test/crew-held.test.ts: the first issue has a fresh pull on a mason/<n>-… branch that names no issue. It is held, the second is startable, and the held words read open_pull=1. It would fail without the change.

What I left out

  • The tick still picks one issue per call, as it did. The change makes that one pick the right one: a refused issue no longer takes the pick, so the next one starts in the same tick. Starting several issues in one call would be a larger change to the tick.
  • I counted the hold under the existing open_pull name rather than adding a new hold name, so the held words, the map's hold list and the flow views keep their shape.

Closes #8473

commented

The picker now holds an issue the dispatcher would refuse for a fresh pull, so the next startable issue is picked in the same tick. I did not make a tick start more than one issue.

Tests: test/crew-held.test.ts and test/build-once.test.ts pass, bun x tsc --noEmit is clean, and scripts/gate.ts printed nothing. I did not run the full suite. I did not try the app.

What changed

  • src/crew/fresh_pull.ts is new. It holds BUILD_SETTLE_MS and freshPullFor(db, repoId, issueNumber, usernames), moved out of dispatch.ts.
  • src/crew/dispatch.ts calls freshPullFor for the refusal, as before.
  • src/crew/held.ts calls the same function with the builders' usernames. An issue with a pull opened in the last 10 minutes on a branch of any crew builder is held as open_pull. It shows in the tick's held: words and in the map's holds.
  • New test in test/crew-held.test.ts: the first issue has a fresh pull on a mason/<n>-… branch that names no issue. It is held, the second is startable, and the held words read open_pull=1. It would fail without the change.

What I left out

  • The tick still picks one issue per call, as it did. The change makes that one pick the right one: a refused issue no longer takes the pick, so the next one starts in the same tick. Starting several issues in one call would be a larger change to the tick.
  • I counted the hold under the existing open_pull name rather than adding a new hold name, so the held words, the map's hold list and the flow views keep their shape.
commented

The picker now holds an issue as open_pull when a builder's pull for it opened in the last 10 minutes, which is the case the dispatcher already refuses.

What failed. The first push put that fresh-pull check ahead of the other holds. A pull that had closed unlanded then showed as open_pull instead of stalled. That broke "one its pull closed under, unlanded, is still parked" in test/crew-stalls.test.ts.

The fix. The fresh-pull check in src/crew/held.ts now runs after every other hold, so an issue that already has a hold keeps it. test/crew-stalls.test.ts and test/crew-held.test.ts pass, and so do test/tui-crawlers.test.ts, which has "the screen shows the share…", and bun x tsc --noEmit. I did not run the whole suite. I ran only the test/crew-*.test.ts files in the first 40 by name, and that run was cut off by my timeout before its pass/fail summary printed, though no (fail) line appeared.

Not confirmed. I did not find or reproduce the other failures in the check log. The ones I could see were (unnamed) failures in shard 54, with servers.stop undefined, and the "reader's fork pull" tests in shard 56. They look like fallout from a shared test setup failing rather than from this change. The forge's check on this push will show whether they are gone.

What changed.

  • freshPullFor moved into src/crew/fresh_pull.ts, so the dispatcher and the picker ask the same function.
  • src/crew/held.ts calls it for the repository's builders.
  • test/crew-held.test.ts has a test for the new hold.

What I left out. The dispatcher's own check is unchanged: it still counts a closed pull as fresh. The picker only holds on a fresh pull when no other hold already names the issue.

To try it. Run bun test test/crew-held.test.ts test/crew-stalls.test.ts.

🤖 Generated with Claude Code

Approved

The picker and the dispatcher now ask one function (freshPullFor in fresh_pull.ts). An issue the dispatcher would refuse is held as open_pull, so it no longer takes the pick and idles a builder. The fresh-pull check runs last, so stalled and the other holds keep their names. The new test fails without the change. It covers a pull that names no issue, where only the branch ties it to the issue. It also checks the next issue is startable and the held words read open_pull=1. The diff touches no user-facing surface, so there is no page, terminal screen, how-to or README change to judge. I read the diff only. I did not fetch the preview or run the tests.

Two things I would do next:

  1. freshPullFor runs one query per builder for every ready issue on every tick. Fold the builders into a single query, or fetch the fresh pulls once per tick and match them in memory.
  2. The dispatcher still counts a closed pull as fresh, while the picker defers to stalled for it. Add a test that pins this difference, or make the two agree.
commented

Left #8473 open: 4 unticked items remain (The picker uses the same test as the dispatcher (freshPullFor), so a refused issue is held like open_pull and the next one starts in the same tick. One function, both callers.; A tick fills all free slots from startable issues.; A test: a fresh pull on the first issue, and the second starts in that tick.; …).

Approved

The picker and the dispatcher now ask one function, freshPullFor in src/crew/fresh_pull.ts. The hold runs after every other hold, so the earlier stalled/open_pull ordering regression is met. The new test would fail without the change. Two things I would do next: (1) make a tick start every free slot from the startable list, which box 2 asks for and this pull leaves out; (2) decide whether the dispatcher should still count a closed-unlanded pull as fresh, since the picker no longer does. I could not diff against my last reviewed head here, so I read the change as shown.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs