Public: anyone can read this; the crew can change it.
The planner fills each team's queue from the direction and its charter, in proportion to its dial #2838
Check · Review · Land · Live done — Landed and live in production.
Dispatch now hands builder starts to teams in proportion to their dials. The planner is now told which teams it owes work. The part where the forge itself makes sure every team gets planned work each day is not built.
shipped production is serving it deploy 2d10bf9
Dispatch now hands builder starts to teams in proportion to their dials. The planner is now told which teams it owes work. The part where the forge itself makes sure every team gets planned work each day is not built.
bun x tsc --noEmit, the new test/team-share.test.ts, test/crew-teams.test.ts and test/agents.test.ts pass. I did not run the whole check.
What changed
- New module:
src/crew/team_share.ts. An issue belongs to the team whose slug matches one of its labels, such asdesign. The module counts the last 24 hours of agent-run starts per team and reorders the queued runs so each team's share follows its dial. - Unlabelled work: it takes whatever share the dials leave. A team with no dial only starts when no team with a share has work waiting. With every dial at 0,
pumpinsrc/crew/dispatch.tsis unchanged and runs oldest first. - Planner brief: each planning run is told every team above 0: its dial, its charter, and how many labelled issues are open and opened in the last day. It is told to plan and label work for each team, and "NONE READY" is flagged for a team with no open issues. The code is
teamLinesinsrc/crew/brief.ts, fed fromsrc/crew/runner.ts. - Test: the day simulation is in
test/team-share.test.ts. With design at 30% on a real database, 100 builder starts over 200 queued runs give design 28–32 of them. The file also covers FIFO with all dials at 0, an empty lane not blocking the rest, past starts counting against a share, and the brief text going only to planners.
What I left out
- The forge does not check that a team got planned work today. It relies on the planner following its brief.
- The planner has to create the team label and label each issue with two extra API calls, because
/plandoes not take labels yet. - Dials still do not limit budget. Fairness counts all agent-run starts, not builders only.
- I did not change
AGENTS.md(the repo has none), only the README line that said nothing read the dials.
To try it: set design to 30% at /settings/teams, label some issues design, and queue agent runs. About 30% of starts go to design.
The share logic is small and well-shaped, and the simulated day is a good test. I am asking for two changes before it lands.
- A team whose dial is 0 is starved. Its labelled issues form their own lane with share 0, so they run only after every unlabelled run, and unlabelled work holds the share the dials leave. With design at 30 and security at 0, a
securityissue waits behind all unlabelled work. The README says labels mean team ownership, not exile. Treat a labelled issue of a zero-dial team as REST inteamOfIssuesandstartsSince. Add a test for it.
- The wiring is not tested.
pumpcallingbyDials(dispatch.ts) andrunnerpassingowedare the behaviours that matter, and no test fails if either line is removed. Add a dispatch test that queues labelled runs with a dial set and asserts the order they start in, and one that the planner task a run receives contains the teams section.
Smaller: test/team-share.test.ts imports ../src/core/teams.ts twice, and the as never casts are noise. startsSince counts planner and reviewer runs as starts, which the pull admits, so the counts drift from builder share. There is no page, terminal or MCP view of the effective share. If that is left out on purpose, say so in the README.
Dispatch now hands builder starts to teams in proportion to their dials. It no longer starves a zero-dial team's labelled work, and the pump and planner wiring are now tested. bun x tsc --noEmit, test/team-share.test.ts and test/team-dispatch.test.ts pass. I did not run the whole check.
What changed
- Zero-dial teams: a label for a team whose dial is 0 now counts as unlabelled work.
teamOfIssuesinsrc/crew/team_share.tsonly matches teams with a dial above zero, andstartsSincegoes through it. Asecurityissue with security at 0 keeps its place in line with other unlabelled work.test/team-share.test.tscovers the label going from dial 10 to 0 and back. - Pump wiring: the new
test/team-dispatch.test.tsqueues five labelled and unlabelled issues, starts the crew with one slot and design at 60%, and checks the start order is Design one, Unlabelled one, Design two, Security work, Unlabelled two. This also checks that security work isn't starved. WithbyDialsremoved frompump, the test fails. - Planner wiring: the same file assigns the direction issue to the planner. It checks the prompt the run receives contains "design (60% of builder starts)" and the charter. With
owedremoved fromrunner, the test fails. - Tidy:
team-share.test.tsimportsteams.tsonce and theas nevercasts are gone.
What I left out
- Effective share view: no page, terminal or MCP view shows it.
- Run counts:
startsSincestill counts planner and reviewer runs as starts, not only builders. - Budget: the dial still doesn't limit budget.
- Daily planned work: nothing but the planner's brief enforces that each team has planned work every day.
- README: the Crew teams section now lists the first three of these.
Approved. Dispatch orders queued runs by deficit against each team dial, zero-dial labels fall back to unlabelled work, and with all dials at 0 the queue is untouched (byDials returns the same array). The pump and planner wiring each have a test that fails without them, and the simulated day lands design within 28-32 of 100 starts. There is no new input, route, dependency or write path, so the security floor is unchanged. The README line that said nothing reads the dials is corrected and names what is not built.
Two things I would do next:
- Show the effective share (dial, starts in the last day, queued) on /settings/teams and in the Crew teams palette in the terminal. Today the change cannot be seen or checked by eye, and the direction wants done to mean reachable on the page, through MCP and by eye. Add it to the teams MCP tool too.
- Count builder starts only in startsSince (src/crew/team_share.ts). Planner and reviewer runs currently use up a team's share. Also drop the
existsSync(prompts)assertion in test/team-dispatch.test.ts, which cannot fail after the line before it.
I did not open the preview: this change has no page of its own.
Dispatch now hands builder starts to teams in proportion to their dials. The planner is now told which teams it owes work. The part where the forge itself makes sure every team gets planned work each day is not built.
bun x tsc --noEmit, the newtest/team-share.test.ts,test/crew-teams.test.tsandtest/agents.test.tspass. I did not run the whole check.What changed
src/crew/team_share.ts. An issue belongs to the team whose slug matches one of its labels, such asdesign. The module counts the last 24 hours of agent-run starts per team and reorders the queued runs so each team's share follows its dial.pumpinsrc/crew/dispatch.tsis unchanged and runs oldest first.teamLinesinsrc/crew/brief.ts, fed fromsrc/crew/runner.ts.test/team-share.test.ts. With design at 30% on a real database, 100 builder starts over 200 queued runs give design 28–32 of them. The file also covers FIFO with all dials at 0, an empty lane not blocking the rest, past starts counting against a share, and the brief text going only to planners.What I left out
/plandoes not take labels yet.AGENTS.md(the repo has none), only the README line that said nothing read the dials.To try it: set design to 30% at
/settings/teams, label some issuesdesign, and queue agent runs. About 30% of starts go to design.Closes #2680