Runbook: Worker deploy¶
How to deploy / verify / fix the OTQCheckinAgent Worker.
Standard deploy¶
cd ~/garmin-warehouse/cloudflare/otq-checkin
npx tsc --noEmit # type-check first
npx wrangler deploy
Wrangler output should show:
- Uploaded otq-checkin (Xs)
- Deployed otq-checkin triggers (Ys)
- Both crons listed: 0 3 * * * and 30 15 * * *
- A new Current Version ID
Smoke test after deploy¶
# Worker reachable?
curl https://otq-checkin.manoscasey.workers.dev/health
# → "ok"
# DO state intact?
curl https://otq-checkin.manoscasey.workers.dev/stats
# → {"checkins":N,"processed_replies":M,"daily_summaries":P}
# Trigger a morning summary manually:
SECRET=$(echo -n "$TELEGRAM_BOT_TOKEN" | tail -c 12)
curl -X POST "https://otq-checkin.manoscasey.workers.dev/run-morning-summary?secret=$SECRET"
# → {"sent":true,"reason":"message_id=N"} (or {"sent":false,...} if already sent today)
# Live tail:
npx wrangler tail
When deploy fails¶
wrangler deploy 400 / authentication error¶
OAuth token expired (happens ~6mo).
TypeScript errors¶
# What's wrong:
npx tsc --noEmit
# Common: missing types after a package upgrade. Reinstall deps:
npm install
# Common: a new DO method's signature is wrong. Read the error,
# usually a Promise<T> vs T mismatch.
Migration tag bump needed¶
Adding a NEW DO class (not a new table) requires a migration tag bump
in wrangler.jsonc:
"migrations": [
{ "tag": "v1", "new_sqlite_classes": ["OTQCheckinAgent"] },
{ "tag": "v2", "new_sqlite_classes": ["NewClass"] } // example
]
Adding a new TABLE inside the existing DO class does NOT need a tag
bump — the constructor's CREATE TABLE IF NOT EXISTS handles it.
R2 binding missing or wrong bucket name¶
Symptom: Worker error logs show "WAREHOUSE_DATA is not defined" or similar.
Check wrangler.jsonc:
Bucket name must match exactly (Cloudflare is case-sensitive).
Rollback¶
Cloudflare Workers keeps the last few versions. To roll back:
# List recent versions:
npx wrangler versions list
# Roll back to a specific Version ID:
npx wrangler versions deploy <VERSION_ID>
OR redeploy from a known-good git SHA:
cd ~/garmin-warehouse
git stash # if you have uncommitted changes
git checkout <good-sha>
cd cloudflare/otq-checkin
npx wrangler deploy
git checkout main
git stash pop
Re-register the Telegram webhook¶
If the webhook URL changes or webhook gets deregistered:
TG_TOKEN=$(grep TELEGRAM_BOT_TOKEN ~/.zshrc | head -1 | cut -d= -f2- | tr -d '"')
curl -X POST "https://api.telegram.org/bot${TG_TOKEN}/setWebhook" \
-d "url=https://otq-checkin.manoscasey.workers.dev/webhook"
# Verify:
curl "https://api.telegram.org/bot${TG_TOKEN}/getWebhookInfo"
# Look for url + 0 pending + no last_error
Set / rotate Worker secrets¶
cd ~/garmin-warehouse/cloudflare/otq-checkin
npx wrangler secret put ANTHROPIC_API_KEY # prompts for value
npx wrangler secret put INTERVALS_ICU_KEY
npx wrangler secret put TELEGRAM_BOT_TOKEN
# List secret names (values not shown):
npx wrangler secret list
Inspect DO state¶
The DO has SqlStorage but no direct external query interface — you'd
have to add an endpoint. The /stats endpoint exposes counts. For
deeper inspection during debugging, add a debug endpoint to
index.ts that exposes recent rows:
Don't ship debug endpoints permanently.
When the cron didn't fire¶
# Check Worker observability for cron invocations:
# Cloudflare dashboard → Workers → otq-checkin → Logs → filter "cron"
# Or live tail at fire time:
npx wrangler tail
# Wait for the next scheduled fire (8pm or 8:30am PDT).
If cron is registered but didn't fire: check the wrangler.jsonc crons
field is correct (UTC). Confirm via post-deploy output.
If cron fires but doesn't do anything: index.ts scheduled() routes
by event.cron string match. If the cron string in wrangler.jsonc
differs from what scheduled() matches against, you'll see the
"unknown cron pattern" warning in logs.