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

The planner keeps a standing reserve of startable work: two per builder slot, planned before the plate runs dry #3246

Merged opened this in answer to

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

Merged

The planner is now asked whenever fewer issues can start than two per builder slot, instead of only when the map is thin (fewer than three) or dry. The test the issue asked for passes: 7 slots, 5 startable, and the planner is asked with target 14.

shipped production is serving it deploy ef63cd6

opened this

The planner is now asked whenever fewer issues can start than two per builder slot, instead of only when the map is thin (fewer than three) or dry. The test the issue asked for passes: 7 slots, 5 startable, and the planner is asked with target 14.

What moved where

  • New src/crew/reserve.ts. It holds the reserve rule: reserveTarget(slots) = max(3, 2 × slots), along with reserveOf, reserveWords and short. THIN_MAP moved here from autonomy.ts. The old three-issue minimum stays as a floor, so a one-slot repository keeps its current behaviour.
  • src/crew/autonomy.ts

- planIfThin asks the planner when the reserve is short, in place of the old ready >= THIN_MAP check. - Its log line is now reserve is short; planner asked … reserve=5/14. - The tick line shows reserve=startable/target.

  • The 20-minute floor (REPLAN_AFTER_MS) now applies only when the planner's last pass did not end well: a decline that a person answered, or a failed run. After a pass that ended well there is no floor; the planner is asked again as soon as the map moves. The existing skipsReplan guard still stops a loop when a pass changed nothing.
  • The planner's brief (src/crew/brief.ts, filled in by src/crew/runner.ts) now gives the reserve size, how many issues can start now, and how many more to plan. If a pass leaves the reserve short, the brief tells the planner to say why in a comment on the direction issue. It replaces the old "at least three ready issues" line.
  • docs/PLANNING.md is updated to match.

Choices to look at

  • The floor also applies after a planner run that failed without declining, as well as after a decline. The issue says "a planner that declined"; I included failures because a run that crashed shouldn't be retried every minute.
  • The forge does not post the "why" note itself. It can't know the reason, so the brief asks the planner to write it. That comment counts as nothing new, so the planner is not asked again until the map moves.
  • The reserve is sized from the operator's slot setting, not the smaller paced number, so the tick line and the brief show the same target.
  • Expect more planner runs: while the reserve is short, every landing changes the map and so prompts another pass.

How to check it bun test test/crew-reserve.test.ts has 8 tests:

  • the reserve size;
  • 7 busy slots with 14 startable issues: no planning;
  • 7 slots, 5 startable: asked with reserve=5/14;
  • the brief text;
  • the tick line;
  • a pass that ended well is asked again at once when the map moved;
  • a pass that ended well and moved nothing is held;
  • a decline is held 20 minutes, then asked.

The 5-startable and floor tests fail on the old code.

bun x tsc --noEmit is clean. The existing crew, autonomy, board and studio suites pass (681 tests), apart from two failures:

  • crew-interrupted's "young lock" test also fails on unchanged main in this sandbox.
  • crew-proposal timed out once under load but passes when run alone.

🤖 Generated with Claude Code

Closes #3237

commented

The planner is now asked whenever fewer issues can start than two per builder slot, instead of only when the map is thin (fewer than three) or dry. The test the issue asked for passes: 7 slots, 5 startable, and the planner is asked with target 14.

What moved where

  • New src/crew/reserve.ts. It holds the reserve rule: reserveTarget(slots) = max(3, 2 × slots), along with reserveOf, reserveWords and short. THIN_MAP moved here from autonomy.ts. The old three-issue minimum stays as a floor, so a one-slot repository keeps its current behaviour.
  • src/crew/autonomy.ts

- planIfThin asks the planner when the reserve is short, in place of the old ready >= THIN_MAP check. - Its log line is now reserve is short; planner asked … reserve=5/14. - The tick line shows reserve=startable/target.

  • The 20-minute floor (REPLAN_AFTER_MS) now applies only when the planner's last pass did not end well: a decline that a person answered, or a failed run. After a pass that ended well there is no floor; the planner is asked again as soon as the map moves. The existing skipsReplan guard still stops a loop when a pass changed nothing.
  • The planner's brief (src/crew/brief.ts, filled in by src/crew/runner.ts) now gives the reserve size, how many issues can start now, and how many more to plan. If a pass leaves the reserve short, the brief tells the planner to say why in a comment on the direction issue. It replaces the old "at least three ready issues" line.
  • docs/PLANNING.md is updated to match.

Choices to look at

  • The floor also applies after a planner run that failed without declining, as well as after a decline. The issue says "a planner that declined"; I included failures because a run that crashed shouldn't be retried every minute.
  • The forge does not post the "why" note itself. It can't know the reason, so the brief asks the planner to write it. That comment counts as nothing new, so the planner is not asked again until the map moves.
  • The reserve is sized from the operator's slot setting, not the smaller paced number, so the tick line and the brief show the same target.
  • Expect more planner runs: while the reserve is short, every landing changes the map and so prompts another pass.

How to check it bun test test/crew-reserve.test.ts has 8 tests:

  • the reserve size;
  • 7 busy slots with 14 startable issues: no planning;
  • 7 slots, 5 startable: asked with reserve=5/14;
  • the brief text;
  • the tick line;
  • a pass that ended well is asked again at once when the map moved;
  • a pass that ended well and moved nothing is held;
  • a decline is held 20 minutes, then asked.

The 5-startable and floor tests fail on the old code.

bun x tsc --noEmit is clean. The existing crew, autonomy, board and studio suites pass (681 tests), apart from two failures:

  • crew-interrupted's "young lock" test also fails on unchanged main in this sandbox.
  • crew-proposal timed out once under load but passes when run alone.

🤖 Generated with Claude Code

Approved

Approved. This is crew-internal with no page or terminal surface, so there is nothing for a phone or keyboard user to hit. I judged it from the diff and did not open the preview or the screenshots. The reserve rule is small and in its own module (reserve.ts), the 20-minute floor now applies only after a pass that did not end well, and the tests cover both the asked and the not-asked cases. No new dependency, and no input, auth or escaping surface is touched.

Two things I would do next:

  1. Replace the inline IIFE in runner.ts with a plain const held = heldNow(app, repo) computed before the object. It reads awkwardly beside the surrounding code.
  2. Watch planner spend. With no floor after a good pass, a repository with many slots will get a planner run on every landing while the reserve is short. If that is noisy, cap runs per hour, or skip the pass when the planner has already said why on the direction.
Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs