OS-persistent is primary. In-process is the complement.
Corrected 2026-07-21 (was inverted).
spine-multi-tenantspine-maintenance-runbooksbun run test:tenant-runbooksscheduled() → marker → removebun run test:cron-os · claim cron-os-persistentbun-cron + cron-os-persistentSurvives reboot. System local time. Fresh process each fire.
tests/journey/cron-os-persistent.test.ts (does not wait a full minute; fires via bun run --cron-title)Dies with the process. UTC. Shared state. No overlap (next fire after
handler Promise settles). Errors match setTimeout (uncaughtException /
unhandledRejection). --hot safe. Disposable via using job = ….
Bun.cron(schedule, handler) → CronJob · wrapper scheduleInProcessusing job = … → Symbol.dispose → stop()--hot re-evalbun run spine:schedule:once · bun run spine:schedule:once -- --tenant=install-verify*/10 * * * * UTC · lib/operations/snapshot-cron.ts · portal/Pages artifactsbun run spine:schedule:once -- --tenant=ops-snapshot · bun run ops:snapshot:onceAlign both with TZ=UTC when you need them to agree.
lib/harness/cron.ts + spine/scheduler.ts + spine/tenants.ts
bun run test:cron # complement + contract docs
bun run test:cron-os # OS-persistent primary journey
bun run spine:schedule:once -- --tenant=install-verify # multi-tenant smoke
Lookup: bun tools/bun-doc-refs.ts url Bun.cron