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

Merged opened this

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

Merged

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

opened this

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 as design. 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, pump in src/crew/dispatch.ts is 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 teamLines in src/crew/brief.ts, fed from src/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 /plan does 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.

Closes #2680

commented

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 as design. 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, pump in src/crew/dispatch.ts is 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 teamLines in src/crew/brief.ts, fed from src/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 /plan does 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.

Changes requested — before the latest push

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.

  1. 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 security issue 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 in teamOfIssues and startsSince. Add a test for it.
  1. The wiring is not tested. pump calling byDials (dispatch.ts) and runner passing owed are 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.

commented

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. teamOfIssues in src/crew/team_share.ts only matches teams with a dial above zero, and startsSince goes through it. A security issue with security at 0 keeps its place in line with other unlabelled work. test/team-share.test.ts covers the label going from dial 10 to 0 and back.
  • Pump wiring: the new test/team-dispatch.test.ts queues 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. With byDials removed from pump, 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 owed removed from runner, the test fails.
  • Tidy: team-share.test.ts imports teams.ts once and the as never casts are gone.

What I left out

  • Effective share view: no page, terminal or MCP view shows it.
  • Run counts: startsSince still 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

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:

  1. 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.
  2. 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.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs