Repository navigation
fix(agents): stop a parked background session once its turn ends - #1825
Conversation
A parked background session kept its Claude Code process resident until its wake, so a desk with many parked workers ran out of memory and capacity refused every start while almost nothing was busy. The watch (its chores poll) now stops the background session of every parked agent beekeeper started, once its turn ended (no headless turn, no running command, its transcript and its subagents quiet for stewardQuiet), with claude stop, which keeps the conversation. The wake that ends the park (agents wake, agents resume, the watch's resume or a timer's) already resumes a stopped background session from its transcript with claude --bg --resume. The supervisor's and the guide's sessions, a park already resumable, an agent on an execution host and a session its person started stay. A failed stop is a watch line and is tried again the next poll; a stop is an agents.parkstop event. Fixes #1799 Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MK2zserZP5SGBuk95w9h17
teemow
left a comment
There was a problem hiding this comment.
Review: changes requested
Head reviewed: 4ae5dfa551bdaa56ac9bf4cff9048faeccd504e4
CI: gh pr checks 1825 --repo giantswarm/beekeeper --watch --fail-fast exited 0; all executable checks passed (the conditional findings job was skipped). The Linux/macOS vet and test run https://github.com/giantswarm/beekeeper/actions/runs/41252989685 and pre-commit run https://github.com/giantswarm/beekeeper/actions/runs/41252989718 both report success with headSha exactly matching the reviewed head. CircleCI checks were verified through GitHub check results, without a CircleCI login or token. CI supplies the full-suite and lint proof; no local builds or tests were repeated.
The change addresses #1799 by retiring quiet parked background sessions and using the existing background resume path. One blocking finding remains:
- [P1] Serialize park-stop with resume/wake before terminating the session —
cmd/agentbackground.go:447-450.stopParkedselects candidates from the state and session snapshots supplied topending, then callsclaude stopwithout revalidating or coordinating with resume/wake. A concrete interleaving is: the watch reads a quiet parked session; a concurrentagents resumeclears its Park (resumeParkedOpts) and successfully sends the resume message to its running CLI (wakeAgent); the watch then selects the agent from its old snapshots and stops that CLI. The newly read process table does not prevent this:headlessTurnonly detects print/headless turns, while Commands/LastActive/LastSubagent still come from the older session snapshot. The resume has already succeeded and removed the park, so the wake can be lost and auto-resume has nothing left to retry. Coordinate the stop transition with resume/wake, revalidate the same park and fresh activity before stopping, and ensure a wake arriving during the transition is delivered after stopping. Add a regression case interleaving resume between candidate observation and stop. This finding follows directly from the control flow; no local check was needed.
The watch chose a parked background session from the poll's snapshots and stopped it without looking again, so a resume that ended the park and reached the running CLI in between could be lost to the stop. The stop and every wake of a background start now hold one lock per session (parkstop/<session>.lock). Under it the stop looks again: the agent still parked on the park it saw, not woken within stewardQuiet (the wake's stamp), and, read anew, still running with its turn ended. It holds the lock until the stopped CLI exited, so a wake that comes meanwhile waits and then resumes the stopped session from its transcript instead of messaging a CLI the stop ends. A wake in flight makes the stop skip the poll. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MK2zserZP5SGBuk95w9h17
|
Review finding [P1] fixed in 5ed2de2: the watch's stop and every wake of a background start now hold a per-session lock. Under the lock, the stop checks again that the park is the same one it saw, that the session hasn't been woken within the quiet window, and that a fresh read of the sessions still shows it running with its turn over. It keeps the lock until the CLI has exited, so a wake that arrives during the stop waits and then resumes the stopped session with Comment by an agent (a desk worker). |
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01MK2zserZP5SGBuk95w9h17
* origin/main: fix(agents): stop a parked background session once its turn ends (#1825) fix(capacity): treat an unopened five-hour window as unused (#1808) fix(supervisor): keep the loop's inner-loop states out of the person's decisions (#1828) feat(run): add opt-in worktree builder Jobs (#1811) fix(agents): tolerate host releases within the execution protocol (#1807) feat(supervisor): review only where the desk's team ownership allows it (#1798) fix(agents): qualify a bare omp model id from capacity pick as provider/id (#1793) fix(watch): --once fires no timer (#1797) fix(supervisor): the loop's default classify rule files no decision for what is none (#1794) fix(supervisor): review every new head after an approval; act on escalation answers (#1787) docs(sessions): describe all supported harnesses (#1792) fix(guard): explain when body files must be written (#1791) # Conflicts: # cmd/supervisorloop.go # cmd/supervisorloop_test.go
* origin/main: fix(agents): stop a parked background session once its turn ends (#1825)
* origin/main: fix(agents): stop a parked background session once its turn ends (#1825) fix(capacity): treat an unopened five-hour window as unused (#1808) fix(supervisor): keep the loop's inner-loop states out of the person's decisions (#1828) feat(run): add opt-in worktree builder Jobs (#1811) # Conflicts: # cmd/supervisorloop_test.go
* origin/main: fix(note): recover notes after a failed initial status write (#1836) fix(supervisor): refill worker capacity on every loop pass (#1826) feat(board): tidy closed items to Done and archive them after a grace (#1790) chore(deps): update gsoci.azurecr.io/giantswarm/architect docker tag to v8.6.31 (#1815) fix(agents): stop a parked background session once its turn ends (#1825) fix(capacity): treat an unopened five-hour window as unused (#1808) fix(supervisor): keep the loop's inner-loop states out of the person's decisions (#1828) feat(run): add opt-in worktree builder Jobs (#1811) # Conflicts: # CHANGELOG.md
Fixes #1799
Problem
With
agents.mode: background, a worker that parks (agents park,lease claim --park) ends its turn, but its Claude Code background session process stays resident until the wake. A desk with many parked workers runs out of memory, andcapacityrefuses every new start while almost nothing is busy.Change
stopParked, in the chores poll afterautoResume).parkedResidentpicks every parked agent whose session beekeeper started as a background session, still runs, and has ended its turn: no headless turn, no running command, and neither its transcript nor its subagents' written withinstewardQuiet. The watch stops it withclaude stop, which keeps the conversation, logsPARKED SESSION STOPPEDand records anagents.parkstopevent.agents wake,agents resume, the watch's auto-resume and a timer's wake all go throughwakeAgent. That function finds no live CLI and resumes the session from its transcript withclaude --bg --resume. The new end-to-end test covers this whole path: park → stop → resume.roleKeeps)PARKED SESSION STAYSline, and the next poll tries again.agents parkitself, because the parking session runsparkinside its own turn and stopping it there would cut that turn short. The park's output now tells a background session that it will be stopped once the turn ends and resumed on the wake.stopBackgroundStart(agents remove) now uses the samebgStophelper.agents parkhelp describe the behaviour.Tests
TestParkedResident: the selection and every case that stays, including a session still inside its park turn, a running command, a working subagent, a session already stopped, a non-background start, a resumable park, an execution host and the supervisor.TestParkedBackgroundSessionStopsAndResumes: the park line; no stop while the turn is still going; oneclaude stop <short>after it ends, with its event and line; no second stop once the CLI is gone; thenagents resumerunsclaude --bg --resume <id>in the start's directory, with the park's wait in the message.TestParkedBackgroundStopFails: a failed stop is reported and tried again.go vet ./cmd/andgo test ./cmd/pass locally.🤖 Generated with Claude Code
https://claude.ai/code/session_01MK2zserZP5SGBuk95w9h17