Scheduled tasks, cron jobs, background job queues, recurring work. Use when user mentions cron, scheduler, background jobs, recurring tasks, Bull, BullMQ, Celery, Sidekiq, node-cron, APScheduler.
From godmodenpx claudepluginhub arbazkhan971/godmodeThis skill uses the workspace's default tool permissions.
Designs and optimizes AI agent action spaces, tool definitions, observation formats, error recovery, and context for higher task completion rates.
Enables AI agents to execute x402 payments with per-task budgets, spending controls, and non-custodial wallets via MCP tools. Use when agents pay for APIs, services, or other agents.
Compares coding agents like Claude Code and Aider on custom YAML-defined codebase tasks using git worktrees, measuring pass rate, cost, time, and consistency.
/godmode:cronsetInterval or setTimeout loops that need conversion to proper scheduled jobsEvaluate what needs to run, how often, and under what constraints:
SCHEDULING REQUIREMENTS ASSESSMENT:
| Dimension | Value |
| Task type | <cleanup | report | sync | alert |
| | billing | digest | health check> |
| Frequency | <seconds | minutes | hourly | |
CRON EXPRESSION SYNTAX:
| Field | Values | Special chars |
| Minute | 0-59 | * , - / |
| Hour | 0-23 | * , - / |
| Day of month | 1-31 | * , - / ? L W |
Common patterns:
and 7 = Sunday) — check your library
SCHEDULER TECHNOLOGY SELECTION:
| Technology | Language | Backend | Distributed| Persistence| Ops cost |
|--|--|--|--|--|--|
| node-cron | Node.js | In-proc | No | None | Minimal |
| | Simple cron | (memory) | (single) | (restart | |
| | schedules | | | loses) | |
| | | | | | |
| BullMQ | Node.js | Redis | Yes | Redis | Low |
...
Design the scheduling topology, job registry, and execution flow:
SCHEDULE ARCHITECTURE:
| Scheduler Job Registry Executors |
| --------- ------------ --------- |
| +-- daily-digest -------------- Worker Pool A (2) |
| Cron Engine ------+-- cleanup-expired ----------- Worker Pool B (3) |
import { Queue, Worker, QueueEvents } from 'bullmq';
import Redis from 'ioredis';
const connection = new Redis({
host: process.env.REDIS_HOST || 'localhost',
port: parseInt(process.env.REDIS_PORT || '6379'),
from celery import Celery
from celery.schedules import crontab, solar
from datetime import timedelta
app = Celery('scheduler', broker='redis://localhost:6379/0', backend='redis://localhost:6379/1')
from apscheduler.schedulers.background import BackgroundScheduler
from apscheduler.jobstores.redis import RedisJobStore
from apscheduler.executors.pool import ThreadPoolExecutor, ProcessPoolExecutor
from apscheduler.triggers.cron import CronTrigger
import pytz
Sidekiq-Cron (Ruby): Sidekiq::Cron::Job.load_from_hash with cron expression, class, and queue.
Hangfire (.NET): RecurringJob.AddOrUpdate with cron expression, SQL Server or Redis storage.
Quartz (Java): @Configuration with JobDetail bean and CronTrigger schedule.
Every scheduled job MUST safely run more than once for the same logical execution window:
IDEMPOTENCY STRATEGIES FOR SCHEDULED JOBS:
| Strategy | Use case |
| Date-based key | "digest:2025-03-15" — one per day |
| Window-based key | "sync:2025-03-15T10:00Z" — one |
| | per 15-min window |
Principle: derive the idempotency key from the SCHEDULE, not the job ID.
- The system re-creates the job on restart with a new ID
...
// Idempotent wrapper for scheduled jobs
class ScheduledJobRunner {
constructor(private redis: Redis) {}
async runOnce(
jobName: string,
Prevent multiple scheduler instances from firing the same job simultaneously:
DISTRIBUTED LOCKING STRATEGIES:
| Method | Backend | Pros | Cons |
| Redis SETNX + EX | Redis | Fast, atomic acquire | Not CP-safe |
| Redlock | Redis | Multi-node consensus | Controversial |
| DB advisory lock | Postgres| No extra infra | DB-coupled |
| ZooKeeper/etcd | ZK/etcd | CP-safe, reliable | Ops overhead |
class DistributedSchedulerLock {
constructor(private redis: Redis) {}
async acquireLeader(schedulerId: string, ttl: number = 30): Promise<boolean> {
// Only one scheduler instance becomes leader
const acquired = await this.redis.set(
import hashlib
def pg_advisory_lock_for_job(conn, job_name: str) -> bool:
"""Acquire a PostgreSQL advisory lock for a scheduled job.
Returns True if lock acquired, False if another process holds it."""
# Convert job name to a stable int64 for PG advisory lock
Track schedule health and detect missed or failing runs:
CRON JOB MONITORING:
| Metric | Current | Alert | Status |
| Jobs scheduled | 8 | — | OK |
| Last run: daily-digest | 09:00 | miss > 1h| OK |
| Last run: cleanup | 06:00 | miss > 7h| OK |
// Scheduled job health monitor
class CronJobMonitor {
constructor(private redis: Redis, private alerter: Alerter) {}
async recordRun(jobName: string, result: {
status: 'success' | 'failure';
Handle scheduled jobs that exhaust retries:
SCHEDULED JOB DLQ DESIGN:
| DLQ for Scheduled Jobs |
| Queue: scheduled-jobs-dlq |
| Retention: 30 days |
| Alert: Any entry (scheduled jobs should not fail) |
Design priority-aware scheduling and dependent job pipelines:
SCHEDULED JOB PRIORITIES:
| Priority | Schedule | Job | SLA |
| P0 | */5 * * * * | Health check | < 30s |
| P1 | */15 * * * * | Inventory sync | < 2m |
| P1 | 0 9 * * * | Daily digest | < 5m |
JOB CHAINING (dependent execution):
| Pipeline: "end-of-day" |
// BullMQ Flow (parent-child dependencies)
import { FlowProducer } from 'bullmq';
const flowProducer = new FlowProducer({ connection });
// End-of-day pipeline — child jobs run first, parent last
Prevent scheduled jobs from overwhelming downstream services:
RATE-LIMITED SCHEDULING:
| Scenario | Strategy |
| API with 100 req/min | Token bucket limiter on worker |
| Email provider 500/hr | BullMQ limiter: 500 per 3600s |
| DB batch writes | Chunk + delay between batches |
// Scheduled job that processes in rate-limited batches
async function runDailyDigest() {
const users = await db.users.findMany({
where: { digestEnabled: true, lastDigestBefore: today() },
});
Handle timezone-aware scheduling correctly:
TIMEZONE HANDLING:
| Rule |
| 1. Store all schedules in UTC internally |
| 2. Convert to user timezone for display only |
| 3. Use IANA timezone names (America/New_York), never |
Choose the right persistence layer:
SCHEDULER PERSISTENCE COMPARISON:
| Aspect | Redis-backed | DB-backed |
| Speed | Sub-ms operations | 1-10ms queries |
| Durability | RDB/AOF (config) | Full ACID |
| Crash recovery | May lose last | No data loss |
Use Redis-backed (BullMQ, Sidekiq-Cron) when:
- Need complex queries on job history
1. Save schedule configuration as `config/schedules/registry.ts`
2. Save scheduler setup as `config/schedules/scheduler.ts`
3. Save idempotency helpers as `lib/scheduled-job-runner.ts`
4. Save monitoring as `lib/cron-monitor.ts`
5. Commit: "cron: <scheduler> — <N> jobs, <frequency range>, <lock strategy>"
6. If new scheduler: "Scheduled jobs configured. <N> jobs registered. Deploy and monitor."
7. If fixing failures: "Idempotency and locking added. Missed-schedule alerting enabled."
8. If migrating: "Migrated from <old> to <new>. All schedules verified."
Never ask to continue. Loop autonomously until done.
# Validate cron expressions and test jobs
npx cron-validate "0 9 * * 1-5"
npm run test:cron -- --timeout 30000
redis-cli KEYS "bull:*:repeat:*" | head -10
IF job duration > 80% of interval: increase interval or optimize job. WHEN missed runs > 0 in 24h: alert and investigate. IF retry count > 3: move to dead letter queue.
| Flag | Description |
|---|---|
| (none) | Full scheduled task design workflow |
--tech <name> | Target specific scheduler (bullmq, celery, sidekiq, apscheduler, hangfire, quartz, node-cron) |
--diagnose | Diagnose missed or failing scheduled jobs |
KEEP if: improvement verified. DISCARD if: regression or no change. Revert discards immediately.
Stop when: target reached, budget exhausted, or >5 consecutive discards.