Operations: the cron schedule¶
Chad's contract with the day is the cron list. Eleven standing jobs,
all registered by chad-setup.sh, all budget-guarded, all
deliberately conservative — every fire tokenizes a prompt and spawns
a model call, so the rule is: fewer, cheaper runs.
The eleven jobs¶
| Job | Cadence | Budget guard | What it does |
|---|---|---|---|
email-check |
0 2,6-23 * * * (19×/day) |
skip if remaining < 30k | Reads mail via proton-tool, follows EMAIL-POLICY.md, logs to today's memory. Drafts replies through chad-drafter; routes auto ones through chad-autosend-replies, parks draft for review. |
workspace-backup |
every 6h | n/a | chad-backup-to-github with sha-diff check. Ships workspace/ to chad-state. |
issue-triage |
daily 10:00 UTC | skip if remaining < 3×N×70k | chad-issue-triage — scores open issues, routes top 2 through a researcher kind. |
gbrain-dream |
nightly 03:30 UTC | skip if remaining < 50k | chad-gbrain-dream — embed-stale + extract-graph + extract-timeline. Detached. Writes 24h delta digest. |
self-improve |
weekly Sun 03:00 UTC | skip if remaining < 2×budget | chad-self-improve — proposes 1–3 durable improvements based on last week's signal. Draft-only. |
chad-budget-audit |
weekly Mon 04:00 UTC | n/a | Compares last-50-runs telemetry vs task-profiles.json. Rolls up premium spend. Appends recommendations to feedback-proposals.md. |
chad-proposal-apply |
daily 04:30 UTC | n/a | Reads latest proposals JSON, applies narrow safe-list cron tunings. Gated by chad-action-gate chad_self_modify_cron. |
chad-skill-watch |
daily 09:00 UTC | n/a | Diffs the live skill catalog against last snapshot. Surfaces additions/removals/changes. |
memory-curator |
weekly Sat 04:00 UTC | inactivity-gated + budget guard | chad-memory-curator — Hermes-style consolidation. Snapshots first, spawns researcher with curator prompt, draft-only. |
spawn-poll |
every 5min | n/a | chad-spawn-poll — reconciles async gha sub-agent spawns. Skips fresh entries. |
spawn-gc |
weekly Mon 02:30 UTC | n/a | chad-spawn-gc — branch retention for chad-spawn/* on chad-state. |
Why the schedule looks the way it does¶
Several constraints shaped the cadence:
Embedded inference is slow. Chad's primary model
(nvidia/nemotron-3-super-120b-a12b) takes multiple seconds per
turn. Cron prompts are therefore one-line wrapper invocations —
the wrapper does the heavy work in shell or Python, and the model
just acks the result.
Multi-turn tool calling is fragile under embedded models. Heavy work that needs multi-step reasoning happens inside a sub-agent spawn, not in the cron prompt. The cron is the trigger; the spawn is the worker.
The token budget is daily. Honor-based cap of 500k tokens, UTC
reset. A runaway Monday should not drain Tuesday's pool. Every cron
checks chad-budget show --field remaining_tokens near the top;
if reserve is too low, the wrapper falls through to a no-op or a
deferred draft.
Email cadence beats human latency. 19×/day is enough that allowlisted correspondents see a reply within an hour. Going faster (every 30 min, 48×/day) burns ~60% more tokens with no perceptible improvement.
Wrapper invariant¶
Every cron prompt is a one-line wrapper invocation. Example:
Run `chad-gbrain-dream`. The wrapper syncs workspace docs into the
brain, detaches the slow embed/extract steps, and writes its own
summary to today's memory. Confirm it printed `gbrain dream
complete`, then exit.
The model fires the wrapper. The wrapper does the work. Long-running
operations detach via nohup ... & disown and write results to
memory/<UTC-date>.md — the next cron tick reads that log to
confirm completion.
This pattern decouples the model's wall-clock time from the work's wall-clock time. A 5-minute embedding job doesn't tie up the embedded model's session for 5 minutes.
Where the work actually happens¶
The cron prompts are short. The wrappers are not. The full source
catalog is in scripts/chad-cron-wrappers/:
- Inbox sweep:
chad-mail-check,chad-mail-send,chad-email-check-cron,chad-autosend-replies,chad-drafter,chad-action-gate - Brain maintenance:
chad-gbrain-dream,chad-ensure-today-memory - Issue triage:
chad-issue-triage-cron - Self-improvement:
chad-self-improve,chad-budget-audit,chad-proposal-apply,chad-skill-watch - Memory curator:
chad-memory-curator,chad-memory-snapshot - Spawn substrate:
chad-spawn-poll,chad-spawn-gc - Plumbing:
chad-route-prompt,chad-auth-context,chad-premium,chad-premium-client,chad-dump-logs,chad-cron-reload,chad-workspace-backup,chad-log-event
Each wrapper has SPDX headers and shellcheck-clean. The source repo is the canonical reference; this page is the schedule overview.
Audit & observability¶
- Every wrapper appends to today's memory file
(
memory/<UTC-date>.md) so the next session sees a record of what fired. - Every action-gate decision lands in
auto-action-log.jsonl. See Autonomy. - Every spawn writes to
subagents/<task-id>/. See Orchestrator. - Every cron run is logged by openclaw at
~/.openclaw/cron-runs.jsonl. Failures surface via the nextchad-budget-audit.
Per-cron context tuning¶
Each cron job runs in an isolated session, so by default the gateway
loads its full bootstrap context — skill descriptions, identity
files, plugin manifests — into every fire. For wrapper-only crons
that only need to ack a sentinel line, that's wasteful. The gateway
exposes a --light-context flag that swaps the bootstrap for a
slimmed one:
# Apply to one cron
openclaw cron edit <job-id> --light-context
# Apply to all
for id in $(openclaw cron list --json | jq -r '.jobs[].id'); do
openclaw cron edit "$id" --light-context
done
Observed input-token savings on Nemotron, same wrapper, same sentinel:
| Cron | Before | After | Reduction |
|---|---|---|---|
chad-skill-watch |
~177k | ~48k | 73% |
chad-proposal-apply |
~177k | ~32k | 82% |
chad-budget-audit |
~64k → still varies | ~64k | depends on findings count |
chad-setup.sh will re-apply --light-context to every cron it
registers. If you migrate older crons by hand, the for-loop above
is idempotent.
Idle-timeout floor¶
The Nemotron path occasionally hangs mid-stream. Without an idle
timeout, hung sessions can sit for many minutes before the cron
scheduler gives up. Set a floor in openclaw.json:
{
"agents": {
"defaults": {
"model": { "primary": "inference/nvidia/nemotron-3-super-120b-a12b" },
"timeoutSeconds": 600,
"llm": { "idleTimeoutSeconds": 60 }
}
}
}
A hung session now fails fast (60 s of no model output) and the
next cron tick retries. Combine with --light-context and most
wrapper-only crons finish in 30-100 s.
Delivery flags (load-bearing)¶
Every cron job must be registered with --no-deliver
--best-effort-deliver, persisting as delivery.mode = "none",
delivery.bestEffort = true in cron/jobs.json. Without these,
the gateway's default behavior is "deliver the wrapper's stdout to
the last messaging channel," which fails closed when no channel is
configured (the chad sandbox has none) and surfaces as an
Error cron status — even when the wrapper itself ran fine.
chad-setup.sh registers every chad-owned cron with these flags,
patches pre-existing crons on every run (idempotent), and the
chad-readme calls this out as load-bearing.
What changed recently¶
- Added
memory-curator(weekly Sat) — Hermes-style memory consolidation. Inactivity-gated. - Added
spawn-poll(every 5 min) — async gha spawn reconciler. - Added
spawn-gc(weekly Mon) — chad-state branch retention. - Added
chad-proposal-apply(daily) andchad-skill-watch(daily) to close the self-improvement loop alongsidechad-budget-audit. gbrain-dreamcadence corrected to 03:30 UTC (was sometimes documented as 03:00).email-checkreduced from 48×/day to 19×/day for ~60% token savings. See the introduction for context.--light-contextflag applied to every cron — typical 70–80% input-token reduction on wrapper-only fires.agents.defaults.llm.idleTimeoutSeconds = 60added — bounds worst-case latency when Nemotron hangs.