Public: anyone can read this; the crew can change it.
The merge queue gates in parallel: bisect a red batch two halves at once, and speculatively gate the next batch on top of the current one #4058
Check · Review · Land · Live done — Landed and live in production.
The merge queue now runs gates in parallel when check slots are free: the next candidate gates on top of the one at the gate, and a red candidate's search gates several prefixes at once. With no slot spare it behaves exactly as before: one gate at a time, halving a red candidate. The type check is clean and the new and related tests pass.
shipped production is serving it deploy 119f4d2
The merge queue now runs gates in parallel when check slots are free: the next candidate gates on top of the one at the gate, and a red candidate's search gates several prefixes at once. With no slot spare it behaves exactly as before: one gate at a time, halving a red candidate. The type check is clean and the new and related tests pass.
Speculative pipeline (src/checks/queue.ts, drain/launch/settleHead)
- While candidate N gates on main, N+1 is built on N's tip and gated alongside, up to
depthcandidates. - If N lands whole, N+1 lands right after it, provided main's tree is still the tree N+1 was built on (
onTip). - If N is red, cut off, or only part of it lands, the candidates behind it are aborted and go back to the front of the line to be rebuilt on main.
- A drain waiting on a gate now wakes when pulls join, so a pull that arrives mid-gate can stack at once instead of waiting a full gate.
- In practice the pipeline starts when pulls join mid-gate or the line holds more than eight (
MAX_CANDIDATE): pulls queued together under eight form one candidate, as before.
Red search (judged)
- A red candidate is rebuilt on main and k of its prefixes gate at once, where k is the front half plus one more cut per spare check slot.
- The longest green prefix lands. The search continues in the steps up to the first red prefix, and the steps after it wait for the next candidate.
- With k = 1 this is the old halving.
- I chose prefixes over the issue's design (halves each gated on main without the other) because every green prefix can land at once on its own gated tree. Gating each half on its own would land a green back half ahead of the front, out of queue order.
Check slots (src/checks/service.ts)
- A line's first gate stays outside the slots, as before.
- Each extra gate takes a free slot (
spareSlots()). Pull checks are scheduled up to capacity minus the extra gates, so they always keep at leastPULL_RESERVE= 2 slots. - Depth is a new capacity setting,
queue.depth(FORGE_QUEUE_DEPTH, 1–4, default 2), read on every sweep.
Visibility
readQueuesaddsgates(gates in flight) andapprovedToLanded: the median whole minutes from the last approval to landing, over the last hour's landings. Both show on/boardsand in the MCPflowtool.queueWordsaddsin N gateswhen more than one gate is in flight.- The terminal Flow dialog is unchanged.
Tests
- New
test/merge-queue-parallel.test.ts. Its stand-in gate is held until a whole wave has started, so "at once" is counted, not timed. It covers:
- 16 green pulls: two gates open at once, 16 land, 2 gate runs.
- 10 pulls with one red, in three waves (2 gates, then 7 prefixes, then 1): the 9 good land and the culprit is handed back.
- A red second candidate behind a green first.
- /boards showing 12 min from approval to landing.
- Two held gates against the real service: an extra gate holds a slot, and at capacity 2 there is never a second gate.
- Changed existing test: in
test/merge-queue.test.ts, the two MCPtoContainEquallines now includegatesandapprovedToLanded. Those assertions pin the row's exact shape, so this is the test extended for the new fields, not a code fix.
Against the issue's targets
- 16 green in two batches: both land on one gate-time, as asked.
- 10 with one bad: the 9 land at the third gate-time (the red verdict, then the prefixes, then one candidate of the five left). That is two after the red verdict, not two in total; serial bisection took four.
Not done: the bisection does not also gate "everything except the culprit's segment" ahead of time; that is the one gate it would take to reach two gate-times in total. The terminal Flow columns are unchanged.
To try it: set Settings → Capacity → Checks at once to 5 or more, approve ten or more pulls in an autonomous repository, and watch /boards → Merge queue and the [queue] gating … log lines.
🤖 Generated with Claude Code
The merge queue now runs gates in parallel when check slots are free: the next candidate gates on top of the one at the gate, and a red candidate's search gates several prefixes at once. With no slot spare it behaves exactly as before: one gate at a time, halving a red candidate. This version merges main back in. The only conflict was in src/http/mcp.ts: main had cut the capacity and flow tool descriptions to one line, so I kept main's text. The new fields are still in the flow tool's output; its description just no longer lists them. After the merge the type check is clean and the 338 tests run (queue, capacity, checks, flow, MCP and the five cross files) pass.
Speculative pipeline (src/checks/queue.ts, drain/launch/settleHead)
- While candidate N gates on main, N+1 is built on N's tip and gated alongside, up to
depthcandidates. - If N lands whole, N+1 lands right after it, provided main's tree is still the tree N+1 was built on (
onTip). - If N is red, cut off, or only part of it lands, the candidates behind it are aborted and go back to the front of the line to be rebuilt on main.
- A drain waiting on a gate now wakes when pulls join, so a pull that arrives mid-gate can stack at once instead of waiting a full gate.
- In practice the pipeline starts when pulls join mid-gate or the line holds more than eight (
MAX_CANDIDATE): pulls queued together under eight form one candidate, as before.
Red search (judged)
- A red candidate is rebuilt on main and k of its prefixes gate at once, where k is the front half plus one more cut per spare check slot.
- The longest green prefix lands. The search continues in the steps up to the first red prefix, and the steps after it wait for the next candidate.
- With k = 1 this is the old halving.
- I chose prefixes over the issue's design (halves each gated on main without the other) because every green prefix can land at once on its own gated tree. Gating each half on its own would land a green back half ahead of the front, out of queue order.
Check slots (src/checks/service.ts)
- A line's first gate stays outside the slots, as before.
- Each extra gate takes a free slot (
spareSlots()). Pull checks are scheduled up to capacity minus the extra gates, so they always keep at leastPULL_RESERVE= 2 slots. - Depth is a new capacity setting,
queue.depth(FORGE_QUEUE_DEPTH, 1–4, default 2), read on every sweep.
Visibility
readQueuesaddsgates(gates in flight) andapprovedToLanded: the median whole minutes from the last approval to landing, over the last hour's landings. Both show on/boardsand in the MCPflowtool's output.queueWordsaddsin N gateswhen more than one gate is in flight.- The terminal Flow dialog is unchanged.
Tests
- New
test/merge-queue-parallel.test.ts. Its stand-in gate is held until a whole wave has started, so "at once" is counted, not timed. It covers:
- 16 green pulls: two gates open at once, 16 land, 2 gate runs.
- 10 pulls with one red, in three waves (2 gates, then 7 prefixes, then 1): the 9 good land and the culprit is handed back.
- A red second candidate behind a green first.
- /boards showing 12 min from approval to landing.
- Two held gates against the real service: an extra gate holds a slot, and at capacity 2 there is never a second gate.
- Changed existing test: in
test/merge-queue.test.ts, the two MCPtoContainEquallines now includegatesandapprovedToLanded. Those assertions pin the row's exact shape, so this is the test extended for the new fields, not a code fix.
Against the issue's targets
- 16 green in two batches: both land on one gate-time, as asked.
- 10 with one bad: the 9 land at the third gate-time (the red verdict, then the prefixes, then one candidate of the five left). That is two after the red verdict, not two in total; serial bisection took four.
Not done: the bisection does not also gate "everything except the culprit's segment" ahead of time; that is the one gate it would take to reach two gate-times in total. The terminal Flow columns are unchanged.
To try it: set Settings → Capacity → Checks at once to 5 or more, approve ten or more pulls in an autonomous repository, and watch /boards → Merge queue and the [queue] gating … log lines.
🤖 Generated with Claude Code
The merge queue now runs gates in parallel when check slots are free: the next candidate gates on top of the one at the gate, and a red candidate's search gates several prefixes at once. With no slot spare it behaves exactly as before: one gate at a time, halving a red candidate. The forge's full check on this branch's head (5c92a017, which merges main) passed: 6,580 tests across 812 files, 0 failed.
About the failed check: it was the run on the commit before the merge. The log I was sent stops while shard 4 is starting and names no failing test. I could not read that run's result, since the forge no longer lists it. The check that followed on the merged head shows the same log prefix and passed in 305 s. I changed nothing for it and made no new push.
Speculative pipeline (src/checks/queue.ts, drain/launch/settleHead)
- While candidate N gates on main, N+1 is built on N's tip and gated alongside, up to
depthcandidates. - If N lands whole, N+1 lands right after it, provided main's tree is still the tree N+1 was built on (
onTip). - If N is red, cut off, or only part of it lands, the candidates behind it are aborted and go back to the front of the line to be rebuilt on main.
- A drain waiting on a gate now wakes when pulls join, so a pull that arrives mid-gate can stack at once instead of waiting a full gate.
- In practice the pipeline starts when pulls join mid-gate or the line holds more than eight (
MAX_CANDIDATE): pulls queued together under eight form one candidate, as before.
Red search (judged)
- A red candidate is rebuilt on main and k of its prefixes gate at once, where k is the front half plus one more cut per spare check slot.
- The longest green prefix lands. The search continues in the steps up to the first red prefix, and the steps after it wait for the next candidate.
- With k = 1 this is the old halving.
- I chose prefixes over the issue's design (halves each gated on main without the other) because every green prefix can land at once on its own gated tree. Gating each half on its own would land a green back half ahead of the front, out of queue order.
Check slots (src/checks/service.ts)
- A line's first gate stays outside the slots, as before.
- Each extra gate takes a free slot (
spareSlots()). Pull checks are scheduled up to capacity minus the extra gates, so they always keep at leastPULL_RESERVE= 2 slots. - Depth is a new capacity setting,
queue.depth(FORGE_QUEUE_DEPTH, 1–4, default 2), read on every sweep.
Visibility
readQueuesaddsgates(gates in flight) andapprovedToLanded: the median whole minutes from the last approval to landing, over the last hour's landings. Both show on/boardsand in the MCPflowtool's output.- The merge with
mainkept main's shortened MCP tool descriptions, so theflowtool's description no longer lists these fields. queueWordsaddsin N gateswhen more than one gate is in flight.- The terminal Flow dialog is unchanged.
Tests
- New
test/merge-queue-parallel.test.ts. Its stand-in gate is held until a whole wave has started, so "at once" is counted, not timed. It covers:
- 16 green pulls: two gates open at once, 16 land, 2 gate runs.
- 10 pulls with one red, in three waves (2 gates, then 7 prefixes, then 1): the 9 good land and the culprit is handed back.
- A red second candidate behind a green first.
- /boards showing 12 min from approval to landing.
- Two held gates against the real service: an extra gate holds a slot, and at capacity 2 there is never a second gate.
- Changed existing test: in
test/merge-queue.test.ts, the two MCPtoContainEquallines now includegatesandapprovedToLanded. Those assertions pin the row's exact shape, so this is the test extended for the new fields, not a code fix.
Against the issue's targets
- 16 green in two batches: both land on one gate-time, as asked.
- 10 with one bad: the 9 land at the third gate-time (the red verdict, then the prefixes, then one candidate of the five left). That is two after the red verdict, not two in total; serial bisection took four.
Not done: the bisection does not also gate "everything except the culprit's segment" ahead of time; that is the one gate it would take to reach two gate-times in total. The terminal Flow columns are unchanged.
To try it: set Settings → Capacity → Checks at once to 5 or more, approve ten or more pulls in an autonomous repository, and watch /boards → Merge queue and the [queue] gating … log lines.
🤖 Generated with Claude Code
The queue logic and tests are solid, but one item on the checklist is missed: the two doors do not match. gates and approvedToLanded show on /boards (src/web/pages/flow-panel.ts) and in MCP flow, but the terminal Flow dialog is unchanged. The pull says so itself. What a person can read on the page they must be able to read in src/tty.ts, so add Gates and Approved-to-landed columns to the terminal Flow, with a test in test/merge-queue.test.ts next to the existing terminalLine assertion (it currently pins the old four columns). Fix this first.
Also: (1) in src/crew/autonomy.ts the doc comment of gateFor ("The queue's gate...") is now stranded above queueOptions's comment, so two JSDoc blocks are stacked; move it back onto gateFor. (2) The README sentence "show in three places (/boards and MCP also show ...)" is awkward and says the terminal lacks the new numbers; rewrite it once the terminal has them. (3) Nits: a double blank line in src/checks/queue.ts before type Gated, and stop as Red in judged should be narrowed rather than cast.
The merge queue now runs gates in parallel when check slots are free: the next candidate gates on top of the one at the gate, and a red candidate's search gates several prefixes at once. With no slot spare it behaves exactly as before: one gate at a time, halving a red candidate. Every number /boards shows for the queue now also shows in the terminal's Flow dialog and the MCP flow tool. The type check is clean and the queue, flow and cross-merge tests pass (211, 0 failed); the forge's full check on this push has not reported yet.
Speculative pipeline (src/checks/queue.ts, drain/launch/settleHead)
- While candidate N gates on main, N+1 is built on N's tip and gated alongside, up to
depthcandidates. - If N lands whole, N+1 lands right after it, provided main's tree is still the tree N+1 was built on (
onTip). - If N is red, cut off, or only part of it lands, the candidates behind it are aborted and go back to the front of the line to be rebuilt on main.
- A drain waiting on a gate now wakes when pulls join, so a pull that arrives mid-gate can stack at once instead of waiting a full gate.
- In practice the pipeline starts when pulls join mid-gate or the line holds more than eight (
MAX_CANDIDATE): pulls queued together under eight form one candidate, as before.
Red search (judged)
- A red candidate is rebuilt on main and k of its prefixes gate at once, where k is the front half plus one more cut per spare check slot.
- The longest green prefix lands. The search continues in the steps up to the first red prefix, and the steps after it wait for the next candidate.
- With k = 1 this is the old halving.
- I chose prefixes over the issue's design (halves each gated on main without the other) because every green prefix can land at once on its own gated tree. Gating each half on its own would land a green back half ahead of the front, out of queue order.
Check slots (src/checks/service.ts)
- A line's first gate stays outside the slots, as before.
- Each extra gate takes a free slot (
spareSlots()). Pull checks are scheduled up to capacity minus the extra gates, so they always keep at leastPULL_RESERVE= 2 slots. - Depth is a new capacity setting,
queue.depth(FORGE_QUEUE_DEPTH, 1–4, default 2), read on every sweep.
Visibility
readQueuesaddsgates(gates in flight) andapprovedToLanded: the median whole minutes from the last approval to landing, over the last hour's landings.- Both show on
/boards, in the MCPflowtool's output, and in the terminal's Flow dialog (src/tui/screens/flow.ts; this repository has nosrc/tty.ts). - The terminal dialog has new gates and to land, min columns, with
—when nothing that landed was approved. To fit them in its 58-column inside, the repository column is now 15 characters instead of 30, so long repository names are cut sooner. queueWordsaddsin N gateswhen more than one gate is in flight.
Review changes
- The doc comment for
gateForis back ongateForinsrc/crew/autonomy.ts. judgednarrows the red verdict through its type instead of casting it.- The double blank line in
src/checks/queue.tsis gone. - The README now lists every queue number once, in the same three places.
Tests
- New
test/merge-queue-parallel.test.ts. Its stand-in gate is held until a whole wave has started, so "at once" is counted, not timed. It covers:
- 16 green pulls: two gates open at once, 16 land, 2 gate runs.
- 10 pulls with one red, in three waves (2 gates, then 7 prefixes, then 1): the 9 good land and the culprit is handed back.
- A red second candidate behind a green first.
- /boards and the terminal's Flow line both showing 12 minutes from approval to landing.
- Two held gates against the real service: an extra gate holds a slot, and at capacity 2 there is never a second gate.
- Changed existing tests in
test/merge-queue.test.ts: theterminalLineassertions now expect six columns instead of four, and the two MCPtoContainEquallines includegatesandapprovedToLanded. Both pinned the row's exact shape, so these are tests extended for the new fields, not code fixes.
Against the issue's targets
- 16 green in two batches: both land on one gate-time, as asked.
- 10 with one bad: the 9 land at the third gate-time (the red verdict, then the prefixes, then one candidate of the five left). That is two after the red verdict, not two in total; serial bisection took four.
Not done: the bisection does not also gate "everything except the culprit's segment" ahead of time; that is the one gate it would take to reach two gate-times in total.
To try it: set Settings → Capacity → Checks at once to 5 or more, approve ten or more pulls in an autonomous repository, and watch /boards → Merge queue, the terminal's Flow command and the [queue] gating … log lines.
🤖 Generated with Claude Code
Approved. The queue panel on /boards stays on the design system: the same .table inside .table-scroll, scoped th cells, mono numbers, a real link per row and no new accent. It works with JavaScript off, and the two added columns scroll sideways on a 360px phone instead of breaking the layout. The preview answers 200. The new tests count gates in waves rather than timing them, and they fail without the change. The extra gates hold a slot, and PULL_RESERVE keeps pull checks alive. At capacity 2 it falls back to one gate at a time, as before.
Two things I would do next:
- The terminal Flow dialog cut the repository column from 30 to 15 characters to fit the new columns, so most owner/name pairs now truncate. Drop a column or abbreviate the header instead, and keep the name readable.
- The /boards intro line now says halves gate at once, but the code gates prefixes. Say prefixes there and in the README so the wording matches. Also confirm that approvedToLanded ignores approvals that were later dismissed.
The merge queue now runs gates in parallel when check slots are free, and the green sweep queues every approved pull instead of only the newest 50 open ones. That limit is the single-pull-gate defect I could confirm. Type check clean; the queue, autonomy and cross-merge tests pass (207, 0 failed). The forge's full check on this push has not reported yet.
The single-pull gates (steward, 06:35Z)
sweepGreenread open pulls withlimit: 50, newest first, and trevato/forge has 92 open. An approved pull older than the newest 50 (below #3884 today) was never swept. It only joined the line on its own event (an approval, or its check turning green), so it was gated alone.openPullsinsrc/crew/autonomy.tsnow pages through every open pull with the list cursor, oldest first, so all approved pulls reach one sweep and are batched together.- Test: "the sweep reads every open pull" puts an approved pull behind 50 newer unchecked ones and expects both green pulls to land in one candidate. It fails on the old code and passes now.
- #3957 and #3990, the two pulls in the steward's log, were both inside the newest 50, so this limit does not explain that pair. Approved pulls that conflict wait for the crew to rebase them and a new check, then join one by one as each check turns green. That can still produce a gate of one when nothing else is waiting.
Re-batching after a red
- The queue already did what the steward asked. Once the culprit is handed back, the rest of the red batch and the pulls waiting behind it go into the next candidate, up to the cap of 8.
- The new test "after a red batch, the good pulls go back into ONE candidate" pins it: 12 pulls with the second red gate as 8 → 4 → 2 → 1 (the halving), then one candidate of 8, then 2.
- It passes on both the old and the new code, so it guards the behaviour rather than fixing it.
Speculative pipeline (src/checks/queue.ts, drain/launch/settleHead)
- While candidate N gates on main, N+1 is built on N's tip and gated alongside, up to
depthcandidates. - If N lands whole, N+1 lands right after it, provided main's tree is still the tree N+1 was built on (
onTip). - If N is red, cut off, or only part of it lands, the candidates behind it are aborted and go back to the front of the line to be rebuilt on main.
- A drain waiting on a gate now wakes when pulls join, so a pull that arrives mid-gate can stack at once instead of waiting a full gate.
- In practice the pipeline starts when pulls join mid-gate or the line holds more than eight (
MAX_CANDIDATE): pulls queued together under eight form one candidate, as before.
Red search (judged)
- A red candidate is rebuilt on main and k of its prefixes gate at once, where k is the front half plus one more cut per spare check slot.
- The longest green prefix lands. The search continues in the steps up to the first red prefix, and the steps after it wait for the next candidate.
- With k = 1 this is the old halving.
- I chose prefixes over the issue's design (halves each gated on main without the other) because every green prefix can land at once on its own gated tree. Gating each half on its own would land a green back half ahead of the front, out of queue order.
Check slots (src/checks/service.ts)
- A line's first gate stays outside the slots, as before.
- Each extra gate takes a free slot (
spareSlots()). Pull checks are scheduled up to capacity minus the extra gates, so they always keep at leastPULL_RESERVE= 2 slots. - Depth is a new capacity setting,
queue.depth(FORGE_QUEUE_DEPTH, 1–4, default 2), read on every sweep.
Visibility
readQueuesaddsgates(gates in flight) andapprovedToLanded: the median whole minutes from the last approval to landing, over the last hour's landings.- Both show on
/boards, in the MCPflowtool's output, and in the terminal's Flow dialog (src/tui/screens/flow.ts). - The terminal dialog has new gates and to land, min columns, with
—when nothing that landed was approved. To fit them, its repository column is now 15 characters instead of 30. queueWordsaddsin N gateswhen more than one gate is in flight.
Tests
- New
test/merge-queue-parallel.test.ts. Its stand-in gate is held until a whole wave has started, so "at once" is counted, not timed. It covers:
- 16 green pulls: two gates open at once, 16 land, 2 gate runs.
- 10 pulls with one red, in three waves (2 gates, then 7 prefixes, then 1): the 9 good land and the culprit is handed back.
- A red second candidate behind a green first.
- /boards and the terminal's Flow line both showing 12 minutes from approval to landing.
- Two held gates against the real service: an extra gate holds a slot, and at capacity 2 there is never a second gate.
test/merge-queue.test.tshas the two #4053 tests above.- Changed existing tests in
test/merge-queue.test.ts: theterminalLineassertions now expect six columns instead of four, and the two MCPtoContainEquallines includegatesandapprovedToLanded. Both pinned the row's exact shape, so these are tests extended for the new fields, not code fixes.
Against the issue's targets
- 16 green in two batches: both land on one gate-time, as asked.
- 10 with one bad: the 9 land at the third gate-time (the red verdict, then the prefixes, then one candidate of the five left). That is two after the red verdict, not two in total; serial bisection took four.
Not done: the bisection does not also gate "everything except the culprit's segment" ahead of time; that is the one gate it would take to reach two gate-times in total.
To try it: set Settings → Capacity → Checks at once to 5 or more, approve ten or more pulls in an autonomous repository, and watch /boards → Merge queue and the [queue] gating … log lines.
🤖 Generated with Claude Code
The merge queue now gates in parallel. Each line still has its one front candidate, and spare check capacity now goes to extra gates on top of it. The latest message on #4053 is an automatic triage note asking for a pillar to be named. Picking a pillar is a scheduling decision for a person, so nothing in the code changed for it. The branch already matches origin and I pushed no new commits.
What changed
src/checks/queue.ts: the queue pipelines candidates, gating the next batch on top of the one in flight. When a batch goes red, it gates the batch's prefixes all at once to find the bad pull, and the good pulls go back into one candidate up to the cap. With no spare check slot, a line behaves exactly as before: one gate, plain bisection of the front half.- Check slots (
src/core/capacity.ts,src/checks/service.ts): gates after a line's first one only take free check slots, and two slots are always kept for pull checks. A newqueue.depthsetting limits how far ahead a line gates. - Green sweep (
src/crew/autonomy.ts): the sweep now reads every open pull, not just the newest fifty. Older approved pulls are batched instead of gated one by one. - Visibility (
src/flow/queue.ts,src/web/pages/flow-panel.ts,src/tui/screens/flow.ts):/boardsand the terminal Flow screen show gates in flight and the time from approved to landed. - Docs and tests: the README describes the parallel queue and
queue.depth.test/merge-queue-parallel.test.tscovers pipelining, the prefix search, the slot reserve and the sweep.test/merge-queue.test.tspins that the single-slot behaviour is unchanged.
What to look at
The slot accounting in src/checks/queue.ts. Extra gates must never starve pull checks, and the two-slot reserve is what guarantees that.
Not done
- I didn't merge
maininto the branch. It is 10 commits behind andgit merge-treereports no conflicts, so a merge can wait until the pull is scheduled. - I didn't name a pillar. That decision is open for a person on #4053.
How to try it
Run bun test test/merge-queue.test.ts test/merge-queue-parallel.test.ts. On the preview, approve several pulls and watch /boards: with spare capacity you'll see more than one gate in flight per line.
🤖 Generated with Claude Code
The merge queue now gates candidates in parallel and finds which candidate made a red by gating all of its prefixes at once (#4053). Extra gates beyond a line's first use free check slots, and two slots (PULL_RESERVE) are always kept for pull checks. The queue.depth setting controls how far the queue looks ahead. /boards and the terminal Flow show the gates in flight and the time from approved to landed. The green sweep reads every open pull, not just the newest fifty, so older approved pulls get batched instead of gated one by one.
This update merges main. The only conflict was in src/checks/service.ts, where the branch's PULL_RESERVE and main's LAND_SETTLE_MS (#4061) were added at the same spot. They are unrelated and both are kept. Main's notBefore filter on queued checks merged on its own, next to the branch's spare-slot accounting, so a land's check that hasn't settled yet is still not picked.
The type check passes. The merge-queue, checks, check-*, landing and boards suites pass (343 tests, 0 failing). The forge runs the full check on this push.
🤖 Generated with Claude Code
Approved. The pipeline (speculative next candidate, prefix search, slot reserve via spareSlots/PULL_RESERVE) is sound and tested with counted waves rather than timers. Web change is two extra columns in the existing .table inside .table-scroll, no new markup pattern, works without JS, and the terminal Flow dialog and MCP carry the same numbers. /boards answers 404 to an anonymous curl on the preview, so I could not see the table rendered; I judged it from source and the /boards test. Two things I would do next: (1) the terminal repository column shrank from 30 to 15 characters, so most owner/name pairs are now cut; drop a lower-ranked column or abbreviate instead of truncating the name people look for. (2) Web queue cells use .mono; DESIGN.md says numeric cells take .num (right-aligned), so switch the new numeric columns (and the existing ones) to .num. Also the README still says the web copy says bisected in places; keep wording consistent with searched.
The merge queue now runs gates in parallel when check slots are free: the next candidate gates on top of the one at the gate, and a red candidate's search gates several prefixes at once. With no slot spare it behaves exactly as before: one gate at a time, halving a red candidate. The type check is clean and the new and related tests pass.
Speculative pipeline (
src/checks/queue.ts,drain/launch/settleHead)depthcandidates.onTip).MAX_CANDIDATE): pulls queued together under eight form one candidate, as before.Red search (
judged)Check slots (
src/checks/service.ts)spareSlots()). Pull checks are scheduled up to capacity minus the extra gates, so they always keep at leastPULL_RESERVE= 2 slots.queue.depth(FORGE_QUEUE_DEPTH, 1–4, default 2), read on every sweep.Visibility
readQueuesaddsgates(gates in flight) andapprovedToLanded: the median whole minutes from the last approval to landing, over the last hour's landings. Both show on/boardsand in the MCPflowtool.queueWordsaddsin N gateswhen more than one gate is in flight.Tests
test/merge-queue-parallel.test.ts. Its stand-in gate is held until a whole wave has started, so "at once" is counted, not timed. It covers:- 16 green pulls: two gates open at once, 16 land, 2 gate runs. - 10 pulls with one red, in three waves (2 gates, then 7 prefixes, then 1): the 9 good land and the culprit is handed back. - A red second candidate behind a green first. -
/boardsshowing 12 min from approval to landing. - Two held gates against the real service: an extra gate holds a slot, and at capacity 2 there is never a second gate.test/merge-queue.test.ts, the two MCPtoContainEquallines now includegatesandapprovedToLanded. Those assertions pin the row's exact shape, so this is the test extended for the new fields, not a code fix.Against the issue's targets
Not done: the bisection does not also gate "everything except the culprit's segment" ahead of time; that is the one gate it would take to reach two gate-times in total. The terminal Flow columns are unchanged.
To try it: set Settings → Capacity → Checks at once to 5 or more, approve ten or more pulls in an autonomous repository, and watch
/boards→ Merge queue and the[queue] gating …log lines.🤖 Generated with Claude Code
Closes #4053