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

The crew has a maintainer role: an agent can be made one, the crew page names it, and it has no path to merge or push main #11216

Merged opened this

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

Merged

`maintainer` is now a `CrewRole`: an agent can be made one, and the team card on a repository's Settings page lists it as "Maintainers". The new tests pass, along with `tsc`, the gate, the design lint, `test/mcp-size.test.ts` and 151 neighbouring crew, agent, autonomy and MCP tests. I did not run the whole suite, because `scripts/fast.ts` could not narrow it. I did not call `keep_plan` (not availa…

shipped production is serving it deploy f21b126

opened this

maintainer is now a CrewRole: an agent can be made one, and the team card on a repository's Settings page lists it as "Maintainers". The new tests pass, along with tsc, the gate, the design lint, test/mcp-size.test.ts and 151 neighbouring crew, agent, autonomy and MCP tests. I did not run the whole suite, because scripts/fast.ts could not narrow it. I did not call keep_plan (not available here) and did not check the page in a browser.

What changed

  • Role: CrewRole and CREW_ROLES in src/core/agents.ts accept maintainer. Roles are a single list, so making agents on the page, over the API and through the MCP agent tool all take the same word. The agent form's "Jobs" checkboxes offer it, and I updated the invalid_roles text and the MCP tool doc to say so.
  • Team card: src/web/studio/crew.ts adds a Maintainers row with an add picker for agents that declare the role, and a remove button for its members.
  • Not a pillar: I kept maintainer out of PILLAR_ROLES. It has no slot count and no "empty seat" warning, and checkSeatSlots refuses it, so seatsOf and the MCP crew tool output are unchanged.
  • Picker: outsideSeat in src/crew/dispatch.ts already treated a maintainer as outside every seat for issues. It did let a maintainer take feedback work on a pull, so I closed that: a maintainer is now outside it, as a planner is.
  • Refusals: merge, land and push were already closed to every agent; maintainers inherit that, and the new tests pin it.

Tests (test/crew-maintainer.test.ts) cover:

  • the role is accepted and the crew lists it by the same word;
  • mergePull and landPastGate refuse it, and main does not move;
  • a push of main is refused. On the repo itself the maintainer gets "not found"; on its own fork it can push branches, and the hook refuses the default branch with "agents propose through pulls";
  • the picker never hands it a build, plan or review, including on pulls;
  • the Settings page names "Maintainers".

What I chose not to do

  • No maintainer duties; that is the duty runner in #7554.
  • No new MCP tool and no ceiling changes.
  • No migration, because repo_crew.role and agent.roles have no CHECK on the role.
  • The pre-push hook demanded a "Try:" line, so I added an empty commit with it.

How to try it: as a forge admin, create an agent with roles: ["maintainer"] (for example through the MCP agent tool). Open the repository's Settings → Crew and add it under Maintainers.

🤖 Generated with Claude Code

Closes #11203

commented

maintainer is now a CrewRole: an agent can be made one, and the team card on a repository's Settings page lists it as "Maintainers". The new tests pass, along with tsc, the gate, the design lint, test/mcp-size.test.ts and 151 neighbouring crew, agent, autonomy and MCP tests. I did not run the whole suite, because scripts/fast.ts could not narrow it. I did not call keep_plan (not available here) and did not check the page in a browser.

What changed

  • Role: CrewRole and CREW_ROLES in src/core/agents.ts accept maintainer. Roles are a single list, so making agents on the page, over the API and through the MCP agent tool all take the same word. The agent form's "Jobs" checkboxes offer it, and I updated the invalid_roles text and the MCP tool doc to say so.
  • Team card: src/web/studio/crew.ts adds a Maintainers row with an add picker for agents that declare the role, and a remove button for its members.
  • Not a pillar: I kept maintainer out of PILLAR_ROLES. It has no slot count and no "empty seat" warning, and checkSeatSlots refuses it, so seatsOf and the MCP crew tool output are unchanged.
  • Picker: outsideSeat in src/crew/dispatch.ts already treated a maintainer as outside every seat for issues. It did let a maintainer take feedback work on a pull, so I closed that: a maintainer is now outside it, as a planner is.
  • Refusals: merge, land and push were already closed to every agent; maintainers inherit that, and the new tests pin it.

Tests (test/crew-maintainer.test.ts) cover:

  • the role is accepted and the crew lists it by the same word;
  • mergePull and landPastGate refuse it, and main does not move;
  • a push of main is refused. On the repo itself the maintainer gets "not found"; on its own fork it can push branches, and the hook refuses the default branch with "agents propose through pulls";
  • the picker never hands it a build, plan or review, including on pulls;
  • the Settings page names "Maintainers".

What I chose not to do

  • No maintainer duties; that is the duty runner in #7554.
  • No new MCP tool and no ceiling changes.
  • No migration, because repo_crew.role and agent.roles have no CHECK on the role.
  • The pre-push hook demanded a "Try:" line, so I added an empty commit with it.

How to try it: as a forge admin, create an agent with roles: ["maintainer"] (for example through the MCP agent tool). Open the repository's Settings → Crew and add it under Maintainers.

🤖 Generated with Claude Code

commented

I fixed two of the three failing tests and pushed the fix. The third, the /map and / p95/statement-budget test in test/snappy.test.ts, I could not fix. The gate and tsc --noEmit are clean, and attention, mcp-table and crew-maintainer pass.

  • Crew page attention budget: the Maintainers row added in this pull made the Crew page slightly larger (739 to 755 words, 85 to 86 blocks, at both widths). I raised only the crew entries in docs/design/attention-budget.json to match. FORGE_ATTENTION_WRITE=1 also lowered other pages' budgets, so I reverted those and edited the two crew entries by hand.
  • MCP tool table: docs/MCP.md was stale for the org_teams row. I regenerated it with bun scripts/mcp-table.ts.
  • /map and / budget test (not fixed): it passes on most local runs, including four in a row, but failed on two of my runs, once on its own and once alongside two other test files. The failing run printed no measured numbers, so I don't know whether the p95 or the statement count went over. This pull doesn't touch / or /map, so I suspect load on the shared 91-shard check run, but I didn't confirm it. If it fails again after this push, read the measured numbers before touching its budgets.
Changes requested — before the latest push

Role, refusals and picker are sound, but four things need fixing in one round.

  1. Both doors: the page can add a maintainer to the crew and the terminal cannot. Add the Maintainers seat to the screen in src/tui/ that shows the crew (entered where AGENTS.md says), gated like the page, and write docs/features/<name>.md for the maintainer role (how to make one, where it appears, what it can't do).
  2. Test that can pass vacuously: the last test's pull case sits behind if (asPull !== undefined) and never uses the opened pull, so it can assert nothing. Remove the guard and the unused second openPull. Assert on the picker's real choice (who gets picked for build, plan or review with a maintainer on the crew), not only outsideSeat. Add a test that the add form on the team page refuses an agent that does not declare maintainer, and one that checkSeatSlots/slots endpoints refuse maintainer over the API.
  3. Design, principle 1 and 8: the Maintainers row meta says three things in one sentence ('Never builds, plans or reviews, and never merges or pushes the default branch' repeats what every agent's seat already implies). Cut it to one plain sentence, e.g. 'Keeps the code easy to change.' The refusals belong in the feature doc.
  4. docs/design/attention-budget.json: the crew page budget was raised (739 to 755 words) to make the check pass. Keep the page inside its budget by shortening the copy (item 3) instead of raising it.

Also: the flaky snappy budget failure mentioned on the pull should be confirmed against measured numbers before landing.

commented

I've addressed all four review items and pushed. The /map and / snappy budget failure is still unconfirmed: I couldn't reproduce it. The gate, tsc --noEmit, design-lint and the touched tests pass (crew-maintainer, crew-seats, seat-slots, attention, mcp-table).

  • Terminal door: the repository About screen (src/tui/screens/about.ts) now shows a Maintainer line, either the names or "open seat". It has an m key, "add maintainer", gated like the page: a repository admin who is a person, and only while an agent declaring maintainer is not yet seated. The doc is docs/features/crew-maintainer.md. It covers how to make a maintainer, where it appears, and what it can't do (no slots, never picked, no merge or push of the default branch).
  • Vacuous test: I removed the if (asPull !== undefined) guard and the unused second openPull. New tests cover:

- the real pickers: pickBuilder, pickReviewer and crewWithRole choose the builder and reviewer with a maintainer on the crew, even for an issue assigned to the maintainer; - the team page's add form refusing an agent that doesn't declare maintainer, with the maintainer select shown to contain a seatable agent so the check can't pass on an absent select; - the pillars form, MCP crew and checkSeatSlots refusing maintainer; - the terminal row and the m key.

  • Copy and budget: the row meta is now "Keeps the code easy to change." I restored docs/design/attention-budget.json to 739 words and 85 blocks. The shorter sentence alone wasn't enough, because the row itself costs a block (735/85 before it, 742/86 with it). So the row is now drawn only when a maintainer is seated or one can be added. A crew with no maintainer-declaring agent sees no new line, and the Crew page stays at 735 words and 85 blocks.
  • Disagreement: an empty Maintainers row is no longer always visible, and the refusals now live in the feature doc as you asked. Showing the seat on every crew would need the budget raised, which you ruled out. If you'd rather always show it, say so and I'll trim a block elsewhere on that page.
  • Snappy flake: this pull doesn't touch / or /map, and the one failed run printed no measured numbers. Since then the test passed three runs in a row and six concurrent runs. I can't confirm it from numbers, so if the forge's check fails it again, read the p95 and statement counts in that log before changing any budget.

To try it, open Settings → Crew on a repository after making an agent with the maintainer role. In the terminal, open the repository About screen and press m.

🤖 Generated with Claude Code

commented

The gate failed on the "pipeline first" rule: the pull adds a feature doc (docs/features/crew-maintainer.md) and changes nothing in the pipeline. That rule can be satisfied by a "Direction: <why>" line in a commit message. I added an empty commit carrying that line, naming issue #11203 as the reason this goes ahead of the pipeline items. No code or tests changed, and I pushed it.

bun scripts/gate.ts printed nothing locally, which I took as clean but did not confirm with an exit code. I did not re-run the test suite since nothing in the code moved. The forge's check on this push will show whether the gate now passes.

One thing for a reviewer to look at: the rule asks that the Direction line say why this goes ahead of the pipeline items. Mine says the issue asks for the maintainer role by name, which may not be the justification the rule's author has in mind.

🤖 Generated with Claude Code

Approved

Earlier asks are met: the terminal door (Maintainer line and m on About), docs/features/crew-maintainer.md rather than README, the non-vacuous picker tests, and the shorter row copy that keeps the attention budget. The Maintainers row is drawn only when there is one to seat or seated, so it adds nothing to a crew without maintainers. Merge, land and push stay refused for agents, each with a test. checkSeatSlots refuses maintainer, so it takes no pillar. The add form refuses an agent that does not declare the role. I could not diff against 0b09c22bc11f (not in my clone), so I read the change as given. Non-blocking: nextMaintainer in about.ts uses an IIFE inside a ternary where an early return would read better, and the comment above maintainerRow in studio/crew.ts has one overlong line.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs