Skip to content

fix(session-lifecycle): detach Claude Code, track PID independently, safe recycling fallback#4

Merged
c-h- merged 1 commit into
mainfrom
fix/session-lifecycle
Feb 18, 2026
Merged

fix(session-lifecycle): detach Claude Code, track PID independently, safe recycling fallback#4
c-h- merged 1 commit into
mainfrom
fix/session-lifecycle

Conversation

@c-h-

@c-h- c-h- commented Feb 18, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes three interconnected bugs where session lifecycle was incorrectly coupled to the launch wrapper process:

  • BUG-3: Claude Code child process now fully detached (detached: true, stdio: ignore, unref()) — runs in its own process group and survives wrapper SIGTERM
  • BUG-2: Session metadata (PID, start time, cwd) persisted to ~/.claude/agent-ctl/sessions/<id>.json so status checks work after the wrapper exits. Liveness verified via kill(pid, 0) with automatic stale metadata cleanup
  • BUG-1: processStartedAfterSession() now defaults to false (stopped) when start time is unavailable, preventing old sessions from appearing as 'running' due to recycled PIDs

Test plan

  • Build passes (tsc)
  • All 42 tests pass (24 claude-code + 18 openclaw)
  • Typecheck passes (tsc --noEmit)
  • Lint passes (biome check)
  • New tests: session shows running via persisted metadata with live PID
  • New tests: session shows stopped when persisted PID is dead
  • New tests: stale metadata cleaned up on dead PID
  • New tests: PID recycling detected in persisted metadata via start time
  • New tests: old metadata without startTime + live PID assumes running
  • Updated test: no-startTime falls back to stopped (BUG-1 safety)
  • Pre-push hook passed (lint → typecheck → build → test)

🤖 Generated with Claude Code

… PID independently

BUG-3: Spawn Claude Code with detached: true + stdio: ignore + unref() so
the child runs in its own process group. When the wrapper gets SIGTERM,
Claude Code keeps running independently (like nohup).

BUG-2: Persist session metadata (PID, start time, cwd) to
~/.claude/agent-ctl/sessions/<id>.json so status checks work after the
wrapper exits. isSessionRunning() now checks both ps-discovered PIDs and
persisted metadata with kill(pid, 0) liveness checks. Stale metadata is
automatically cleaned up when the PID is dead.

BUG-1: processStartedAfterSession() now defaults to false (stopped) when
start time is unavailable, preventing old sessions from appearing as
'running' due to recycled PIDs when verification is impossible.

Tests added:
- Session shows running via persisted metadata with live PID
- Session shows stopped when persisted PID is dead
- Stale metadata cleaned up on dead PID
- PID recycling detected in persisted metadata via start time
- Old metadata without startTime + live PID assumes running
- Existing no-startTime test updated: falls back to stopped (BUG-1 safety)

Co-Authored-By: Charlie Hulcher <charlie@kindo.ai>
@c-h- c-h- merged commit 06bbc6f into main Feb 18, 2026
1 check passed
c-h- added a commit that referenced this pull request Feb 20, 2026
… PID independently (#4)

BUG-3: Spawn Claude Code with detached: true + stdio: ignore + unref() so
the child runs in its own process group. When the wrapper gets SIGTERM,
Claude Code keeps running independently (like nohup).

BUG-2: Persist session metadata (PID, start time, cwd) to
~/.claude/agent-ctl/sessions/<id>.json so status checks work after the
wrapper exits. isSessionRunning() now checks both ps-discovered PIDs and
persisted metadata with kill(pid, 0) liveness checks. Stale metadata is
automatically cleaned up when the PID is dead.

BUG-1: processStartedAfterSession() now defaults to false (stopped) when
start time is unavailable, preventing old sessions from appearing as
'running' due to recycled PIDs when verification is impossible.

Tests added:
- Session shows running via persisted metadata with live PID
- Session shows stopped when persisted PID is dead
- Stale metadata cleaned up on dead PID
- PID recycling detected in persisted metadata via start time
- Old metadata without startTime + live PID assumes running
- Existing no-startTime test updated: falls back to stopped (BUG-1 safety)

Co-authored-by: Doink (OpenClaw) <charlie+doink@kindo.ai>
c-h- pushed a commit that referenced this pull request Apr 4, 2026
Aligns CI workflow with publish workflow which already uses @v6.
Includes performance improvements and security fixes.

Closes #2

Co-authored-by: Marshall (OpenClaw) <marshall@openclaw.ai>
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.

1 participant