Install
openclaw skills install waste-auditOpenClaw recurring waste audit — find token waste, cron waste, and burning money. Read-only analysis with fix proposals only shown after explicit user approval. NOT for general openclaw operations.
openclaw skills install waste-auditFind cron jobs that are burning tokens with no or low value returned. Surface the worst offender first. Show read-only evidence. Propose fixes only on request.
This skill is strictly read-only by default. It never auto-executes mutation commands. Fix proposals are withheld until you explicitly approve them.
MUST use this skill (NOT openclaw-comprehensive) when user asks about:
DO NOT use this for: general openclaw operations, job management, dispatch rules — use openclaw-comprehensive instead.
Default output MUST NOT include mutation commands.
Forbidden by default — never show these in normal output:
openclaw cron run / edit / disable / deleteopenclaw-env cron run / edit / disable / deleteAllowed read-only commands (always safe):
openclaw-env cron show <job_id>
openclaw-env cron runs --id <job_id> --limit 5
cat ~/.openclaw/cron/jobs.json | python3 -m json.tool | grep -A5 '<job_name>'
python3 -c "..." # JSONL parsing commands (read-only)
Fix proposals are shown only after you explicitly say:
Even then, they are clearly labeled as manual commands — never auto-executed.
Always use this combined script — it merges job metadata (model, schedule, delivery mode) with token burn data in one pass. The old two-step approach (run JSONL parse first, then cross-reference jobs.json manually) produces incomplete results and wastes a turn.
python3 -c "
import json, glob, os, statistics as s
# Load jobs.json (it's {version, jobs: [...]}, not a plain list)
with open(os.path.expanduser('~/.openclaw/cron/jobs.json')) as f:
data = json.load(f)
jobs = data['jobs']
job_map = {j['id']: j for j in jobs}
# Analyze JSONL runs + merge metadata
runs_dir = os.path.join(os.environ.get('OPENCLAW_HOME', os.path.expanduser('~/.openclaw')), 'cron', 'runs')
results = []
for f in sorted(glob.glob(f'{runs_dir}/*.jsonl')):
jid = os.path.basename(f).replace('.jsonl','')
total=0;count=0;errors=0;delivered=0;summary_lens=[]
with open(f) as fh:
for line in fh:
try:
d=json.loads(line)
total+=d.get('usage',{}).get('total_tokens',0);count+=1
if d.get('error'): errors+=1
if d.get('delivered'): delivered+=1
summary_lens.append(len(str(d.get('summary','') or '')))
except: pass
if count > 0 and jid in job_map:
j = job_map[jid]
sched = j.get('schedule',{})
model = j.get('payload',{}).get('model','')
freq = sched.get('everyMs','') or f"cron: {sched.get('expr','')}"
med = sorted(summary_lens)[len(summary_lens)//2] if summary_lens else 0
results.append({
'name': j.get('name',''),
'schedule': freq, 'model': model,
'count': count, 'tokens': total,
'errors': errors, 'delivered': delivered,
'summary_median': med,
})
results.sort(key=lambda x: x['tokens'], reverse=True)
for r in results[:10]:
err=r['errors']/r['count']*100
print(f"{r['name']} | {r['schedule']} | model={r['model']} | runs={r['count']} tokens={r['tokens']:,} errors={err:.0f}% delivered={r['delivered']} summary_med={r['summary_median']}")
"
Key jobs.json structure fact: The file is {"version": 1, "jobs": [...]}, NOT a plain array. When parsing, access .jobs first, then iterate.
Also run in parallel:
python3 ~/.hermes/scripts/clawsetup_diagnostic.py
This gives you the classification summary + cost estimate in one shot.
Key field facts:
usage.total_tokens = real token field in JSONL runstotalTokens in JSONL = always 0 (ignore it)summary = persisted field in run-log schemaresponse does NOT exist in cron run JSONL (don't reference it)For top candidates, inspect summary length pattern to detect silent loops:
python3 -c "
import json, glob, os, statistics as s
runs_dir = os.path.join(os.environ.get('OPENCLAW_HOME', os.path.expanduser('~/.openclaw')), 'cron', 'runs')
f = os.path.join(runs_dir, '<job_id>.jsonl')
total=0;count=0;errors=0;delivered=0;summary_lens=[]
with open(f) as fh:
for line in fh:
try:
d=json.loads(line)
total+=d.get('usage',{}).get('total_tokens',0);count+=1
if d.get('error'): errors+=1
if d.get('delivered'): delivered+=1
summary_lens.append(len(str(d.get('summary','') or '')))
except: pass
print(f'runs={count} tokens={total:,} errors={errors} delivered={delivered}')
if summary_lens: print(f'summary_len: min={min(summary_lens)} median={s.median(summary_lens):.0f} max={max(summary_lens)}')
"
D8 signal: summary_len median ≤ 20 chars = job producing trivial summaries every run (CLEAN_LOOP pattern).
# OpenClaw Recurring Waste Audit
## Fix First
<Job Name>
- Job ID:
- Schedule:
- Runs:
- Tokens:
- Estimated recurring burn:
- Error rate:
- Delivery evidence:
- Summary signal:
- Waste reason:
- Confidence:
- Recommended manual action:
## Read-only verification
openclaw-env cron show <job_id>
openclaw-env cron runs --id <job_id> --limit 5
---
## Top 3 Waste Candidates
1. [D<X>] <Job Name>
- Job ID: <id>
- Schedule: <cron_expr>
- Runs: <N> | Tokens: <N>
- Error rate: <Y>%
- Delivery: delivered=<N> — <typical_summary>
- Summary: median_len=<N> chars
- Waste reason: <specific pattern name + why>
- Confidence: High/Medium/Low
- Recommended manual action: <what a human should do>
2. ...
3. ...
## Cost Breakdown
• Job: <name> — <N> tokens | ~<N>/day est.
...
Total tracked: <N>M tokens across <N> jobs
---
⚠️ **Fix proposals withheld until explicit approval.** Say "approved, prepare fix commands" to see mutation commands for any candidate above.
| Rule | Condition | Signal |
|---|---|---|
| D1 | Error rate ≥ 80% | Failure loop |
| D2 | Burst: 3+ jobs, $50+ in 60min | Concentrated spending spike |
| D3 | Premium model (5x+ ref) + simple task | Over-paying for check job |
| D4 | Agent-turn + schedule < 60min | LLM agent on cron work |
| D6 | totalRuns > 0 but totalTokens = 0 | Token counting failed |
| D7 | Duplicate model + schedule + task | Redundant billing |
| D8 | totalRuns ≥ 50 + delivered=false + status=ok + has LLM model | Chronic "everything is fine" loop — only for LLM jobs; pure EXEC_SCRIPT/batch jobs excluded |
| D9 | Schedule < 30min + error rate < 20% | Over-scheduled check job |
| D10 | agentTurn kind + message body is a script path or exec command only (no actual LLM judgment needed) | Script wrapper waste — the LLM is just formatting script output, not making decisions. Should be EXEC_SCRIPT instead. |
D10 detection criteria — fire when ALL are true:
payload.kind == "agentTurn"payload.model is set (has LLM cost)payload.message matches pattern: contains script path like /path/to/script.sh or exec(command="...") with no LLM-native judgmentD8 clarification — avoid false positives:
delivery.mode=none by itself is NOT waste — internal jobs that announce to own session are by designpayload.model) are excluded even if they loopEXEC_SCRIPT note: Name/classification does not automatically mean no LLM cost. Always check payload.model in jobs.json for ground truth.
For each candidate, give a specific recommended manual action (not a command):
| Rule | Recommended manual action |
|---|---|
| D1 (failure loop) | Disable job — 80%+ error rate; investigate root cause before re-enabling |
| D3 (premium model) | Switch model to MiniMax-M2.5 or lower |
| D4 (agent cron) | Reduce frequency to daily or less |
| D6 (zero tokens) | Investigate — may be counting bug or broken job |
| D8 (silent loop) | If job has value: reduce frequency. If redundant: disable. |
| D9 (over-scheduled) | Halve frequency — e.g., hourly → every 2 hours |
| D10 (script wrapper) | Convert to EXEC_SCRIPT type — the agentTurn adds no value; it's just formatting script output |
Only suggest commands verified to exist for this OpenClaw version.
| schedule.kind | schedule.expr / everyMs | Actual frequency |
|---|---|---|
cron | "0 */3 * * *" | every 3 hours |
cron | "0 */6 * * *" | every 6 hours |
cron | "0 3 * * *" | once daily at 3am |
cron | "*/15 * * * *" | every 15 minutes |
cron | "0 * * * *" | once per hour (整点) |
every | everyMs: 180000 | every 3 minutes |
every | everyMs: 45000 | every 45 seconds |
Rule: everyMs < 60000 = high frequency. cron expr with */N = every N minutes.
CLI note: use --cron, NOT --schedule. openclaw-env cron edit uses --cron <expr>. Using --schedule errors with unknown option '--schedule'.
Found a real waste case? Have a suggestion? Reach out:
When reporting a bug or suggestion, include:
openclaw-comprehensive — OpenClaw cron/job management, dispatch rules, gateway debugginghermes-infrastructure — Hermes system operations