Skip to content

continuous-learning-v2 observer daemon does not survive on native Windows (Git Bash nohup/& reaped immediately) #2489

Description

@Hochmah

Summary

continuous-learning-v2's background observer (agents/start-observer.shagents/observer-loop.sh) is lazy-started via nohup ... & from inside a hook process (hooks/observe.sh, invoked per-tool-call through scripts/hooks/observe-runner.jsscripts/hooks/plugin-hook-bootstrap.js). On native Windows (Git Bash / MSYS2, not WSL), the backgrounded process is reaped almost immediately after the spawning hook process exits, so the observer never survives long enough to analyze observations.jsonl or produce instincts — even though observer.enabled: true is set correctly and the lazy-start logic runs without error.

Environment

  • OS: Windows 11
  • Shell for hook execution: Git Bash (MSYS2), confirmed via uname -aMINGW64_NT-...-Msys
  • ECC installed via npx ecc-install --profile developer --with capability:security (manual install, not the Claude Code plugin path)
  • Node 24, Python 3.14 (not the WindowsApps redirector stub)

Repro

  1. Set observer.enabled: true in continuous-learning-v2/config.json.
  2. Wire the plugin's PreToolUse/PostToolUse observe hooks into settings.json (manual-install path — same effect as the plugin path enabling them).
  3. Trigger any tool call. hooks/observe.sh's lazy-start logic fires correctly:
    • .observer.pid is created with a real PID.
    • observer-start.log logs Observer started (PID: <pid>).
  4. Immediately check the PID (Get-Process -Id <pid> / tasklist /FI "PID eq <pid>"): process is already gone.
  5. observer.log contains only the startup line — no periodic analysis entries, confirming the loop never got a chance to run its first cycle.

Root cause (best guess)

agents/start-observer.sh backgrounds the loop with:

nohup env ... "$OBSERVER_LOOP_SCRIPT" >> "$LOG_FILE" 2>&1 &

This is a POSIX pattern that assumes the child process detaches from the parent's process group and survives the parent's exit. On Windows, the parent chain here (Node spawnSyncbash.exe (MSYS2) → observe.shnohup bash &) is a chain of native Windows processes. Many Windows process-spawning frameworks (including Node's child_process in some configurations) create child processes under a Job Object with JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE, which kills all descendants — including nohup-backgrounded ones — when the top-level spawning process (the hook, which is transient and exits right after the tool call) terminates. nohup/& under Git Bash/MSYS2 does not give the same process-group detachment guarantee as real Unix, since it's still CreateProcess under the hood, not fork().

This means the entire v2 observer daemon is silently non-functional on any Windows install that isn't running Claude Code itself inside WSL2.

Suggested fixes (any of)

  1. Detect Windows and use a Windows-native backgrounding mechanism instead of nohup ... &, e.g. powershell -NoProfile -Command "Start-Process ... -WindowStyle Hidden" or scheduling via Task Scheduler, so the child process isn't tied to the parent's Job Object.
  2. Document the limitation explicitly in docs/wsl-setup.md / the continuous-learning-v2 README: the background observer requires WSL2 (or macOS/Linux) and is a no-op on native Windows, so users aren't left with observer.enabled: true silently doing nothing.
  3. Add a startup self-check: after the lazy-start, observe.sh (or start-observer.sh) could re-check the PID after a short delay and log a clear [Hook] observer failed to persist (native Windows backgrounding limitation) warning instead of only logging "Observer started" and going silent.

Related

The manual install path (npx ecc-install) places skills under skills/ecc/<name>/ instead of skills/<name>/, which several slash commands' path resolvers assume (referenced as issue #2037 in commands/instinct-status.md's own comments). That's a separate bug from this one, but worth linking since both surfaced together while setting up continuous-learning-v2 on a manual Windows install.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions