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

A cut waits up to five minutes for running crew runs to finish, and the tick says how many it waited on #11920

Merged opened this

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

Merged

The release keeper now waits up to five minutes for running crew runs before it cuts, so a release no longer stops a run partway and loses it.

shipped production is serving it deploy 8f18665

opened this

The release keeper now waits up to five minutes for running crew runs before it cuts, so a release no longer stops a run partway and loses it.

  • src/keeper/cadence.ts: decideCut takes two new inputs: crewRuns (how many are running) and crewSince (when the keeper first saw them). While runs are going and now − crewSince is under the new constant CREW_WAIT_MS (5 min), it does not cut. The tick line reads waiting on N crew runs (Xm of 5m). Once the five minutes pass it cuts even if runs are still going, so a stuck run can't block releases. A release_now hotfix skips the wait. The rule is checked after the snapshot rule, and the doc comment at the top of the file lists it.
  • src/keeper/keeper.ts: a new optional port, crewRuns: () => number. The keeper remembers the first tick that found runs in a WeakMap keyed by the forge database, so the five minutes count from that tick. The timer resets when no runs are left or after a cut. It lives in memory only, so a restart starts the count again.
  • src/keeper/wire.ts: the forge's keeper now counts running crew runs, using the existing runningChildren(app.db).agents. Without this the rule would never be reached, which is why the change touches two more files than the issue's file list.

Not done:

  • The release workflow and the chart are untouched.
  • Running checks don't hold a cut; only crew runs (agent runs) do.
  • I couldn't keep the plan on the issue: the keep_plan tool isn't available in this run.

The new test is in test/release-keeper-cadence.test.ts, with the clock passed in. It calls decideCut directly: runs in flight hold the cut at minute 4 and release it at minute 5, and a hotfix is not held. It also runs real ticks, so the wait counts from the first tick that saw the runs. It fails without the change.

Checks run here: bun scripts/gate.ts and bun x tsc --noEmit are clean. 26 test files pass (134 tests): every release-keeper and keeper test, release-now, tui-release-keeper, src-imports, unreached, timers-named and global-spies-restored. scripts/fast.ts named 164 files, estimated at about 14 minutes against its 5-minute budget, so I left the rest to the forge's full check on the push.

To try it on the preview: while an agent run is going and main is ahead of the live tag, the keeper's tick line shows waiting on 1 crew run (0m of 5m), and the release goes out at minute 5.

🤖 Generated with Claude Code

Closes #11918

commented

The release keeper now waits up to five minutes for running crew runs before it cuts, so a release no longer stops a run partway and loses it.

  • src/keeper/cadence.ts: decideCut takes two new inputs: crewRuns (how many are running) and crewSince (when the keeper first saw them). While runs are going and now − crewSince is under the new constant CREW_WAIT_MS (5 min), it does not cut. The tick line reads waiting on N crew runs (Xm of 5m). Once the five minutes pass it cuts even if runs are still going, so a stuck run can't block releases. A release_now hotfix skips the wait. The rule is checked after the snapshot rule, and the doc comment at the top of the file lists it.
  • src/keeper/keeper.ts: a new optional port, crewRuns: () => number. The keeper remembers the first tick that found runs in a WeakMap keyed by the forge database, so the five minutes count from that tick. The timer resets when no runs are left or after a cut. It lives in memory only, so a restart starts the count again.
  • src/keeper/wire.ts: the forge's keeper now counts running crew runs, using the existing runningChildren(app.db).agents. Without this the rule would never be reached, which is why the change touches two more files than the issue's file list.

Not done:

  • The release workflow and the chart are untouched.
  • Running checks don't hold a cut; only crew runs (agent runs) do.
  • I couldn't keep the plan on the issue: the keep_plan tool isn't available in this run.

The new test is in test/release-keeper-cadence.test.ts, with the clock passed in. It calls decideCut directly: runs in flight hold the cut at minute 4 and release it at minute 5, and a hotfix is not held. It also runs real ticks, so the wait counts from the first tick that saw the runs. It fails without the change.

Checks run here: bun scripts/gate.ts and bun x tsc --noEmit are clean. 26 test files pass (134 tests): every release-keeper and keeper test, release-now, tui-release-keeper, src-imports, unreached, timers-named and global-spies-restored. scripts/fast.ts named 164 files, estimated at about 14 minutes against its 5-minute budget, so I left the rest to the forge's full check on the push.

To try it on the preview: while an agent run is going and main is ahead of the live tag, the keeper's tick line shows waiting on 1 crew run (0m of 5m), and the release goes out at minute 5.

🤖 Generated with Claude Code

Approved

The hold is correct and small: five minutes counted from the first tick that found crew runs, reset when none run or after a cut, and a release_now hotfix skips it. The test passes the clock in and covers minute 4, minute 5 and the hotfix, through decideCut and real ticks. There is no web or terminal surface, so no TUI screen or docs page is owed. I did not run the tests myself. Two things I would do next: (1) the first-seen stamp is in memory, so a keeper restart restarts the five minutes. Persist it if restarts turn out to be frequent. (2) The stamp only updates on ticks that reach decideCut, so an early return before it pauses the count. Move the crewSince call to the top of tickStep so the count tracks wall time.

Sign in to join this conversation.

↑↓ navigate ↵ open ⌘↵ its app esc close

Keyboard shortcuts

Docs