fix(daemon): restart recovers from a session-less zombie container instead of blocking - #56
Merged
Merged
Conversation
…stead of blocking Hit via sandy-ui: the agent exits but the daemon container stays up (container alive, inner tmux session dead) — a zombie. On the next launch, both the --start idempotency check and the bare-sandy DEC-B check keyed off container EXISTENCE alone (`docker ps -q --filter label=sandy.daemon=true`), so the zombie read as "already running" and wedged the user out of the workspace. Refine D9 "container-as-truth" to "container WITH A LIVE SESSION as truth": both checks now probe the inner session (`docker exec … tmux has-session`, with a 5×1s mid-startup retry so a container that's merely still-launching isn't misread and reaped out from under a concurrent start). A dead-session zombie is reaped via `"$0" --stop` and the operation proceeds fresh — --start launches a new session, bare sandy falls through to interactive. Defensive: makes restart robust regardless of WHY the zombie exists (supervisor died, non-TTY sandy-ui launch, a restart racing the ~60s #47 teardown window). The separate question of why an agent exits immediately in a given workspace is unaffected by this. Tests: run-tests.sh §73 (structural — both checks probe has-session + reap via $0 --stop) and acceptance-daemon.sh §6.5 (real-docker end-to-end: kill the inner tmux session → assert --start reaps the zombie and replaces it with a fresh live session). Docs: CLAUDE.md D6 refinement + DEC-B. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Fixes the "restart error only happens when a prior exited-but-alive container is present" case.
The bug
A daemon container can be up but with its agent session dead (a zombie) — the agent exits, but the container's PID 1 (
tail -f /dev/null) keeps it running, and it lingers until the #47 supervisor's ~60s watch-loop reaps it (or indefinitely if the supervisor died). Both restart-decision paths keyed off container existence alone:--startidempotency (sandy:3567) → "Daemon session already running" + exit 0sandyDEC-B (sandy:5373) → "A daemon session is already running" + exit 1So a zombie blocked the restart even though the session was dead.
The fix
Refine D9 "container-as-truth" → "container with a LIVE session as truth." Both checks now probe the inner session (
docker exec … tmux has-session) with a short 5×1s mid-startup retry (so a still-launching container isn't misread and reaped out from under a concurrent start). A dead-session zombie is reaped via"$0" --stopand the op proceeds fresh —--startlaunches a new session, baresandyfalls through to interactive.This is defensive — it makes restart robust regardless of why the zombie exists (supervisor died, non-TTY sandy-ui launch, a restart racing the teardown window).
Tests / docs
run-tests.sh§73 — structural: both checks probehas-session+ reap via$0 --stop.acceptance-daemon.sh§6.5 — real-docker end-to-end:tmux kill-serverto make a zombie → assert--startreaps it and replaces the container with a fresh live session (and §7 teardown re-points at the new one).CLAUDE.md— D6 refinement + DEC-B update.Note: the separate question of why an agent exits immediately in a given workspace (unconfirmed; likely a non-TTY credential/approval drop on the sandy-ui launch path) is not addressed here — that's still being diagnosed.
🤖 Generated with Claude Code