.claude/skills/openclaw-cron-ghost-jobs/SKILL.md
Fix OpenClaw cron jobs running after being removed from jobs.json. Use when: (1) A cron job keeps executing even though it was deleted from jobs.json, (2) Duplicate deliveries (e.g., Julia gets morning briefing twice), (3) Old/legacy jobs firing alongside their replacements after a migration. Root cause is orphan run state files in ~/.openclaw/cron/runs/ that persist nextRunAtMs independently of the job definition.
npx skillsauth add Dbochman/dotfiles openclaw-cron-ghost-jobsInstall 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.
After removing a cron job from ~/.openclaw/cron/jobs.json, the job continues to
execute on its previous schedule. This causes duplicate deliveries when a new
replacement job is added alongside the still-running old one.
jobs.json but still firesls ~/.openclaw/cron/runs/ shows JSONL files for job IDs not in jobs.jsonOpenClaw's cron subsystem persists run state in per-job JSONL files at
~/.openclaw/cron/runs/<job-id>.jsonl. Each run appends a JSON line with
nextRunAtMs indicating when to fire next. The cron scheduler reads these
files independently — if a run file exists with a future nextRunAtMs, the
job will execute even if its definition is gone from jobs.json.
Removing a job from jobs.json does NOT automatically clean up its run file.
ssh dylans-mac-mini 'python3 -c "
import json, os
with open(\"/Users/dbochman/.openclaw/cron/jobs.json\") as f:
job_ids = {j[\"id\"] for j in json.load(f)[\"jobs\"]}
for f in sorted(os.listdir(\"/Users/dbochman/.openclaw/cron/runs/\")):
run_id = f.replace(\".jsonl\", \"\")
if run_id not in job_ids:
print(f\"ORPHAN: {f}\")
"'
ssh dylans-mac-mini 'python3 -c "
import json, os, time
runs_dir = \"/Users/dbochman/.openclaw/cron/runs/\"
now_ms = int(time.time() * 1000)
for f in sorted(os.listdir(runs_dir)):
with open(os.path.join(runs_dir, f)) as fh:
lines = fh.readlines()
if lines:
last = json.loads(lines[-1])
next_run = last.get(\"nextRunAtMs\", 0)
if next_run > now_ms:
print(f\"ACTIVE GHOST: {f} nextRun={next_run}\")
"'
Delete the orphan run files:
ssh dylans-mac-mini 'trash ~/.openclaw/cron/runs/<orphan-job-id>.jsonl'
No gateway restart is needed — the cron subsystem will simply not find the run file on its next timer tick.
When removing cron jobs from jobs.json, always also delete their run files:
# Remove from jobs.json (edit the file)
# Then clean up the run file
trash ~/.openclaw/cron/runs/<job-id>.jsonl
After cleanup:
# Only active job IDs should have run files
ls ~/.openclaw/cron/runs/
# Compare against:
python3 -c "import json; [print(j['id']) for j in json.load(open('/Users/dbochman/.openclaw/cron/jobs.json'))['jobs']]"
deleteAfterRun: true) clean up their own run files after executionkind: "cron") accumulate run history indefinitelyjobs.json but does NOT scan for orphan run filestools
Use exact configured Reolink cameras through the local Home Hub for availability and power status, fresh stills, visual commentary, protected Dylan/Julia/household sharing, and reversible spotlight control. Supports trusted owner tasks and explicitly scoped proactive automations; not for Nest or Ring cameras, arbitrary recipients, recordings, account changes, or raw camera APIs.
data-ai
Privately manage Dylan and Julia's household plant inventory and care history by physical location, bed, and exact Flower Cam view. Use for confirmed plant onboarding from camera conversations, camera- or bed-filtered inventory, record corrections, individual or whole-bed care, and private filtered exports. Pair with reolink-camera when an owner asks about plants visible in Flower Cam images.
testing
Inspect and control the physically secured Reachy Mini at Crosstown through ClawBody. Use for requests to check Reachy, look around, express an emotion, play any official emotion or dance preset, speak proactively, mute or unmute its microphone, stop movement, or describe what its camera sees.
tools
Handle Reachy/iMessage handoffs, selective durable memory, forgetting, and diagnostics; automatic context comes from the gateway plugin.