Skip to content

Cron Schedules

Every recurring job in the stack. Times are local PDT/PST (DST-aware where the runner respects TZ; UTC-fixed where it doesn't).

Active

When (PDT) When (UTC) Mechanism What Source
Always-on launchd com.casey.warehouse-ui uvicorn ui.app:app --port 8765 — the warehouse FastAPI UI. Sources ~/.warehouse_secrets.sh for env, KeepAlive auto-restart, RunAtLoad. ~/Library/LaunchAgents/com.casey.warehouse-ui.plist
Daily 7:00am 14:00 launchd com.casey.warehouse-sync ~/garmin-warehouse/scripts/daily_sync.sh — garmindb sync, ICU refresh, kb embed, R2 backup, Worker cache build, comment scan ~/Library/LaunchAgents/com.casey.warehouse-sync.plist
Sun 6:00am 13:00 (Sun) launchd com.casey.podcast-sync ~/data-ingestion/scripts/podcast-sync.sh — triage + research new episodes across all shows ~/Library/LaunchAgents/com.casey.podcast-sync.plist
Daily 8:00pm 03:00 (next day) Cloudflare Worker 0 3 * * * OTQCheckinAgent.runCheckin() — Telegram check-in for any unlogged scheduled events cloudflare/otq-checkin/wrangler.jsonc
Daily 8:30am 15:30 Cloudflare Worker 30 15 * * * OTQCheckinAgent.runMorningSummary() — Telegram training summary cloudflare/otq-checkin/wrangler.jsonc

DST behavior: - launchd respects local TZ → fires at 7am PDT and 7am PST without intervention. - Cloudflare crons are UTC-fixed → when PST returns in November, evening fires at 9pm and morning at 9:30am local. Edit wrangler.jsonc twice a year, or accept the drift.

Removed (2026-05-05 — teardown-laptop.sh ran, shadow mode short-circuited

after first day of clean R2 → daily_sync round-trip verification)

When Was Replacement Tear-down state
Daily 8:00pm launchd com.casey.warehouse-checkin (evening_checkin.py) Worker runCheckin() (cron 0 3 * * *) plist renamed to .disabled, kept on disk for rollback
8:30/9:30/6:30am launchd com.casey.warehouse-listener (telegram_listener.py, polling) Worker /webhook (Telegram push) plist renamed to .disabled, kept on disk for rollback
Daily 8pm UTC GitHub Actions evening-checkin.yml Worker runCheckin() renamed to .disabled locally; commit + push when ready to stop GHA from firing

To re-enable any of these (disaster recovery, e.g. Worker is broken and a fix is hours away): rename the .disabled file back, launchctl load it or git revert the GHA workflow rename. Both Python scripts still exist and work; only the orchestration moved.

Inspect / manage

# Active launchd jobs (PID, last-exit, label):
launchctl list | grep com.casey

# Reload after editing a plist:
launchctl unload ~/Library/LaunchAgents/com.casey.<job>.plist
launchctl load   ~/Library/LaunchAgents/com.casey.<job>.plist

# Force-restart an always-on job in place (preferred for warehouse-ui):
launchctl kickstart -k gui/$(id -u)/com.casey.warehouse-ui

# Cloudflare crons (registered on each wrangler deploy):
cd ~/garmin-warehouse/cloudflare/otq-checkin && grep -A4 '"crons"' wrangler.jsonc

# Worker live tail:
cd ~/garmin-warehouse/cloudflare/otq-checkin && npx wrangler tail

# Per-layer health snapshot (the same data the /status page renders):
curl -s http://127.0.0.1:8765/freshness | python3 -m json.tool
# Or visit https://warehouse.caseymanos.com/status

Wake-from-sleep

Mac must be awake at trigger time for launchd jobs. pmset repeat wakeorpoweron MTWRFSU 6:55:00 schedules wake before the 7am sync if the lid is closed. Worker is NOT subject to this — runs in CF's edge.

Quiet-hours guard

daily_sync.sh has a built-in 5am-11pm guard to defeat launchd's missed-wake recovery. If the Mac was asleep at 7am and wakes at 1am, launchd auto-fires the job — without the guard, that kicks off garmindb downloads + kb embedding off-schedule. Override with FORCE_RUN=1.

Notifications

daily_sync.sh and podcast-sync.sh both send notifications on completion (Telegram + Resend email). See systems memory.