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

Team settings: how much of each pillar, in one clear place #8690

Merged opened this in answer to

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

Merged

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

opened this

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) and reviewerSlots (0–8, default 8) are new columns, added in migration 20261102190000-pillar-slots with its pin. The builder's number is the existing autonomySlots, now 0–8.
  • Planner at 0: planIfThin stops asking it and the forge no longer lends one.
  • Reviewer at 0: sendToReview returns none, 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.ts gives 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.ts shows its state line and a number field with Set, and a paused seat shows a "paused" badge. The form posts to POST /:owner/:name/settings/pillars, admin only and same-origin.
  • Effect and trail. A change applies on the next tick. autonomySlots, plannerSlots and reviewerSlots are now audited, so each change is a repo.settings_updated line with from and to. An agent admin is refused.
  • MCP. crew { repo, seat, slots, dryRun? } plans first and applies with dryRun: false and an admin token. crew { repo } also returns slots[]. It is an extension of the existing tool, and mcp-size passes.

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 slots is 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

  • reviewerSlots defaults to 8, not 1, so existing repositories review as they did. At 1, a shared repository's queued reviews blocked the next pull in test/holds.test.ts.
  • The cap branch of sendToReview is 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), because src/actions/pillars.ts already means the project's pillars.
  • I did not look at it at phone width or take a screenshot. The row reuses the existing row-fields form 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.

Closes #7668

commented

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) and reviewerSlots (0–8, default 8) are new columns, added in migration 20261102190000-pillar-slots with its pin. The builder's number is the existing autonomySlots, now 0–8.
  • Planner at 0: planIfThin stops asking it and the forge no longer lends one.
  • Reviewer at 0: sendToReview returns none, 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.ts gives 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.ts shows its state line and a number field with Set, and a paused seat shows a "paused" badge. The form posts to POST /:owner/:name/settings/pillars, admin only and same-origin.
  • Effect and trail. A change applies on the next tick. autonomySlots, plannerSlots and reviewerSlots are now audited, so each change is a repo.settings_updated line with from and to. An agent admin is refused.
  • MCP. crew { repo, seat, slots, dryRun? } plans first and applies with dryRun: false and an admin token. crew { repo } also returns slots[]. It is an extension of the existing tool, and mcp-size passes.

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 slots is 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

  • reviewerSlots defaults to 8, not 1, so existing repositories review as they did. At 1, a shared repository's queued reviews blocked the next pull in test/holds.test.ts.
  • The cap branch of sendToReview is 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), because src/actions/pillars.ts already means the project's pillars.
  • I did not look at it at phone width or take a screenshot. The row reuses the existing row-fields form 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.

commented

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 requested — before the latest push

Changes needed (one round):

  1. 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.
  2. 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.
  3. 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).
  4. 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).
  5. The owner-waits message still says 'no reviewer' when the reviewer is paused; say it is paused.
commented

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.

  1. Builder at 0 and the form. checkAutonomy now 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 expected 0 to be refused is changed, because 0 is now valid. It tests -1 instead.
  2. 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, s and r set the planner, builder and reviewer. It is gated by canAdmin like the page and saves through the same setSeatSlots action, 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 refused 2 for the planner.
  3. Design. The <br/> and the bare span are gone. Each seat row now holds a child row: 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's input input-sm in a row-fields form, and I named that in docs/design/areas/settings.md.
  4. Teams path. The reviewer cap now runs before sendToTeams. A new end-to-end test calls sendToReview on a real green pull. At the cap it returns wait, at 0 it returns none, and one slot above the number in flight it returns sent. I did not write a test that goes through a team-labelled pull; the cap sits above that branch in the code.
  5. 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.

Approved

All five earlier asks are met on the new diff:

  1. 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.
  2. Terminal: the Autonomy screen shows the three seats and p/s/r set them, gated by canAdmin and going through setSeatSlots. It is tested.
  3. 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.
  4. Reviewer cap: it now sits before the teams route, and the end-to-end sendToReview test covers wait, none and sent.
  5. 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.

commented

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).; …).

Approved

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:

  1. 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.
  2. 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.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs