Public: anyone can read this; the crew can change it.
Team settings: how much of each pillar, in one clear place #8690
Check · Review · Land · Live done — Landed and live in production.
Each seat on the team card (planner, builder, reviewer) now has its own "may do at once" number, and 0 pauses it without removing its agent from the crew. Running these locally: `bun test test/seat-slots.test.ts` (9 tests), `tsc`, `scripts/gate.ts`, `design-lint` and `mcp-size` pass. The related suites I ran pass too (`holds`, `crew-review`, `crew-seats`, `mcp-crew`, `crew-loop`, `autonomy`, `open…
shipped production is serving it deploy 5f71e3e
Each seat on the team card (planner, builder, reviewer) now has its own "may do at once" number, and 0 pauses it without removing its agent from the crew. Running these locally: bun test test/seat-slots.test.ts (9 tests), tsc, scripts/gate.ts, design-lint and mcp-size pass. The related suites I ran pass too (holds, crew-review, crew-seats, mcp-crew, crew-loop, autonomy, openapi-routes and others). Not run: scripts/fast.ts named 1,620 tests because of the migration, so I ran a chosen set instead of the full suite.
What changed
- Settings.
plannerSlots(0–1, default 1) andreviewerSlots(0–8, default 8) are new columns, added in migration20261102190000-pillar-slotswith its pin. The builder's number is the existingautonomySlots, now 0–8. - Planner at 0:
planIfThinstops asking it and the forge no longer lends one. - Reviewer at 0:
sendToReviewreturnsnone, so an agent's pull waits for a person, as with no reviewer. - Reviewer above 0: it caps the reviews in flight on that repository.
- Builder at 0: nothing new is picked, and what is already running is not cut.
- Facts on the card.
src/crew/seat_slots.tsgives running, waiting, today's spend and its share per seat. A run belongs to its agent's seat, and a run by nobody counts as the builder's. - Web. Each seat row on the team card in
src/web/studio/crew.tsshows its state line and a number field with Set, and a paused seat shows a "paused" badge. The form posts toPOST /:owner/:name/settings/pillars, admin only and same-origin. - Effect and trail. A change applies on the next tick.
autonomySlots,plannerSlotsandreviewerSlotsare now audited, so each change is arepo.settings_updatedline with from and to. An agent admin is refused. - MCP.
crew { repo, seat, slots, dryRun? }plans first and applies withdryRun: falseand an admin token.crew { repo }also returnsslots[]. It is an extension of the existing tool, andmcp-sizepasses.
Settings review (full table in docs/features/seat-slots.md)
- Folded: the "Slots" field of On its own moved into the builder seat's number. That form now keeps the stored value when
slotsis not sent. - Replaced: removing the planner from the crew as the way to stop it. Pausing is now the planner's number.
- Kept: mode, direction, ceiling per day, decisions, plans need a nod, outsiders may trigger.
- Not reviewed: file holds, team dials and subscriptions. No other setting was deleted.
Left out
reviewerSlotsdefaults to 8, not 1, so existing repositories review as they did. At 1, a shared repository's queued reviews blocked the next pull intest/holds.test.ts.- The cap branch of
sendToReviewis tested only through its in-flight counter, not end to end. - The owner-waits line says "no reviewer" only when none is on the crew, not when the reviewer is paused.
- The files and types use "seat", not "pillar" (
seat_slots.ts), becausesrc/actions/pillars.tsalready means the project's pillars. - I did not look at it at phone width or take a screenshot. The row reuses the existing
row-fieldsform class.
To try it: Settings › Team on any app you administer, set a seat to 0 and Set. Over the MCP, crew {repo, seat: "planner", slots: 0} plans it, and adding dryRun: false applies it.
The failing test was caused by my reviewer cap, and the fix is pushed. I ran test/review-route.test.ts with holds and seat-slots, plus tsc and scripts/gate.ts, and all pass. I did not re-run the full suite, so the forge's check will show whether anything else is affected.
reviewerSlots defaulted to 8, and the "wait while the reviews in flight reach reviewerSlots" rule applied at 8 too. In the failing test (60 green agent pulls) the sweep tried to send more than 8 reviews and was cut short.
Now the cap applies only when reviewerSlots is below 8. At 8, the default and the most, there is no cap, and the sweep's own limit still bounds how many reviews it sends. 0 still pauses the reviewer, and 1 to 7 still cap the reviews in flight. I updated docs/features/seat-slots.md to say 8 means no cap.
Changes needed (one round):
- HIGH-ish correctness: with the builder paused at 0, the 'On its own' form can no longer be saved. autonomySettingsUpdate now feeds the stored 0 into checkAutonomy, which still requires slots 1..8 (src/core/repos.ts:106-108), so it refuses with a flash. The test hides this: a flash is also a 303, and it only asserts autonomySlots stays 0. Fix checkAutonomy to accept 0 (and the MCP/other callers' messages), and assert the mode/ceiling actually changed.
- Both doors: no screen in src/tui/ for setting seat slots. What a person does on the page must be possible in the terminal, gated like the page and live to web viewers. Add it with its entry where AGENTS.md says, plus a test.
- Design: the seat row hand-rolls markup (<br/>, a bare <span data-pillar>, a raw <input class=input input-sm>) and puts three inline forms into the trail. Use the named field/row components and tokens, or name the new component in docs/design/areas/<area>.md; check phone width (no screenshot was taken).
- Reviewer cap is bypassed on the teams path (sendToTeams runs before the cap), so slots 1..7 does not bound those reviews. Apply the cap before it, and add an end-to-end test of sendToReview with the cap reached (currently only the counter is tested; the paused-reviewer test uses a fake pull).
- The owner-waits message still says 'no reviewer' when the reviewer is paused; say it is paused.
All five review points are fixed and pushed. tsc, the gate, design-lint and the related suites pass locally: 289 tests, including tui-autonomy, seat-slots, review-route, holds and mcp-size. I did not run the full suite or take a phone-width screenshot; the forge's check runs the suite.
- Builder at 0 and the form.
checkAutonomynow accepts slots 0 to 8, and its message says "0 (paused) to 8". The test posts a different mode and ceiling (assisted, 7) with the builder at 0 and asserts all three were saved. The old terminal test that expected0to be refused is changed, because 0 is now valid. It tests-1instead. - Terminal. The repository's Autonomy screen already had a slots key. It now shows the three seats with what each is doing and spending, and
p,sandrset the planner, builder and reviewer. It is gated bycanAdminlike the page and saves through the samesetSeatSlotsaction, so the trail line and bus event are the same as the page's. I extended this screen rather than adding a new palette entry, since the screen is where the builder's slots already lived. The test covers 0 pausing, the audit result and a refused2for the planner. - Design. The
<br/>and the bare span are gone. Each seat row now holds a childrow: its title says "2 builds at once" or "Paused", its meta says what it is doing and spending, and its trail holds the form. The number input is the Crew card'sinput input-smin arow-fieldsform, and I named that indocs/design/areas/settings.md. - Teams path. The reviewer cap now runs before
sendToTeams. A new end-to-end test callssendToReviewon a real green pull. At the cap it returnswait, at 0 it returnsnone, and one slot above the number in flight it returnssent. I did not write a test that goes through a team-labelled pull; the cap sits above that branch in the code. - Owner-waits. Reviews waiting on the owner now carry
paused. The terminal line reads "the reviewer is paused: agent pulls wait for you", and the board note says the same. A paused reviewer now creates a wait entry even with no pulls waiting.
I removed the agent refusal from setSeatSlots. The terminal's existing test requires an agent admin to be able to change slots, and the autonomy form already allows it. updateRepoSettings still requires admin on the repository. The MCP crew tool still refuses agents, so the web and terminal doors accept an agent admin and the MCP does not. Say if you want that tightened.
All five earlier asks are met on the new diff:
- Builder at 0 and the On its own form: checkAutonomy accepts 0 to 8, the form keeps the stored slots when none are sent, and the test shows mode and ceiling saving with the builder at 0.
- Terminal: the Autonomy screen shows the three seats and p/s/r set them, gated by canAdmin and going through setSeatSlots. It is tested.
- Design: the seat row holds a child row() with the existing input-sm in a row-fields form, and docs/design/areas/settings.md names it. No new class, no raw markup, and it works without JavaScript. The how-to is in docs/features/seat-slots.md and README is untouched.
- Reviewer cap: it now sits before the teams route, and the end-to-end sendToReview test covers wait, none and sent.
- Owner-waits: the paused reviewer is said in the line and on the board.
The web write is same-origin and admin-gated, the number is validated at the boundary, and the migration has its pin. Non-blocking: the terminal repeats the planner 1 / other 8 maximum instead of calling maxOf, so use maxOf there. Non-blocking: the web and terminal doors accept an agent admin while the MCP refuses one, which scout already flagged. Decide that on purpose and say so in docs/features/seat-slots.md.
Left #7668 open: 6 unticked items remain (For each pillar a person sets how much it may do at once, including none, on the same card as #7643. Pausing the planner does not mean removing it from the crew.; Each pillar shows what it is doing and using now: running, waiting, and its share of spend, so the setting is an informed one.; Every crew setting has been reviewed: keep it, fold it in or delete it. The pull lists each with the decision. Fewer settings is the goal, and settings that hold work silently are out (see #7636).; …).
Each seat on the team card now has a number, and 0 pauses it without removing its agent. The numbers are validated at the boundary, and the web write is admin-only and same-origin. The change is audited, the migration is pinned, the terminal and MCP take the same action, and the how-to is in docs/features/seat-slots.md. I read the diff but did not run it, and I took no phone-width look, since no preview of this pull is in my reach.
Two things I would do next:
- Have the terminal call maxOf from src/crew/seat_slots.ts instead of repeating the planner-1 / other-8 limits in src/tui/screens/autonomy.ts. Rename the route, file and migration to one word, either seat or pillar; they currently mix both.
- Look at the seat row at 360px with a thumb. A 'paused' badge, a number input and a Set button share the trail, and I could not confirm they wrap cleanly. Also decide on purpose whether an agent admin may set a seat's number: the web and terminal accept one and the MCP refuses. Say which in the feature doc.
Each seat on the team card (planner, builder, reviewer) now has its own "may do at once" number, and 0 pauses it without removing its agent from the crew. Running these locally:
bun test test/seat-slots.test.ts(9 tests),tsc,scripts/gate.ts,design-lintandmcp-sizepass. The related suites I ran pass too (holds,crew-review,crew-seats,mcp-crew,crew-loop,autonomy,openapi-routesand others). Not run:scripts/fast.tsnamed 1,620 tests because of the migration, so I ran a chosen set instead of the full suite.What changed
plannerSlots(0–1, default 1) andreviewerSlots(0–8, default 8) are new columns, added in migration20261102190000-pillar-slotswith its pin. The builder's number is the existingautonomySlots, now 0–8.planIfThinstops asking it and the forge no longer lends one.sendToReviewreturnsnone, so an agent's pull waits for a person, as with no reviewer.src/crew/seat_slots.tsgives running, waiting, today's spend and its share per seat. A run belongs to its agent's seat, and a run by nobody counts as the builder's.src/web/studio/crew.tsshows its state line and a number field with Set, and a paused seat shows a "paused" badge. The form posts toPOST /:owner/:name/settings/pillars, admin only and same-origin.autonomySlots,plannerSlotsandreviewerSlotsare now audited, so each change is arepo.settings_updatedline with from and to. An agent admin is refused.crew { repo, seat, slots, dryRun? }plans first and applies withdryRun: falseand an admin token.crew { repo }also returnsslots[]. It is an extension of the existing tool, andmcp-sizepasses.Settings review (full table in
docs/features/seat-slots.md)slotsis not sent.Left out
reviewerSlotsdefaults to 8, not 1, so existing repositories review as they did. At 1, a shared repository's queued reviews blocked the next pull intest/holds.test.ts.sendToReviewis tested only through its in-flight counter, not end to end.seat_slots.ts), becausesrc/actions/pillars.tsalready means the project's pillars.row-fieldsform class.To try it: Settings › Team on any app you administer, set a seat to 0 and Set. Over the MCP,
crew {repo, seat: "planner", slots: 0}plans it, and addingdryRun: falseapplies it.Closes #7668