Cron Scheduling
The cron engine is the scheduler behind all autonomous behavior in OpenAlice. It manages timed jobs and fires events into the EventLog when they're due. The engine itself doesn't run the jobs — it just announces them. Listeners (CronListener, Heartbeat, SnapshotScheduler) decide what to do.
Schedule Types
| Type | Format | Example | Description |
|---|---|---|---|
at | ISO timestamp | "2025-06-01T14:00:00Z" | One-shot. Auto-disables after firing. |
every | Duration string | "30m", "2h", "5m30s" | Recurring interval. |
cron | 5-field expression | "0 9 * * 1-5" | Standard cron (minute, hour, dom, month, dow). |
Duration strings support hours (h), minutes (m), and seconds (s) in combination: "1h30m", "5m30s".
How Jobs Execute
CronEngine timer fires
↓
EventLog.append('cron.fire', { jobId, jobName, payload })
↓
CronListener receives event
↓
AgentCenter.askWithSession(payload, cronSession)
↓
Alice processes with full AI pipeline (tools, reasoning)
↓
ConnectorCenter.notify(result) → last-interacted channel
↓
EventLog.append('cron.done' | 'cron.error')
The cron session (data/sessions/cron/default.jsonl) is independent from your chat sessions. Jobs run serially — if one is still processing when the next fires, the second is skipped.
AI Tools
Alice can manage cron jobs through conversation:
| Tool | Description |
|---|---|
cronAdd | Create a new job with name, schedule, and payload |
cronList | List all jobs with runtime state (next run, last status, errors) |
cronUpdate | Modify name, schedule, payload, or enabled state |
cronRemove | Delete a job permanently |
cronRunNow | Fire immediately (bypasses schedule, doesn't affect next run) |
You: Check the ETH funding rate every 4 hours.
Alice: [calls cronAdd]
Created "ETH Funding Rate Check" (id: a1b2c3d4)
Schedule: every 4h
Next run: in 4 hours
You: Show me all my scheduled jobs.
Alice: [calls cronList]
1. ETH Funding Rate Check — every 4h — next: 2025-03-15 18:00
2. Morning Portfolio Review — cron "0 9 * * 1-5" — next: Monday 9:00 AM
3. __heartbeat__ — every 30m — next: 14:30 (internal)
Persistence
Jobs are stored in data/cron/jobs.json as a single JSON file with atomic writes (write to temp file, then rename). This survives crashes — on restart, the engine reloads all jobs and re-arms timers.
Error Handling
Failed jobs get exponential backoff:
| Consecutive Errors | Backoff |
|---|---|
| 1 | 30 seconds |
| 2 | 1 minute |
| 3 | 5 minutes |
| 4 | 15 minutes |
| 5+ | 1 hour |
The consecutive error count resets on success. One-shot (at) jobs that fail are still disabled — they don't retry.
Internal Jobs
Jobs with double-underscore names (__heartbeat__, __snapshot__) are internal — the CronListener ignores them. They have dedicated handlers:
This separation prevents internal jobs from being routed through the AI pipeline unnecessarily.