Skip to content

fix(agents): stop a parked background session once its turn ends - #1825

Merged
teemow merged 3 commits into
mainfrom
fix/1799-park-stops-process
Nov 1, 2026
Merged

teemow merged 3 commits into
mainfrom
fix/1799-park-stops-process

Conversation

@teemow

@teemow teemow commented Oct 31, 2026

Copy link
Copy Markdown
Member

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, and capacity refuses every new start while almost nothing is busy.

Change

  • The watch stops parked background sessions (stopParked, in the chores poll after autoResume). parkedResident picks 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 within stewardQuiet. The watch stops it with claude stop, which keeps the conversation, logs PARKED SESSION STOPPED and records an agents.parkstop event.
  • The wake already handles a stopped session. agents wake, agents resume, the watch's auto-resume and a timer's wake all go through wakeAgent. That function finds no live CLI and resumes the session from its transcript with claude --bg --resume. The new end-to-end test covers this whole path: park → stop → resume.
  • Sessions that stay:
    • the sessions of the supervisor and the guide (roleKeeps)
    • a park that is already resumable, which the watch is about to resume anyway
    • an agent on an SSH execution host
    • any session beekeeper did not start as a background session
  • A failed stop is a PARKED SESSION STAYS line, and the next poll tries again.
  • The stop is done by the watch, not by agents park itself, because the parking session runs park inside 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 same bgStop helper.
  • README and the agents park help 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; one claude stop <short> after it ends, with its event and line; no second stop once the CLI is gone; then agents resume runs claude --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/ and go test ./cmd/ pass locally.

🤖 Generated with Claude Code

https://claude.ai/code/session_01MK2zserZP5SGBuk95w9h17

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
teemow requested a review from a team as a code owner October 31, 2026 23:32

@teemow teemow left a comment

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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. stopParked selects candidates from the state and session snapshots supplied to pending, then calls claude stop without revalidating or coordinating with resume/wake. A concrete interleaving is: the watch reads a quiet parked session; a concurrent agents resume clears 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: headlessTurn only 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
@teemow

teemow commented Oct 31, 2026

Copy link
Copy Markdown
Member Author

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 claude --bg --resume. New regression tests: TestParkedBackgroundStopAfterAResume (the resume lands between the look and the stop, plus a re-park on a different wait) and TestParkedBackgroundWakeDuringTheStop (the wake waits for the stop and then resumes; a fresh wake holds off the stop). Both fail with the lock and the re-check removed.

Comment by an agent (a desk worker).

@teemow
teemow merged commit 6403fef into main Nov 1, 2026
16 checks passed
@teemow
teemow deleted the fix/1799-park-stops-process branch November 1, 2026 00:28
teemow added a commit that referenced this pull request Nov 1, 2026
* 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
teemow added a commit that referenced this pull request Nov 1, 2026
…iews

* 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)

# Conflicts:
#	internal/superloop/superloop.go
teemow added a commit that referenced this pull request Nov 1, 2026
* 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)

Signed-off-by: Timo Derstappen <teemow@gmail.com>
teemow added a commit that referenced this pull request Nov 1, 2026
* origin/main:
  fix(agents): stop a parked background session once its turn ends (#1825)
teemow added a commit that referenced this pull request Nov 1, 2026
* 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)
teemow added a commit that referenced this pull request Nov 1, 2026
* 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)
teemow added a commit that referenced this pull request Nov 1, 2026
* 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
teemow added a commit that referenced this pull request Nov 1, 2026
* 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
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parked background sessions stay resident and starve the machine's memory

1 participant