# Heartbeat Scheduling Combines OpenClaw's HEARTBEAT.md task format with Paperclip's interval-based heartbeat model. Designed for Claude Code agents running on a schedule. ## How it works 1. A scheduler (cron/launchd/systemd) runs `heartbeat-runner.sh` at a fixed interval (e.g., every 30 minutes) 2. The runner reads `HEARTBEAT.md` for task definitions 3. **Emptiness detection**: if the file has no real tasks, skip the API call entirely (saves cost — from OpenClaw) 4. For each task: check if it's due based on its interval and last-run time 5. Run due tasks via `claude -p` with the task's prompt 6. Suppress short acknowledgment responses (<300 chars containing HEARTBEAT_OK) 7. Update `.heartbeat-state.json` with last-run timestamps ## Two execution types (from OpenClaw) ### systemEvent Injects a text event into an existing session. Lightweight, no new session. Use for: notifications, status checks, simple updates. Template: `scripts/templates/cron/system-event.sh` ### agentTurn Fires a full agent turn with its own session. Full context, full tool access. Use for: background autonomous work, complex tasks, multi-step operations. Template: `scripts/templates/cron/agent-turn.sh` ## Startup catchup (OpenClaw pattern) When the runner starts after downtime (e.g., machine was off): - Run `heartbeat-runner.sh --catchup` - Processes up to 5 missed tasks - 5-second stagger between tasks (prevents thundering herd) ## Cost optimization - **Emptiness detection**: No API call if HEARTBEAT.md has no real content - **ackMaxChars suppression**: Responses under 300 chars with HEARTBEAT_OK are logged but not displayed (saves downstream processing) - **Interval-based**: Only run tasks when actually due, not every heartbeat ## Example cron entries ```bash # Run heartbeat every 30 minutes */30 * * * * /path/to/heartbeat-runner.sh >> /tmp/heartbeat.log 2>&1 # Run heartbeat every hour with catchup on restart @reboot /path/to/heartbeat-runner.sh --catchup >> /tmp/heartbeat.log 2>&1 0 * * * * /path/to/heartbeat-runner.sh >> /tmp/heartbeat.log 2>&1 ``` ## State file format `.heartbeat-state.json`: ```json { "email-check": { "last_run": 1712847600 }, "report-generation": { "last_run": 1712844000 } } ```