Quick Start
1
Agent-centric — just enable it, no ceremony
Once the wrapper’s
ConfigYamlScheduleStore is in use, a scheduled agent gets the notepad automatically. The agent reads its notepad from the prompt and writes back by returning a dict with state_updates (or state).2
Direct Python — build the ScheduleJob yourself
No new arguments — the notepad is picked up automatically from the store.
3
YAML parity
How It Works
Each tick loads prior state, prepends a notepad block, runs the agent, then merges and persists whatever the gate and agent emit. The gate is only handedstate= when it opts in (signature-detected), so legacy stateless gates never see a TypeError.
The Notepad Format
The block prepended to the prompt is deterministic — sortedkey=value lines under a fixed header, separated from the message by a blank line.
With prior state {"last_seen_pr": 4821, "cursor": "abc"}:
- Sorted alphabetically by key so ordering is stable across runs.
- Empty state → no notepad block (byte-for-byte the prior behaviour).
- The block is separated from the message by a blank line.
How the Agent Writes Back
Two ways an agent can persist an update:- Return an object with
state_updatesorstate— a rich chat result exposing adict. Only dicts are honoured; a plain string result writes nothing. - A monitor gate’s
GateResult.state_updates— persists on both the go-path and theno_change/ skip path.
Configuration Options
Behaviour is enabled automatically when the runner usesConfigYamlScheduleStore (the wrapper default) — no new ScheduleJob fields, no new imports, no new module exports.
A job with
delete_after_run=True is removed from the store (and its state popped) before the run reaches the executor. The executor therefore does not persist state for one-shot jobs — persisting now would recreate an orphan job_state entry nothing ever cleans up, and would be injected if the same explicit id were reused. Use cross-run memory only for recurring jobs.Custom Stores
Advanced backends implementJobStateStoreProtocol — the same three methods, the same hasattr() capability detection. All three methods are optional; absence means today’s stateless behaviour.
Best Practices
Keep the notepad tiny
Keep the notepad tiny
Store cursors, ids, and hashes — not full payloads. The 16 KiB cap rejects oversized writes and keeps the prior state, so a runaway
set_state can never bloat config.yaml.Reference notepad keys explicitly in the agent's instructions
Reference notepad keys explicitly in the agent's instructions
The agent sees the sorted
key=value lines verbatim. Write instructions like “if last_seen_pr is present, only list PRs above it.” so the agent knows exactly which keys to read and write.Let the gate carry the watermark on quiet ticks
Let the gate carry the watermark on quiet ticks
A
GateResult.state_updates persists even when the tick is no_change / skipped, so the next tick sees the advance. Use this to move a monitor watermark forward on a silent tick.Don't rely on cross-run memory for one-shot jobs
Don't rely on cross-run memory for one-shot jobs
delete_after_run=True skips persistence by design — a one-shot job has no “next run” to read the notepad. Use cross-run memory only for recurring jobs.Related
Scheduler Monitor
The change-detection sibling that carries a hashed watermark
Scheduler Change Detection
Deeper
GateResult / JobStateStoreProtocol referenceScheduler Pre-Run Gate
The stateless sibling — a cheap go/no-go gate
Scheduler Monitor Mode
Declarable
monitor spec that drives change detection
