Skip to content

feat(harness): cron-less loop daemon under systemd (issue #102) - #103

Merged
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-102-cronless-loop-daemon
Jul 9, 2026
Merged

feat(harness): cron-less loop daemon under systemd (issue #102)#103
robercano-ghbot merged 2 commits into
mainfrom
feat/issue-102-cronless-loop-daemon

Conversation

@robercano-ghbot

Copy link
Copy Markdown
Collaborator

Closes #102.

Summary

Replaces the session-scoped CronCreate PR loop with a systemd --user-supervised daemon, plus docs for the whole architecture.

Harness (previous phase, already reviewed/approved):

  • .claude/scripts/loop-event.sh — one firing: runs the deterministic tick (loop-tick.sh), parses its LAST-line verdict byte-identical (issue Harden PR-loop tick: deterministic loop-tick.sh with single verdict, in-flight detection, spawn lock #81 contract — never re-derived), and emits a loop-event: action=... block. Touches no model/driver process itself.
  • .claude/scripts/loop-daemon.sh — the forever loop. Each iteration runs loop-event.sh; on action=none it sleeps and loops with zero drivers spawned; on an actionable verdict it spawns exactly one contained driver (setsid timeout --kill-after=30s <90m default> claude -p ... --output-format json, whole-process-group timeout kill), ledgers it to .claude/state/loop-runs.log, then sleeps for an adaptive duration read off the census's cadence=FAST|WATCH|IDLE line (60/300/900s, 300s fallback).
  • systemd unit templates (pr-loop.service, claude-rc.service) + arm-loop.sh installer, wired through the existing @orchestrator-managed scaffold/sync marker machinery, and offered from /orchestrator:setup's "arm the loop" step (Linux/WSL2-aware) alongside the legacy /pr-loop cron.

This phase (docs):

  • docs/USAGE.md — new "Cron-less loop (daemon)" section: architecture (pr-loop.serviceloop-daemon.shloop-event.sh/loop-tick.sh; claude-rc-<repo>.service → tmux → claude remote-control for spawning planning sessions from claude.ai/mobile), the adaptive cadence table, the loop-runs.log ledger line format, read-only supervision (ledger + live append-only JSONL transcript tail + claude --resume <id> --fork-session), the kill-and-let-it-re-advance intervention model (never steer a running driver), Linux vs WSL2 setup differences (systemd=true in /etc/wsl.conf + optional schtasks Windows-logon autostart), and a failure-contract table — including the one non-self-healing state: a driver pushes feat/issue-N-* but dies before opening the PR → census reports N as in_flightloop-tick.sh's advance check refuses every tick (logged each time, action=none) with no TTL/self-heal → manual fix is to delete the abandoned branch or open the PR by hand.
  • docs/GETTING_STARTED.md — the onboarding summary and the "arm the loop" checklist step now lead with the daemon as the recommended default; the legacy /orchestrator:pr-loop cron stays documented as the fallback for environments without systemd. Never run both firing sources against the same repo at once (safe — loop-tick.sh's spawn lock prevents a double-spawn — just wasteful).

Gates & review

  • GATES_FILE=.claude/self/gates.json gate.sh build|lint|test all green (docs-only diff this phase; harness phase's own gates — including loop-daemon.test.sh/loop-event.test.sh — passed and were reviewed in the prior phase).
  • Harness phase reviewed through correctness and tests lenses, both approve, consensus all.

🤖 Generated with Claude Code

https://claude.ai/code/session_01EtQ3Zexq39jTL1TSYmoGxi

robercano and others added 2 commits July 9, 2026 20:29
Replaces the session-scoped CronCreate loop with a systemd-supervised
daemon: loop-event.sh runs the deterministic tick and parses its verdict
(never re-deriving it, per issue #81) without touching any model process;
loop-daemon.sh owns the setsid+timeout driver containment, adaptive
FAST/WATCH/IDLE sleep from the census cadence line, and the
.claude/state/loop-runs.log run ledger. action=none spawns zero drivers.

Scaffolds systemd unit templates (pr-loop.service, claude-rc.service) and
an arm-loop.sh installer through the existing @orchestrator-managed marker
machinery (scaffold.sh/sync.sh/MANIFEST.md), and wires setup's "arm the
loop" step to offer the daemon (Linux/WSL2-aware) alongside the legacy
/pr-loop cron, which now points at the daemon as the recommended
replacement.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EtQ3Zexq39jTL1TSYmoGxi
USAGE.md gains a "Cron-less loop (daemon)" section covering the
pr-loop.service/claude-rc.service architecture, adaptive
FAST/WATCH/IDLE cadence, the loop-runs.log ledger, read-only
supervision (ledger + live JSONL transcript + --resume --fork-session),
kill-and-let-it-re-advance intervention, WSL2's systemd=true
prerequisite, and the failure-contract table -- including the one
non-self-healing state (a driver's abandoned feat/issue-N-* branch
wedges ADVANCE for that issue until a human deletes the branch or
opens the PR). GETTING_STARTED.md's onboarding summary and "arm the
loop" checklist step now lead with the daemon as the recommended
default, with the legacy /orchestrator:pr-loop cron kept as the
fallback for environments without systemd.

Co-Authored-By: Claude Fable 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01EtQ3Zexq39jTL1TSYmoGxi
@robercano-ghbot
robercano-ghbot merged commit 10f3e73 into main Jul 9, 2026
9 checks passed
@robercano-ghbot
robercano-ghbot deleted the feat/issue-102-cronless-loop-daemon branch July 9, 2026 19:56
robercano added a commit that referenced this pull request Jul 10, 2026
…f-hosted repo (issue #102 residue)

PR #103 shipped these as setup templates; sync only re-stamps existing
copies, so the self-hosted repo needs them materialized once. Plain
copy_if_absent semantics, markers intact (v1).

Co-Authored-By: Claude Fable 5 <[email protected]>
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.

Loop: cron-less scheduling — local daemon under systemd + remote-control supervision (Looper-style)

2 participants