Cron Scheduler
Convention: See skills/conventions/test-before-bulk.md — test every cron job on 3-5 items first.
Contract
This skill guarantees:
- Schedule staggering: max 1 job per 5-minute slot, no collisions
- Quiet hours gating: timezone-aware, with user-awake override
- Thin job prompts: jobs say "Read skills/X/SKILL.md and run it" (no inline 3000-word prompts)
- Idempotency: jobs can run twice without duplicate side effects
- Results saved as reports:
reports/{job-name}/{YYYY-MM-DD-HHMM}.md
Phases
- Define job. Name, schedule (cron expression), skill to run, timeout.
- Validate schedule. Check no collision with existing jobs (5-minute offset rule).
- Slots: :05, :10, :15, :20, :25, :30, :35, :40, :45, :50
- If collision detected, suggest the next available slot
- Check quiet hours. Default: 11 PM - 8 AM local time.
- Override: user-awake flag (if user is active, quiet hours suspended)
- During quiet hours: save output to held queue
- Morning contact releases the backlog
- Register with host scheduler. OpenClaw cron, Railway cron, crontab, or process manager. Each registered entry should execute via Minions, not
agentTurn. See skills/conventions/cron-via-minions.md for the rewrite pattern (PGLite uses --follow, Postgres uses fire-and-forget + --idempotency-key on the cycle slot). GBrain's v0.11.0 migration auto-rewrites entries for built-in handlers; host-specific handlers need a code-level registration per docs/guides/plugin-handlers.md.
- Write thin prompt. Job prompt is one line: "Read skills/{name}/SKILL.md and run it."
Idempotency Requirement
Every cron job MUST be idempotent:
- Running the same job twice produces the same result (no duplicate pages, no duplicate timeline entries)
- Use checkpoint state files to track progress and resume interrupted runs
- Check for existing output before creating new output
Output Format
Job configuration saved. Report: "Job '{name}' scheduled at {cron expression}. Next run: {time}."
Multi-source brains: use sync --all, not per-source entries
When the brain has 2+ active sources (anything gbrain sources list shows
with a non-null local_path that isn't archived), use one consolidated
cron line instead of N per-source entries.
Preferred (multi-source):
*/5 * * * * gbrain sync --all --parallel 4 --workers 4 --skip-failed
This replaces N per-source lines AND auto-picks-up future sources without
a crontab edit. Concurrency budget: parallel × workers × 2 ≈ 32
connections during the wave (each per-file worker opens its own
2-connection pool). Stay under your Postgres max_connections setting.
Avoid (legacy): separate gbrain sync --source default and
gbrain sync --source zion-brain entries staggered by 5 minutes. They
require manual deconfliction every time a new source is added, and a
slow source can race a fast source on the legacy global gbrain-sync
lock (v0.40.3.0+ uses per-source gbrain-sync:<sourceId> locks but the
per-source cron pattern doesn't benefit from the parallelism that
--all --parallel actually delivers).
gbrain doctor surfaces the recommended line as a sync_consolidation
check whenever it detects 2+ active sources. Paste-ready from there.
Anti-Patterns
- Scheduling jobs at the same minute (:00 for everything)
- Inline 3000-word prompts in cron jobs (use skill file references)
- Running cron jobs without testing on 3-5 items first
- Jobs that produce different output on re-run (not idempotent)
- Sending notifications during quiet hours (save to held queue instead)
- Separate per-source
gbrain sync --source <id> cron entries when
gbrain sync --all --parallel N --workers N would replace them with
one line that auto-picks-up future sources.
Source: garrytan/gbrain → skills/cron-scheduler/SKILL.md
1---2name: cron-scheduler3description: | Schedule management with staggering, quiet hours, and wake-up override. Validates schedules, prevents collisions, and gates delivery during quiet hours.4---5
6
7# Cron Scheduler
8
9> **Convention:** See `skills/conventions/test-before-bulk.md` — test every cron job on 3-5 items first.
10
11## Contract
12
13This skill guarantees:
14- Schedule staggering: max 1 job per 5-minute slot, no collisions
15- Quiet hours gating: timezone-aware, with user-awake override
16- Thin job prompts: jobs say "Read skills/X/SKILL.md and run it" (no inline 3000-word prompts)
17- Idempotency: jobs can run twice without duplicate side effects
18- Results saved as reports: `reports/{job-name}/{YYYY-MM-DD-HHMM}.md`
19
20## Phases
21
221. **Define job.** Name, schedule (cron expression), skill to run, timeout.
232. **Validate schedule.** Check no collision with existing jobs (5-minute offset rule).
24 - Slots: :05, :10, :15, :20, :25, :30, :35, :40, :45, :50
25 - If collision detected, suggest the next available slot
263. **Check quiet hours.** Default: 11 PM - 8 AM local time.
27 - Override: user-awake flag (if user is active, quiet hours suspended)
28 - During quiet hours: save output to held queue
29 - Morning contact releases the backlog
304. **Register with host scheduler.** OpenClaw cron, Railway cron, crontab, or process manager. **Each registered entry should execute via Minions, not `agentTurn`.** See `skills/conventions/cron-via-minions.md` for the rewrite pattern (PGLite uses `--follow`, Postgres uses fire-and-forget + `--idempotency-key` on the cycle slot). GBrain's v0.11.0 migration auto-rewrites entries for built-in handlers; host-specific handlers need a code-level registration per `docs/guides/plugin-handlers.md`.
315. **Write thin prompt.** Job prompt is one line: "Read skills/{name}/SKILL.md and run it."
32
33## Idempotency Requirement
34
35Every cron job MUST be idempotent:
36- Running the same job twice produces the same result (no duplicate pages, no duplicate timeline entries)
37- Use checkpoint state files to track progress and resume interrupted runs
38- Check for existing output before creating new output
39
40## Output Format
41
42Job configuration saved. Report: "Job '{name}' scheduled at {cron expression}. Next run: {time}."
43
44## Multi-source brains: use `sync --all`, not per-source entries
45
46When the brain has 2+ active sources (anything `gbrain sources list` shows
47with a non-null `local_path` that isn't archived), use one consolidated
48cron line instead of N per-source entries.
49
50**Preferred (multi-source)**:
51
52```cron
53*/5 * * * * gbrain sync --all --parallel 4 --workers 4 --skip-failed
54```
55
56This replaces N per-source lines AND auto-picks-up future sources without
57a crontab edit. Concurrency budget: `parallel × workers × 2 ≈ 32`
58connections during the wave (each per-file worker opens its own
592-connection pool). Stay under your Postgres `max_connections` setting.
60
61**Avoid (legacy)**: separate `gbrain sync --source default` and
62`gbrain sync --source zion-brain` entries staggered by 5 minutes. They
63require manual deconfliction every time a new source is added, and a
64slow source can race a fast source on the legacy global `gbrain-sync`
65lock (v0.40.3.0+ uses per-source `gbrain-sync:<sourceId>` locks but the
66per-source cron pattern doesn't benefit from the parallelism that
67`--all --parallel` actually delivers).
68
69`gbrain doctor` surfaces the recommended line as a `sync_consolidation`
70check whenever it detects 2+ active sources. Paste-ready from there.
71
72## Anti-Patterns
73
74- Scheduling jobs at the same minute (:00 for everything)
75- Inline 3000-word prompts in cron jobs (use skill file references)
76- Running cron jobs without testing on 3-5 items first
77- Jobs that produce different output on re-run (not idempotent)
78- Sending notifications during quiet hours (save to held queue instead)
79- Separate per-source `gbrain sync --source <id>` cron entries when
80 `gbrain sync --all --parallel N --workers N` would replace them with
81 one line that auto-picks-up future sources.
82
83---
84
85**Source:** [`garrytan/gbrain`](https://github.com/garrytan/gbrain) → `skills/cron-scheduler/SKILL.md`