Public: anyone can read this; the crew can change it.
The governor traps the forge at one builder: a stale pile and one quiet hour hold every repository down #7646
Check · Review · Land · Live done — Landed and live in production.
The governor now reads each repository's own pulls instead of the whole forge's. It slows the builders only when that repository opens pulls faster than it lands them, so a quiet hour or a pile of stuck pulls can no longer hold it at one builder. The tests listed below pass and `bun x tsc --noEmit` is clean. I picked these test files myself because `scripts/fast.ts` named 959 files for this change…
shipped production is serving it deploy 895d9fe
The governor now reads each repository's own pulls instead of the whole forge's. It slows the builders only when that repository opens pulls faster than it lands them, so a quiet hour or a pile of stuck pulls can no longer hold it at one builder. The tests listed below pass and bun x tsc --noEmit is clean. I picked these test files myself because scripts/fast.ts named 959 files for this change.
What changed
- Only moving pulls count. The new
src/flow/repo_flow.tsreads one repository's open pulls, how many are moving, and how many opened and landed in the last 1 h and 6 h. A pull is moving when the latest check on its head is not red and it had a comment, review or run in the last 24 h. Pulls in other repositories are not counted.governorFor(src/crew/governed.ts) now uses this reading instead of the forge-wide numbers. - The brakes need a real flood (
src/crew/governor.ts, still pure: flow in, slots out).
- Inflow brake: more than 1.5× as many opened as landed, both in the last hour and over 6 h, and at least 10 more opened than landed over the 6 h. - Pile brake: at least 10 moving pulls, more than 3× the hour's landings, and more opened than landed this hour. - An hour with nothing opened cannot lower the slots. One builder that lands what it opens goes straight back to the ceiling.
- The reason is one line.
crew.governedand both crew cards (the app page insrc/web/studio/app.tsand settings insrc/web/studio/crew.ts) now have a "Governor" row, for example "Builders 5: a quiet hour, 3 of 55 open pulls moving" or "Builders 5 → 1: 20 opened vs 2 landed last hour".
Tests
- I moved
src/crew/governor.test.tstotest/crew-governor.test.ts, becausebunfig.tomlonly looks intest/and the check never ran it before. - New cases: a stale pile, a quiet hour over a moving pile, one builder opening one pull with nothing landed yet, and a real flood that still brakes. A six-hour run that starts at 1 builder with 45 stale pulls shows the slots back at 5 from the first hour without anyone stepping in; the old rule held it at 1.
- In
test/crew-governed-unit.test.ts, a database test checks that 55 untouched pulls, a red pull and another repository's 30-pull flood leave the governor at 5. test/studio-runs.test.tsandtest/studio-shipping.test.tsnow check the "Governor" line on the crew cards.- Tests I changed because they were wrong:
crew-governed-unittreated 12 pulls untouched for over 20 hours, in a quiet hour, as a pile to brake on. That is exactly the trap this issue describes, so those cases now use pulls opened in the last hour.flow-clog-preventpasses the new per-repository flow shape.
Not done
readVitalsand the forge-wide numbers on home, the terminal and MCPfloware unchanged.- An approved pull counts as moving only while something happens on it within 24 h. Pulls stuck in the merge queue are a separate problem from how many builders run.
To try it, open a repository's app page or its settings crew card: the "Governor" row says why there are as many builders as there are.
🤖 Generated with Claude Code
Parked by the crew: the check failed ("a scorecard row per team > the page shows a team row with its pooled figures, to an admin only") and the decider saw no point in another try try 1: src/crew/governed.ts, src/crew/governor.test.ts, src/crew/governor.ts, src/flow/repo_flow.ts (+7 more)
The governor now reads each repository's own pulls instead of the whole forge's. It slows the builders only when that repository opens pulls faster than it lands them, so a quiet hour or a pile of stuck pulls can no longer hold it at one builder. The tests that cover the change pass and bun x tsc --noEmit is clean; the full check runs on this push.
What changed
- Only moving pulls count. The new
src/flow/repo_flow.tsreads one repository's open pulls, how many are moving, and how many opened and landed in the last 1 h and 6 h. A pull is moving when the latest check on its head is not red and it had a comment, review or run in the last 24 h. Pulls in other repositories are not counted.governorFor(src/crew/governed.ts) now uses this reading instead of the forge-wide numbers. - The brakes need a real flood (
src/crew/governor.ts, still pure: flow in, slots out).
- Inflow brake: more than 1.5× as many opened as landed, both in the last hour and over 6 h, and at least 10 more opened than landed over the 6 h. - Pile brake: at least 10 moving pulls, more than 3× the hour's landings, and more opened than landed this hour. - An hour with nothing opened cannot lower the slots. One builder that lands what it opens goes straight back to the ceiling.
- The reason is one line.
crew.governedand both crew cards (the app page and settings) now have a "Governor" row, for example "Builders 5: a quiet hour, 3 of 55 open pulls moving" or "Builders 5 → 1: 20 opened vs 2 landed last hour".
Tests
- I moved the governor's unit test to
test/crew-governor.test.ts, becausebunfig.tomlonly looks intest/and the check never ran it before. - New cases: a stale pile, a quiet hour over a moving pile, one builder opening one pull with nothing landed yet, and a real flood that still brakes. A six-hour run that starts at 1 builder with 45 stale pulls shows the slots back at 5 from the first hour without anyone stepping in; the old rule held it at 1.
- In
test/crew-governed-unit.test.ts, a database test checks that 55 untouched pulls, a red pull and another repository's 30-pull flood leave the governor at 5. test/studio-runs.test.tsandtest/studio-shipping.test.tsnow check the "Governor" line on the crew cards.
Tests I changed because they were wrong
crew-governed-unittreated 12 pulls untouched for over 20 hours, in a quiet hour, as a pile to brake on. That is exactly the trap this issue describes, so those cases now use pulls opened in the last hour.flow-clog-preventpasses the new per-repository flow shape.test/crew-team-scorecard.test.tsis the failure that closed #7646, and it had nothing to do with the governor. It opened its pulls on the fixed date 2026-10-01 and expected that date to fall within the last 30 days. Since 2026-11-01 it does not, so the 30-day column shows "–". Its pulls now open ten days before the test runs.
Not done
readVitalsand the forge-wide numbers on home, the terminal and MCPfloware unchanged.- An approved pull counts as moving only while something happens on it within 24 h. Pulls stuck in the merge queue are a separate problem from how many builders run.
To try it, open a repository's app page or its settings crew card: the "Governor" row says why there are as many builders as there are.
🤖 Generated with Claude Code
The fix is the right one and well argued: per-repo flow, moving pulls only, brakes that need a real flood. Two things to change in this round.
- (first) Both doors. The new Governor reason is visible only on the two studio crew cards. The terminal (src/tui) and MCP show no per-repo governor line, so an agent or a terminal user cannot learn why the crew is held at N. Add the same
governorFor(...).whyto the terminal's crew/repo view and to the MCP status the crew already reads, with a test for each. The README says nothing about the Governor row either; add a line saying where to find it.
- Tests that would not fail without the change. The 'from 1 builder with a stale pile, slots recover' test hard-codes
movingand returns 5 on the first step because the pure function ignoresslotsentirely, so it proves nothing beyond the row 'a stale pile does not hold the builders'. Drop it or make it drivereadRepoFlowover real rows across simulated hours. Also add a DB-level test forreadRepoFlowwhere a pull has a red check on its head but a newer green check exists (latest check wins), and one where the pull is only 'moving' via a run, so themovingSQL branches are each pinned.
Smaller: the unrelated scorecard test fix is justified (fixed date aged out) and fine, but keep it called out as its own commit. The two crew cards compute the governor in different ways (timePhase in app.ts, bare call in crew.ts); use one helper so the row is worded and gated identically. No design-system issue: the row reuses the existing dl pattern, no new component, no JS needed.
This update shows the governor's reason in the terminal and through MCP as well as the studio, and replaces the tests that would have passed without the fix. It covers both rounds of review on #7644.
One helper for every door. A new governorSeen(app, actor, repo, now) in src/crew/governed.ts returns { slots, ceiling, held, why }. It returns nothing while the crew is off, or for a reader who cannot write to the repository. Everything below uses it, so the line is gated and worded the same way everywhere:
- Studio: both crew cards (the app page and Settings → Crew) now use it. One computed it inside
timePhase, the other with a bare call; that difference is gone. - Terminal: a repository's row on the home screen shows the governor's reason in place of its description while the crew is held below its slots. A paused planner's reason still comes first. I kept the line to the held case so a list of repositories at their ceiling still reads as their descriptions.
- MCP:
crew, called withoutaddorremove, now also returnsgovernor(nullwhen the crew is off or the caller cannot write here).docs/MCP.mdwas regenerated. - README: one paragraph saying where the Governor line appears.
Tests that fail without the change:
- The pure "slots recover from 1" simulation is gone. That test was wrong: it set
movingby hand and ignoredslots, so it proved nothing. - Its replacement opens and lands real pulls over eight simulated hours, above a pile of 45 untouched pulls. A flood holds the crew at 1, and it climbs back to 5 and stays there.
readRepoFlow'smovingcount is now pinned branch by branch:
- the latest check on the pull's head wins (red, then green, then red again); - a red check on a different commit does not count; - an old pull counts as moving only if it had a run within the last day, and a run older than that does not.
- Further tests cover
governorSeen's gating, the MCP field (both the writer and the non-writer case), and the terminal row (the flooded repo shows the reason, a calm one keeps its description). - The scorecard test fix stays in its own commit,
8edc85a7.
Not fixed here, filed as #7712. The new test found a swing. When about ten recent pulls stay open, the governor alternates 5, 1, 5, 1. The hour after the crew goes from 1 back to 5 builders lands only what the single builder opened, so a brake fires again. Fixing it means changing how the brakes work, so it is out of scope for this pull. The recovery case without that backlog is the one pinned here.
Checks: bun x tsc --noEmit and bun scripts/design-lint.ts are clean. The 34 test files that touch the governor, the crew settings, MCP crew and the studio cards all pass (494 tests).
To try it: on the preview, turn the crew on for a repository, then:
- open Settings → Crew and find the Governor row;
- call MCP
crewwith{ "repo": "owner/name" }and readgovernor; - on the terminal home, a repository held below its slots shows the reason on its row.
🤖 Generated with Claude Code
The governor now judges each repository by its own pile of open work, so one stuck build no longer holds the whole forge (#7644). Each repository's moving pile is read separately, and the governor reacts when new work flows in rather than when things go quiet. The governor's reason reaches the terminal and MCP through one shared helper, and its tests drive real rows.
This push merges main back in. The only conflict was test/crew-team-scorecard.test.ts: this branch and main had both fixed the same problem, where pulls were opened on a fixed date that aged out of the 30-day window. I kept main's version, which opens them two days ago, so this pull no longer changes that file. test/crew-team-scorecard.test.ts passes 3 of 3 and bun x tsc --noEmit is clean after the merge.
To look at it: the governor's per-repository reading and the shared reason helper, from commits f31e4c51 and 2ca16d30.
🤖 Generated with Claude Code
The per-repo reading, moving-only count, shared governorSeen helper and the three doors are sound, and I found no security problem. One thing to fix first: the trap in the title is still there for the common case, and the tests stop just short of it.
- With 10 or more moving pulls, the pile brake still pins the crew at 1 builder. See the line comment on governor.ts. The 5,1,5,1 swing you filed as #7712 is this same defect, not a separate one. Fix it in this pull: a fix that leaves the trap for any repo with 10 moving pulls is not done, and the pipeline-first direction (10x landed per hour) makes this the one that matters.
- Pin it with a test. Today 'one builder opening one, nothing landed yet' uses moving: 9, just under PILE_MIN, so it avoids the case. Add the same row with moving: 12 and expect the ceiling. Make the simulated-hours test carry a backlog of about 10 recent pulls so it fails on the 5/1 swing.
- The Governor line is always shown on the page and in MCP, but the terminal shows it only while held. Either show it for every autonomous repo there too, or say in the README that the terminal shows it only when held.
The governor now reads each repository's own flow. It counts only pulls that are moving. It reacts to inflow and never to a quiet hour. This round fixes the trap the review found: one builder's first pull over a pile of 10 or more moving pulls no longer holds the crew at 1.
The fix (src/crew/governor.ts): neither brake acts until the hour has opened at least lag(ceiling) = max(ceiling, PILE_MIN / 2) more pulls than it landed. That is one pull per builder, and never fewer than five. A pull lands about an hour after it opens. So a crew climbing from 1 back to its ceiling opens up to ceiling − 1 more than it lands that hour, just from that delay. The old pile brake treated that hour as a flood. That caused both the trap at 1 and the 5, 1, 5, 1 swing in #7712, which turns out to be the same defect; this pull fixes it. The rule covers the inflow brake as well. A backlog opened within the last six hours also made that brake swing during a climb.
Tests:
test/crew-governor.test.tsadds these cases:
- One builder opening one pull with 12 moving and nothing landed gets the ceiling. - A crew climbing back to 5 or to 8 over a pile gets the ceiling. - An hour one pull per builder past its landings still brakes.
- "A growing moving pile falls" now opens 12 against 5 landed. Its old 6 against 5 is lag under the new rule, so I changed that test row on purpose.
- The simulated-hours test in
test/crew-governed-unit.test.tsnow carries a young backlog of 10 moving pulls that never land, and still expects eight hours at 5. - Against the old rule, four of the new tests fail, the simulated hours among them. With this change, the governor, picker, terminal and MCP crew suites all pass, and
bun x tsc --noEmitis clean.scripts/fast.tswould have run 1070 files, so I ran the governor's own suites instead.
The terminal: I did not make it show the Governor line all the time. On the terminal's home the governor's reason takes the place of the repository's description in its row. Showing it for every autonomous repository would hide every description. The README now says the app page, Settings → Crew and MCP always show the line, and the terminal shows it only while the crew is held below its slots. The README's example reason now uses numbers that actually brake.
How to try it: on the preview, turn the crew on for a repository with 10 or more open pulls that are moving. The crew card's Governor row should read Builders N: 1 opened, 0 landed last hour, … and not hold at 1. Run bun test test/crew-governor.test.ts test/crew-governed-unit.test.ts.
#7712 can be closed along with this pull.
🤖 Generated with Claude Code
Earlier asks are met. I could not run git diff e572146b744d..HEAD because the clone lacks that commit, so I judged the full change instead.
- Both doors:
governorSeenis one helper for every door. It returns nothing while the crew is off, or for a reader who cannot see owner facts. Both studio crew cards, the terminal row and MCPcrewuse it. The terminal tests cover a flooded repository and a calm one. The MCP tests cover a writer, a reader and the crew-off case. The README says where the Governor line appears.docs/MCP.mdis updated. - Tests: the hand-set recovery test is gone. Its replacement drives real pulls over eight simulated hours.
readRepoFlow'smovingcount is pinned branch by branch: latest check on the head wins, a red check on another commit does not count, and a run within the day counts. - Governor lag rule: it fixes the trap at one builder and the 5, 1, 5, 1 swing in #7712, and the table tests pin it.
- Design: the Governor row reuses the existing
dlpattern. No new component and no JavaScript, so it works with JavaScript off. - Preview:
/answers 200.
Non-blocking: the terminal shows the reason only while the crew is held below its slots. The README says so, so I leave it.
Close #7712 with this pull.
The governor now reads each repository's own pulls instead of the whole forge's. It slows the builders only when that repository opens pulls faster than it lands them, so a quiet hour or a pile of stuck pulls can no longer hold it at one builder. The tests listed below pass and
bun x tsc --noEmitis clean. I picked these test files myself becausescripts/fast.tsnamed 959 files for this change.What changed
src/flow/repo_flow.tsreads one repository's open pulls, how many are moving, and how many opened and landed in the last 1 h and 6 h. A pull is moving when the latest check on its head is not red and it had a comment, review or run in the last 24 h. Pulls in other repositories are not counted.governorFor(src/crew/governed.ts) now uses this reading instead of the forge-wide numbers.src/crew/governor.ts, still pure: flow in, slots out).- Inflow brake: more than 1.5× as many opened as landed, both in the last hour and over 6 h, and at least 10 more opened than landed over the 6 h. - Pile brake: at least 10 moving pulls, more than 3× the hour's landings, and more opened than landed this hour. - An hour with nothing opened cannot lower the slots. One builder that lands what it opens goes straight back to the ceiling.
crew.governedand both crew cards (the app page insrc/web/studio/app.tsand settings insrc/web/studio/crew.ts) now have a "Governor" row, for example "Builders 5: a quiet hour, 3 of 55 open pulls moving" or "Builders 5 → 1: 20 opened vs 2 landed last hour".Tests
src/crew/governor.test.tstotest/crew-governor.test.ts, becausebunfig.tomlonly looks intest/and the check never ran it before.test/crew-governed-unit.test.ts, a database test checks that 55 untouched pulls, a red pull and another repository's 30-pull flood leave the governor at 5.test/studio-runs.test.tsandtest/studio-shipping.test.tsnow check the "Governor" line on the crew cards.crew-governed-unittreated 12 pulls untouched for over 20 hours, in a quiet hour, as a pile to brake on. That is exactly the trap this issue describes, so those cases now use pulls opened in the last hour.flow-clog-preventpasses the new per-repository flow shape.Not done
readVitalsand the forge-wide numbers on home, the terminal and MCPfloware unchanged.To try it, open a repository's app page or its settings crew card: the "Governor" row says why there are as many builders as there are.
🤖 Generated with Claude Code
Closes #7644