fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) - #150
Conversation
…oneshot (issue #124) Type=oneshot + RemainAfterExit=yes only tracked `tmux new -d`'s own exit, so systemd reported "active" forever even after the `claude remote-control` process inside the tmux pane crashed -- no Restart= could ever fire because systemd never saw a failure. Switch to Type=simple + Restart=on-failure + RestartSec=10s, with ExecStart now an inline supervisor: it launches the same detached tmux session as before (preserving the proven TTY environment `claude remote-control` needs for its QR/status rendering, and the human tmux-attach point), then blocks, polling every 10s whether the session/pane is still alive, and exits non-zero the instant it is not. That makes ExecStart itself the long-lived process systemd tracks, so a dead planner now transitions the unit to failed -> restarting within RestartSec, self-healing with no operator action. Verified the inline supervisor's quoting/control-flow end-to-end against a stubbed tmux+claude harness (pane-death and session-kill paths both correctly exit 1 within one poll cycle). Bump the claude-rc-service marker v5 -> v6 in both the template and this repo's installed copy (kept byte-identical). Update arm-loop.sh's stale oneshot-era comments/hints to match the new supervision story. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…rc wording (issue #124) The setup-skill template still described the old Type=oneshot + RemainAfterExit rationale for the claude-rc restart step and closing status/inspect block, while the installed script and both .service units had already moved to Type=simple + Restart=on-failure (issue #124). Mirror the same wording in the template and bump both files' @orchestrator-managed marker to v6 so a fresh scaffold no longer ships a v6 unit next to a stale v5 banner describing broken oneshot behavior. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
25 similar comments
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
26 similar comments
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
|
fix(loop): systemd actually supervises claude-rc (no more fake-forever oneshot) (#124) (not yet reviewed) |
Closes #124.
Problem
claude-rc.servicewasType=oneshot+RemainAfterExit=yeswithExecStart=tmux new -d .... tmux exits as soon as the detached session exists, so systemd reported the unitactiveforever even after theclaude remote-controlprocess inside the pane died. NoRestart=could ever fire because systemd never saw a failure — the planner rotted silently (the exact reDeploy incident on template v4).Fix — Option B (supervising wrapper, inline)
Chose Option B over the issue's stated Option A preference:
claude remote-controlrenders a QR/status and currently runs in a tmux PTY, so it very likely needs a TTY, and this environment cannot run systemd/tmux/claude to prove TTY-independence. Option B preserves the proven tmux launch environment and the humantmux attachpoint, with no new template/MANIFEST/sync surface area.Type=simple,Restart=on-failure,RestartSec=10s.ExecStartis now a/bin/bash -csupervisor that launches the detached tmux session running the same innerexec claude remote-control ...(identical flags/PATH as before), then pollstmux has-session/tmux list-panes -F '#{pane_dead}'in asleep 10loop andexit 1the instant the session/pane dies. systemd tracks that process as the main PID, so a dead planner transitions the unit to failed → restart withinRestartSecwith no operator action.remain-on-exit off) and lingering-but-dead pane (#{pane_dead}==1), belt-and-suspenders.v5 → v6on bothclaude-rc.servicecopies (per the Release: bump plugin version (0.1.4 → 0.2.0) so the cron-less loop daemon reaches downstream installs #109/Release: bump plugin version 0.2.0 → 0.2.1 — ship the post-0.2.0 loop fixes to downstream installs #113 release pattern); the two copies remain byte-identical.arm-loop.sh(installed + scaffold-template twin) comments/messaging updated to the accurate Type=simple + Restart=on-failure supervision story;journalctlinspect hint added;arm-loopmarker bumpedv5 → v6.Acceptance
pkill -f remote-control→ supervisor exits non-zero →Restart=on-failurerelaunches the session withinRestartSec(~10s detect + 10s RestartSec) with no operator action.systemctl --user status claude-rc-<slug>now reflects real planner health (no permanent fakeactive).Verification
Gates (self-adapter
.claude/self/gates.json): build PASS · lint PASS · test PASS (incl.smoke-fanout.sh).Reviewers (consensus=all): correctness (opus) APPROVE · tests (sonnet) APPROVE.
The inline supervisor control-flow was exercised via a stubbed tmux+claude harness (both death paths drive
exit 1); the sed#-delimiter vs#{pane_dead}collision was checked and is a non-issue (the#delimits the sed expression, not the input).Cannot be runtime-tested in CI
No systemd/tmux/claude available in-sandbox, so failed→restart rests on reasoned systemd semantics + verified shell control flow. Recommend a real-machine smoke (
pkill -f remote-controlafter arming) before/soon after merge — the issue's acceptance itself acknowledges this.Non-blocking follow-ups (out of scope for #124)
arm-loop.sh(both copies) line ~102/155 still has a dated historical PATH-resolution comment mentioning "the oneshot unit still reports success" — stale relative to the v6 Type=simple story, not touched here..claude/scripts/arm-loop.shand.claude/skills/setup/templates/arm-loop.sh: the template is missing the issue Loop: spend ceilings — per-tick caps, daily budget with auto-halt, stop-after self-disarm #95 (--stop-after-days) and Loop daemon: harden runtime-env resolution — bake resolved PATH into installed units, don't depend on nvm sourcing at runtime #107 (baked node/claude PATH) blocks. Separate from claude-rc.service: systemd cannot seeclaude remote-controldying inside tmux — planner session goes silently unresponsive #124 (adjacent to the Cockpit: no consumer-facing entry point — plugin ships cockpit.sh but nothing can invoke it outside the self-hosted repo #133/Consumer repos carry stale vendored .claude/scripts that shadow the fresh plugin cache — manage them or stop vendoring #134 script-drift work); left untouched.🤖 Generated with Claude Code