Skip to main content
Pin a scheduled job to a specific model so unattended runs stay on the exact model you signed off on — and fail closed the moment the default drifts. A scheduled job used to resolve its model at fire time, so a job created against a cheap default silently inherited whatever the default later became. Pinning snapshots the model at creation and fails closed on drift.

Quick Start

1

Agent-first (Python)

Snapshot the model on the ScheduleJob at creation. When model is set and pin_model is True, the run is pinned and drift fails closed.
2

Pin from the CLI

Pass --model to capture a snapshot. The pin is on by default.
3

Follow the default (opt out)

Use --no-pin for a canary or experimental job that should follow whatever the default becomes.

How It Works

The snapshot is taken at add time; the drift check runs at fire time and branches into run-pinned or fail-closed.
A --command (no-LLM) job takes no model turn, so pinning is skipped for it.

BotOS scheduled jobs enforce the pin too

BotOS bots that host their own scheduler (Telegram, Slack, etc.) now enforce pin_model on the _execute_schedule_job path, delegating to the same canonical drift check as the gateway executor. A pinned job whose agent has drifted from the snapshot is recorded failed with the drift reason and no LLM call is made — on both the gateway executor path and the BotOS path. The finally restore is run-scoped, so the pin never leaks into the next attended turn on the shared agent instance.

Which Option?

Three levers plus the no-snapshot default cover every case.

Configuration

ScheduleJob fields

provider / model are persisted only when set, and pin_model is persisted only when opting out (False) — agent-only jobs stay byte-for-byte identical on disk. from_dict restores all three and is unknown-key tolerant.

ScheduleJob API Reference

Full ScheduleJob dataclass reference

Common Patterns

Pin every scheduled job in a hardened deploy

Capture a snapshot on each job so no unattended run can drift onto a costlier default.

Fail closed and deliver the alert

Combine the pin with RunPolicy(deliver_on_failure=True) so a drift is recorded failed and surfaced to the target.

--no-pin for canary / experimental jobs

Follow whatever the default becomes for a job you want tracking the latest model.

Provider-prefix normalisation (no false drift)

A provider-qualified pin (openai/gpt-4o-mini) and a bare-model resolver (gpt-4o-mini) do not false-drift — the embedded provider prefix is lifted out before the comparison. Only the first / splits, so fine-tuned paths like openai/ft:gpt-4o:org::id keep their tail.

Best Practices

An unattended run has no human to catch a costlier default. Pass --model on every scheduled job you would not want silently upgraded — the pin defaults to on, so a snapshot is all it takes.
A fail-closed drift is only useful if someone sees it. Set RunPolicy(deliver_on_failure=True) so a drift lands in your channel instead of a silent failed record.
--no-pin opts a job into following the default forever. Reserve it for canaries and experiments — not for anything whose cost or behaviour you rely on.
A provider-qualified pin like openai/gpt-4o-mini is normalised against a bare-model resolution, so it never false-drifts. Provider is only compared when both sides carry one.

Schedule CLI

All schedule commands and options

Scheduled Run Policy

deliver_on_failure & policy scans for unattended runs

Multi-Tenant Scheduler

Isolate each gateway user’s jobs with a principal owner key

Command Action

No-LLM command jobs — pinning is skipped for these