AgentStack
Browse Sign in
Browse Why AgentStack Sell Docs
Sign in
SKILL verified MIT Self-run

Ww Keepalive

skill-lee-fuhr-claude-operator-skills-ww-keepalive · by lee-fuhr

Stands up a self-relaying overnight worker in about 60 seconds, a `claude -p` loop that survives 5-hour caps, session death, and reboots so a long campaign keeps running while you’re away from the keyboard. Owns the resilience mechanism (fresh `claude -p`, never `--resume`; gates + liveness checks + dual-account failover + `--permission-mode auto`; a one-paste plist install; and a standard queue.…

No reviews yet
0 installs
37 views
0.0% view→install

Install

$ agentstack add skill-lee-fuhr-claude-operator-skills-ww-keepalive

✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access Used
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

View the full security report →

Verified badge

Passed review? Show it. Paste this badge into your README, it links to the public security report.

AgentStack Verified badge Links to your public security report.
[![AgentStack Verified](https://agentstack.voostack.com/badges/verified.svg)](https://agentstack.voostack.com/security/report/skill-lee-fuhr-claude-operator-skills-ww-keepalive)

Reliability & compatibility

Security review passed
0 installs to date
no reviews yet
2mo ago

Declared compatibility

Claude CodeClaude Desktop

Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.

Preview Execution monitoring

We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.

How agent discovery & health will work →
Are you the author of Ww Keepalive? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

> Part of Claude Code operator skills: a collection of skills for running a real Claude Code setup.

keepalive: the self-relaying overnight worker

Most autonomous-run setups eventually re-invent the same thing: a loop that survives session caps, the same plist install, the same failover logic, each one built slightly differently and drifting out of sync over time. This skill is that one mechanism, built once, so anything that needs a long unattended run can build on it instead of reinventing it.

The engine: one implementation, campaigns are conf files

The mechanism is code, not a pattern to copy: one canonical engine.sh, kept in a stable location (e.g. ~/.claude/kept-alive/engine.sh). Standing up a campaign = write a small .conf in the lane + a 4-line shim the plist calls (exec /bin/bash /path/to/engine.sh /lane/.conf). NEVER copy-paste a keepalive script again, since copy-drift is exactly what this kills. Several independent lanes needing the same binary-path fix or hand-wired quota gate, one at a time, is the signal that a shared engine was overdue; with the engine, one fix lands everywhere at once.

Hard-won lesson: bash 3.2 unbound-variable trap. launch_once()‘s "${sp[@]}" (an optional --system-prompt array) throws unbound variable under set -u on macOS’s default bash 3.2 whenever the optional system-prompt var is unset, true of most real confs. This aborts BEFORE claude is ever invoked, but the nonzero exit can get misread by the retry loop as an ordinary account cap and retried forever, silently. One production campaign did zero real work for 5 straight days while its own log read as merely “capped.” Fix: "${sp[@]}"${sp[@]+"${sp[@]}"}, the standard bash-.conf. It evaluates every gate and prints the resolved launch command, burns nothing. Deploy engine/conf changes via mv` (atomic: a running bash script must never be edited in place while a fire could be mid-read of it).


⚠️ THE ONE NON-NEGOTIABLE MECHANISM LESSON (read this first)

The kept-alive must launch a FRESH claude -p "$(cat resume.md)" each fire, NOT claude -p --resume .

Two campaigns were once stood up with a session-resume scaffolder instead. Both failed silently on every single fire with No conversation found with session ID …exit 0zero work all night. Root cause: claude -p --resume cannot reattach to a live interactive session’s id (only headless-spawned sessions are resumable that way), and it couples the loop to fragile session state regardless.

The robust pattern: the loop re-reads a brain file (resume.md) and starts a fresh stateless claude -p with that brain as the prompt every fire. The brain carries ALL context and tells the worker to reconcile against done.md / queue.md / state.json and never redo finished work. Statelessness IS the feature: it survives caps, session death, and reboots, because there is no session to lose.

(Exception, if you have a strong in-thread preference: claude -p --resume --permission-mode auto can resume the SAME session in-thread for full-context continuity, but ONLY on a session you KNOW was headless-spawned and resumable, never a live interactive session’s id, and it loses dual-account failover, since a different account can’t resume another account’s session. Default to fresh-claude -p for resilience.)


The mechanism (what this skill owns)

1. The keepalive.sh gates: exit cleanly, in order, on any

templates/keepalive.sh checks, in this order, and exits 0 on any (re-checked inside the loop too; one early bug had a fallback path skip the STOP re-check and spin chunks overnight):

  1. STOP file present → exit (the kill-switch; you can drop it to halt everything).
  2. time.time() > state.deadline → exit (deadline is an absolute epoch timestamp).
  3. state.complete → exit (the worker sets this true when the queue is dry).

2. pgrep-banner liveness (real, not heartbeat)

The brain prints a unique __PGREP_TAG__ banner at the very start of every chunk. The keepalive does pgrep -f __PGREP_TAG__: present → a chunk is genuinely running, skip; absent → relaunch regardless of heartbeat age. This is more reliable than trusting state.heartbeat, which a long dense tool-run can let go stale and trigger a double-drive. The banner is load-bearing: the brain template instructs the worker to print it first thing.

3. Interactive interlock

A live cockpit session writes interactive.pid + keeps a fresh heartbeat. The keepalive stands down while that PID is alive AND its heartbeat is fresh ( $TAG ; __WORKDIR__ -> the repo/target to cd into (or $LANE)

resume.md : __PGREPTAG_ -> $TAG ; __MISSION__/__POSTURE__/__SCOPE__/__GROUNDINGDOCS_

(a bounded-vs-max-effort sibling skill supplies POSTURE; fill MISSION + SCOPE

from the campaign)

queue.md/done.md/state.json : __CAMPAIGN__/__PHASE__ -> real values

4. Seed state.json: started=now, heartbeat=now, deadline=absolute epoch (e.g. now + 18h), complete=false.

5. Write queue.md with the real priority-ordered backlog (each item with a Done test).


**Then hand yourself this one-paste plist install (you run it, the agent does not):**

```bash
LABEL="com.yourname.-keepalive"; LANE="$HOME/path/to/your/campaigns/_"
cat > ~/Library/LaunchAgents/$LABEL.plist 

Label$LABEL
ProgramArguments/bin/bash$LANE/keepalive.sh$LANE
StartInterval900
RunAtLoad
StandardOutPath$LANE/keepalive.out
StandardErrorPath$LANE/keepalive.err

PLIST
launchctl unload ~/Library/LaunchAgents/$LABEL.plist 2>/dev/null; launchctl load ~/Library/LaunchAgents/$LABEL.plist
echo "loaded $LABEL, fires every 15min, RunAtLoad false"

Note the plist passes $LANE as the keepalive’s $1 (the state dir), so a copied script can never point back at the original lane. If you keep a registry of what background services are running on your machine, register the new LaunchAgent there at install time. An unregistered background process is the kind of thing that gets rediscovered confused, months later.

To STOP it:

LABEL="com.yourname.-keepalive"; LANE="$HOME/path/to/your/campaigns/_"
touch "$LANE/STOP"                                          # halt the next fire immediately
launchctl unload ~/Library/LaunchAgents/$LABEL.plist        # tear the loop down
rm ~/Library/LaunchAgents/$LABEL.plist                      # remove it
# (or set state.json complete=true to let it self-exit on the next fire)

The heartbeat (optional but recommended)

For the interactive interlock + a recent-activity signal, touch the heartbeat from the working session each meaningful turn: templates/ does not include a heartbeat script because it’s a one-liner. python3 -c "import json,time;p='$LANE/state.json';s=json.load(open(p));s['heartbeat']=time.time();json.dump(s,open(p,'w'))". The pgrep-banner is the primary liveness signal; the heartbeat is the interlock + freshness signal.


How a bounded/max-effort sibling skill should call this

Any skill that runs a bounded (one-task-per-cycle, delegate-and-verify) or max-effort (workflows-spawning-workflows) autonomous overnight worker should STOP duplicating the mechanism and reference this skill instead. Each keeps ONLY its posture section (the brain personality) and adopts a one-line reference for the entire resilience backbone:

> Resilience backbone: stand up the kept-alive via the ww-keepalive skill (~/.claude/skills/ww-keepalive/SKILL.md). It owns the fresh-claude -p mechanism (never --resume), the keepalive.sh gates + pgrep-banner liveness + interactive interlock + dual-account failover + --permission-mode auto, the user-run one-paste plist install, and the tasklist.md standard (queue.md + done.md scaffolded in the lane). This skill supplies ONLY the brain’s POSTURE for the resume.md template’s __POSTURE__ slot: bounded = one bounded unit/cycle, the top-tier model writes specs + QCs, cheap models execute, no fan-out. Max-effort = workflows-spawning-workflows, audit-gated, use the quota.

That single line replaces: the “THE ONE NON-NEGOTIABLE MECHANISM LESSON” block, the keepalive.sh template, the gates list, the interlock paragraph, the dual-account paragraph, the --permission- mode auto block, the plist/StartInterval instructions, and the queue.md/done.md description, all of which now live HERE. A sibling skill shrinks to: when-to-use, Step-0 framing, and the POSTURE.


Self-improvement

When a mechanism bug is found (a gate that didn’t fire, a liveness false-positive, a failover that stalled), fix it HERE in templates/ + this doc, once, and every sibling skill inherits it. Do NOT patch a per-campaign copy and leave the template stale; that re-creates the drift this skill exists to kill: one canonical home per thing.

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet, be the first.

Versions

  • v0.1.0 Imported from the upstream source.