Skip to content

2026-07-17-cron-trigger-plan.md - #12

Merged
edgehero merged 5 commits into
mainfrom
plan-exec/2026-07-17-cron-trigger
Jul 17, 2026
Merged

2026-07-17-cron-trigger-plan.md#12
edgehero merged 5 commits into
mainfrom
plan-exec/2026-07-17-cron-trigger

Conversation

@edgehero

Copy link
Copy Markdown
Owner

Adds a cron trigger: scheduled kind:"local" jobs via BullMQ Job Schedulers, configured by a JSON file (schedules.json, path in PI_SCHEDULES_FILE). A schedule is a trigger, not a job kind — each tick mints an ordinary local job that drains through the existing runJob → reserveBudget → runContainer path (no second processor, budget gate intact). Purely additive; no new dependencies (BullMQ 5.80.4 owns scheduling); the deprecated repeat: API is not introduced.

The money-critical piece is a per-scheduler stall guard: BullMQ structurally exempts scheduler (repeat:) jobs from maxStalledCount, so a wedged scheduled job would re-run paid indefinitely. A Redis-backed HINCRBY+EXPIRE counter tears the schedule down (removeJobScheduler) past PI_SCHEDULER_STALL_MAX (default 2). Startup reconcile installs schedules idempotently and prunes orphans (a schedule removed from config); -10/-11 from upsertJobScheduler are surfaced loudly, never swallowed. kind:"github" schedules are rejected at load (a schedule has no webhook delivery/issue/title/body).

Scope: worker/ only, plus docs/config, a CI comment, and spec additions. image/ unchanged; receiver//cli.mjs untouched.

Verification

Check Baseline Final
typecheck/lint/test npm test green, 251 pass / 0 fail / 6 skip 301 pass / 0 fail / 12 env-gated skips (+50 from new cron suites), clean exit — 0 regressions
E2E — Queue Verification N/A PASS (cron.integration.test.mjs vs real Valkey, 6/6, run twice, zero residue): byte-identical local drain, budget-INCR-before-container, no-backfill, orphan reconcile + live descriptor id-field=.key, stall→teardown, no-overlap; -10/-11 = SDK throws (never swallowed)
E2E — Container Smoke N/A N/A (image unchanged)
E2E — Webhook N/A N/A (receiver untouched)
Legacy sweep PASS (additive; no add-alongside, no deprecated repeat:, no dead code)

New spec surface: REQ-CRON-SCHEDULED-JOBS, INT-SCHEDULES-FILE-CONTRACT, and reject-github/dual-enqueue bullets under DES-CRON-VIA-BULLMQ-SCHEDULER. detect_drift.py stays exit 0.

Two known pre-existing issues are documented but out of scope: the releaseBudget cap-starvation leak (cross-cutting, all job kinds) and a queue.test.mjs dedup test-hygiene flake.

Closes #3

🤖 Generated with Claude Code

edgehero and others added 5 commits July 17, 2026 16:00
…schedule loading" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
…uler stall guard" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
… — upsert + orphan prune" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
… startWorker" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
…ion surface, contract test, docs & spec hygiene" passed gates

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NGgfho2J6YfRSQN1bSDhj6
@edgehero
edgehero merged commit a8060b5 into main Jul 17, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Cron trigger: scheduled jobs via BullMQ Job Schedulers

1 participant