Scheduling
Standing cron, interval, and one-shot schedules whose exactly-once fires enter the lineage as ordinary observations — never a second source of truth.
The workspace scheduler lets an app declare standing schedules — cron calendars, fixed
intervals (every), and one-shots (at) — that deterministically append schedule fire
observations into a lineage. The scheduler is an input authority only: it never derives
facts, creates intents, or executes effects. “Every weekday at 09:00, refresh the feed”
composes exactly like everything else:
schedule fire observation → mapper atoms → rules → intent.* → declared effect capability
Fire observations
Fires arrive as schedule.tick observations (reserved source capability:scheduler)
carrying the schedule id and digest, the scheduled_for instant, the fire kind
(occurrence, catch_up, or manual), DST and catch-up metadata, the timezone-database
version, and your declared payload under data.
Occurrence identity is (lineage_id, schedule_id, scheduled_for), appended through the
idempotent observation contract — crash recovery and authority handover resolve to the
original receipt, so “exactly once” is structural. Every schedule mutation itself appends
a control observation with actor context, making the registry rebuildable from the
lineage.
Declaring and managing schedules
Manifest-declared schedules live in tarski.toml under [schedules.<id>] and reconcile
at activation; the API can pause, resume, and trigger them, but update and removal fail
closed with schedule.manifest_origin_immutable — the manifest stays authoritative.
Dynamic schedules are managed by CLI:
tarski schedule list
tarski schedule create
tarski schedule preview
tarski schedule pause
tarski schedule trigger
tarski schedule fires
tarski schedule simulate
The cron dialect is five-field with names, ranges, lists, steps, ?, L, W, #, and
@hourly-style aliases. Restricting both day fields without a ? fails closed. DST gaps
fire once at the transition instant (marked dst_adjusted); folds fire the earlier
instant. Occurrence computation is a pure function pinned by a golden conformance corpus.
Per-schedule catch-up policy covers downtime: skip, fire_once (default), or a bounded
fire_all.
Firing authority
Exactly one firing authority exists per lineage, bound to the committed
effect-authoritative activation: local live (tarski serve / tarski dev), local
offline catch-up (tarski run), or the hosted durable scheduler (which wakes hibernated
workspaces when fires come due). Replay, verification, shadow builds, and fixtures
consume recorded fire observations — they never fire.
Verifying scheduled behavior
tarski schedule simulate renders the exact fire observations across a virtual window as
fixture JSONL — byte-identical across runs — so scheduled behavior is tested like any
other behavior. tarski verify includes a schedules check: manifest definitions
validate, fixture fires must match the payload contract, and simulation determinism is
re-proven per run.
When a fire produces no effects, tarski run names why with stable zero-effect reasons:
schedule_fire.no_intent_derived, schedule_fire.no_effect_admitted,
effect_authority.shadow, http.credential.unresolved, or effect_request.blocked.