skills/nohup-poll/SKILL.md
Launch long-running commands (>4 min) via nohup and poll at kv_cache.long_poll_seconds (default 240s) to keep KV cache warm.
npx skillsauth add ryderfreeman4logos/cli-sub-agent nohup-pollInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Launch long-running commands as true background processes and poll at the
configured long-poll interval from kv_cache.long_poll_seconds (default 240s).
Each poll is a separate Bash tool call that triggers a new API request,
keeping the provider's KV cache warm.
Use this skill when the caller needs periodic API activity to keep its KV cache warm.
Default CSA guidance assumes a 5-minute cache (long_poll_seconds = 240), while
Max-tier Opus users can raise kv_cache.long_poll_seconds to 3000 for a 1-hour cache.
| Event | Cost | |-------|------| | Cache HIT (prefix reused within TTL) | 1/12.5 of miss price | | Cache MISS (TTL expired or first call) | Full price |
A 10-minute blocking Bash call creates a >5 min gap between API calls. The entire context (100K+ tokens) is re-read at full miss price.
One cold restart on a 100K context ≈ 2500 cache-hit polling rounds.
For a typical 15-minute cargo build:
MUST use for any command expected to exceed 4 minutes:
| Command | Typical Duration | Polls (default ~240s each) |
|---------|-----------------|-------------------|
| cargo build (full, large project) | 5–15 min | 1–4 |
| just pre-commit (with e2e tests) | 5–20 min | 1–5 |
| cargo install --locked <large> | 10–30 min | 2–7 |
| csa run / csa review / csa debate | 5–60 min | 1–15 |
| Data processing / rsync | varies | varies |
Do NOT use for commands under 4 minutes — the overhead isn't worth it.
Do NOT use for csa session wait — call csa session wait directly because it
already applies the internal long-poll cap.
Look for scripts/bg.sh in the project root. If missing, create it:
mkdir -p scripts && cat > scripts/bg.sh << 'BGEOF'
#!/bin/bash
set -euo pipefail
if [ $# -lt 2 ]; then echo "Usage: bg.sh <logfile> <command...>" >&2; exit 1; fi
LOGFILE="$1"; shift
LOGDIR="$(dirname "$LOGFILE")"
mkdir -p "$LOGDIR"
LOGFILE="$(cd "$LOGDIR" && pwd)/$(basename "$LOGFILE")"
export LOGFILE
nohup bash -c '"$@"; echo $? > "${LOGFILE}.exitcode"' _ "$@" >> "$LOGFILE" 2>&1 &
PID=$!; echo "PID=$PID LOG=$LOGFILE"
sleep 3
if kill -0 "$PID" 2>/dev/null; then echo "ALIVE pid=$PID"; exit 0
else
if [ -f "${LOGFILE}.exitcode" ] && [ "$(cat "${LOGFILE}.exitcode")" = "0" ]; then
echo "DONE pid=$PID exit=0"; exit 0
fi
echo "DEAD pid=$PID exit=$(cat "${LOGFILE}.exitcode" 2>/dev/null || echo unknown)" >&2
tail -20 "$LOGFILE" >&2; exit 1
fi
BGEOF
chmod +x scripts/bg.sh
bash scripts/bg.sh "/tmp/<task>-$(date +%s).log" <command...>
Parse the output to extract PID and LOG path. If output says DEAD, the
command failed immediately — read the log and stop.
CRITICAL: Each poll MUST be a separate Bash tool call. A while sleep
loop inside one Bash call defeats the purpose — the entire loop is one API
gap and the cache expires.
Single poll command (copy-paste ready):
sleep "$(csa config get kv_cache.long_poll_seconds --default 240)" && if kill -0 <PID> 2>/dev/null; then echo "POLL:RUNNING"; tail -3 "<LOG>"; else echo "POLL:DONE exit=$(cat "<LOG>.exitcode" 2>/dev/null || echo unknown)"; tail -20 "<LOG>"; fi
Set Bash timeout high enough to cover the configured sleep plus margin. With the default 240s poll, 300000 ms is sufficient.
After each poll, decide:
| Output contains | Action |
|----------------|--------|
| POLL:RUNNING | Issue another identical poll (new Bash call) |
| POLL:DONE exit=0 | Success — proceed with workflow |
| POLL:DONE exit=<N> | Failure — read full log, diagnose |
When process finishes, read the full log:
cat "<LOG>"
Or tail a reasonable amount if the log is large:
tail -100 "<LOG>"
If you have independent work to do while waiting (read-only research, documentation, etc.), you can interleave:
kill -0 <PID> 2>/dev/null && echo "STILL_RUNNING" || echo "DONE"
The rule: never let more than kv_cache.long_poll_seconds pass without an API interaction
(any tool call counts — Read, Grep, Bash, etc.).
| Context | Typical TTL | Suggested kv_cache.long_poll_seconds |
|---------|-------------|----------------------------------------|
| Legacy / Haiku / generic 5-min cache | 300s | 240 |
| Max-tier Opus 1-hour cache | 3600s | 3000 |
Keep a safety margin instead of polling exactly at the provider TTL.
Each poll adds ~200–500 tokens of history. After N polls:
| Polls | Duration | Accumulated Tokens | Cost vs Cold Restart (100K ctx) | |-------|----------|-------------------|-------------------------------| | 5 | ~20 min | ~2.5K | 0.2% | | 20 | ~80 min | ~10K | 0.8% | | 100 | ~7 hours | ~50K | 4% | | 350 | ~1 day | ~175K | 14% |
Even polling continuously for a full day costs only ~14% of one cold restart. The break-even point is ~12–18 days of continuous polling.
# Step 1: Launch
bash scripts/bg.sh "/tmp/cargo-build-$(date +%s).log" cargo build --release
# → PID=12345 LOG=/tmp/cargo-build-1743666000.log
# Step 2: First poll (separate Bash call; with default 240s, timeout 300000ms is fine)
sleep "$(csa config get kv_cache.long_poll_seconds --default 240)" && if kill -0 12345 2>/dev/null; then echo "POLL:RUNNING"; tail -3 "/tmp/cargo-build-1743666000.log"; else echo "POLL:DONE exit=$(cat "/tmp/cargo-build-1743666000.log.exitcode" 2>/dev/null || echo unknown)"; tail -20 "/tmp/cargo-build-1743666000.log"; fi
# Step 3: Repeat until POLL:DONE
# Step 1: Launch
bash scripts/bg.sh "/tmp/csa-run-$(date +%s).log" csa run --sa-mode true --tier tier-1 "Implement feature X"
# → PID=23456 LOG=/tmp/csa-run-1743666000.log
# Step 2: Poll
sleep "$(csa config get kv_cache.long_poll_seconds --default 240)" && if kill -0 23456 2>/dev/null; then echo "POLL:RUNNING"; tail -3 "/tmp/csa-run-1743666000.log"; else echo "POLL:DONE exit=$(cat "/tmp/csa-run-1743666000.log.exitcode" 2>/dev/null || echo unknown)"; tail -20 "/tmp/csa-run-1743666000.log"; fi
# Step 1: Launch
bash scripts/bg.sh "/tmp/precommit-$(date +%s).log" just pre-commit
# → PID=34567 LOG=/tmp/precommit-1743666000.log
# Step 2: Poll
sleep "$(csa config get kv_cache.long_poll_seconds --default 240)" && if kill -0 34567 2>/dev/null; then echo "POLL:RUNNING"; tail -3 "/tmp/precommit-1743666000.log"; else echo "POLL:DONE exit=$(cat "/tmp/precommit-1743666000.log.exitcode" 2>/dev/null || echo unknown)"; tail -20 "/tmp/precommit-1743666000.log"; fi
| Wrong | Why | Correct |
|-------|-----|---------|
| while sleep 240; do check; done in one Bash call | Single API gap — cache expires | Separate Bash call per poll |
| sleep 400 && check when kv_cache.long_poll_seconds = 240 | Sleep exceeds the configured safe band | sleep "$(csa config get kv_cache.long_poll_seconds --default 240)" |
| run_in_background: true then forget | No periodic API calls — cache expires | Poll or do other work |
| Blocking Bash with 600s timeout | 600s > TTL — cache expires | Use nohup-poll instead |
| wait <PID> from a later Bash call | PID is not a child of the new shell, so wait returns 127 | Read <LOG>.exitcode written by bg.sh |
development
Use when running a non-blocking CSA background code health scan that uses csa health and csa tokuin estimate to propose refactoring GitHub issues for files over token or complexity thresholds.
data-ai
Recover main-agent context after `/clear`, `/compact`, or lost local thread state by using `csa recall` against recorded Claude main sessions.
tools
Use when: merged PR had HIGH/CRITICAL findings that represent a bug class — extracts reusable coding rule
tools
Use when: review found 2+ independent findings in different files, fix phase can parallelize RECON