Skip to main content
A monitor job hashes a watched source each tick and only runs the model when that source has changed — quiet ticks record no_change and spend zero tokens.
The first tick runs and seeds a baseline; later ticks compare the fresh hash to the last one and stay silent until something actually changes.

Quick Start

1

Watch a source

Point monitor at a cheap source. Each tick probes it, hashes the output, and only runs the model on a change:
2

Pin the model for unattended runs

An unattended job created against a cheap default should not silently follow a later, pricier one — snapshot the model and pin it:

Which Gate Should I Use?

Four distinct controls decide whether — and how — a scheduled tick runs.

Run Statuses

A monitor tick records exactly one status so operators can tell “unchanged” apart from “gate said skip”. no_change is distinct from skipped on purpose: it means “the watched source did not change”, not “the gate said don’t run”.

Model-Free Delivery Action

Set command to run a shell command on schedule and deliver its stdout verbatim — no agent, no LLM call.
The command’s process group is killed if it exceeds command_timeout, and that tick is recorded as failed — a hung command can never stall the ticker.

Custom Stateful Gates

Implement JobConditionProtocol.should_run to build your own change-detection gate. A stateful gate accepts the job’s prior state and returns a GateResult that both suppresses an unchanged source and carries the next-tick hash.
When no_change=True, GateResult forces run=False in __post_init__, so a gate can never fire a tick the contract defines as suppressed. Leaving state_updates=None writes nothing — an error probe leaves prior state untouched so a later recovery still suppresses.

Custom State Stores

Implement JobStateStoreProtocol to persist a job’s per-tick scratchpad — a last-seen hash, a cursor, or a watermark.
All three methods are optional for a store to provide — callers detect support with hasattr() and treat absence as today’s stateless behaviour.

Configuration Options

Fields on ScheduleJob that drive monitor mode and model pinning. GateResult fields for stateful gates:

Best Practices

monitor carries per-job state and records no_change; pre_run is a stateless gate that records skipped. Reach for monitor when you need to compare against the last tick, and pre_run when a cheap check answers “is there anything to do right now?”.
Unattended runs fire with no human present. Snapshot provider and model and keep pin_model=True so a job created against gpt-4o-mini never silently inherits a later, pricier default.
A model-free command job runs a real shell command. Keep command_timeout tight so a hung command is killed (with its process group on POSIX) and recorded as failed instead of stalling the ticker.
When a probe fails, return a GateResult with state_updates=None so prior state is preserved. A later recovery to the same output still hashes equal and suppresses, avoiding a spurious change alert.

Async Scheduler

Multi-tenant, at-most-once scheduling primitives

Pre-Run Gate

Stateless go/no-go gate that skips quiet ticks