Skip to content
Skill Authoring
Skill

/cron-scheduler

Use this skill when the user says 'cron job', 'scheduled task', 'run every', 'cron expression', 'recurring job', or needs production-grade scheduled jobs with overlap prevention, monitoring, and structured logging. Do NOT use for n8n workflows or event-driven webhooks.

BOOST
From plugin
memstack
42587 skills2 commands4 hooks
Install
$ npx -y skills add cwinvestments/memstack --skill cron-scheduler --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/cron-scheduler

Context preview

The summary Claude sees to decide when to auto-load this skill.

Use this skill when the user says 'cron job', 'scheduled task', 'run every', 'cron expression', 'recurring job', or needs production-grade scheduled jobs with overlap prevention, monitoring, and structured logging. Do NOT use for n8n workflows or event-driven webhooks.

SKILL.md

cron-scheduler.SKILL.md
name: memstack-automation-cron-scheduler
description: "Use this skill when the user says 'cron job', 'scheduled task', 'run every', 'cron expression', 'recurring job', or needs production-grade scheduled jobs with overlap prevention, monitoring, and structured logging. Do NOT use for n8n workflows or event-driven webhooks."
version: 1.0.0
license: "Proprietary, MemStack™ Pro by CW Affiliate Investments LLC. See LICENSE.txt"

Cron Scheduler: Building scheduled job...

*Builds production-grade scheduled jobs with cron syntax, timezone handling, overlap prevention, health checks, monitoring, alerting, and structured logging.*

Activation

When this skill activates, output:

`Cron Scheduler: Building scheduled job...`

Then execute the protocol below.

Context Guard

| Context | Status | |---------|--------| | User says "cron job", "scheduled task", "run every" | ACTIVE | | User says "cron expression" or "recurring job" | ACTIVE | | User needs a time-based scheduled task with monitoring | ACTIVE | | User wants a visual n8n workflow | DORMANT: use n8n Workflow Builder | | User wants an event-driven webhook | DORMANT: use Webhook Designer |

Common Mistakes

| Mistake | Why It's Wrong | |---------|---------------| | "No overlap prevention" | If a job takes 10 min and runs every 5 min, you get concurrent executions corrupting data. | | "Ignore timezones" | Cron defaults to server timezone. Midnight UTC ≠ midnight local. Always set TZ explicitly. | | "No monitoring" | Silent job failures are invisible. You won't know it broke until users complain. | | "Log to stdout only" | Stdout disappears on restart. Use structured logging with persistence. | | "Run as root" | Principle of least privilege. Run jobs under a dedicated service account. |

Protocol

Step 1: Gather Job Requirements

If the user hasn't provided details, ask:

> 1. **Task**: what does the job do? (sync data, send emails, clean up, generate reports) > 2. **Schedule**: how often? (every 5 min, hourly, daily at 2 AM, weekly) > 3. **Duration**: how long does a typical run take? > 4. **Timezone**: which timezone matters? (UTC, user's local, business hours) > 5. **Platform**: where does this run? (server crontab, cloud function, Docker, Kubernetes) > 6. **Failure mode**: what happens if it fails? (retry, alert, skip until next run)

Step 2: Write the Cron Expression

**Cron syntax (5-field standard):**

┌───────────── minute (0-59)
│ ┌───────────── hour (0-23)
│ │ ┌───────────── day of month (1-31)
│ │ │ ┌───────────── month (1-12)
│ │ │ │ ┌───────────── day of week (0-7, 0 and 7 = Sunday)
│ │ │ │ │
* * * * *

**Common schedule reference:**

| Schedule | Expression | Notes | |----------|-----------|-------| | Every minute | `* * * * *` | Testing only: too aggressive for production | | Every 5 minutes | `*/5 * * * *` | Good for near-real-time sync | | Every 15 minutes | `*/15 * * * *` | Light polling | | Every hour | `0 * * * *` | At minute 0 of every hour | | Every 6 hours | `0 */6 * * *` | 4x daily | | Daily at 2 AM UTC | `0 2 * * *` | Maintenance window | | Daily at 9 AM ET | `0 14 * * *` | 14:00 UTC = 9:00 AM ET (EST, adjust for DST) | | Weekdays at 8 AM | `0 8 * * 1-5` | Monday through Friday | | Weekly (Sunday midnight) | `0 0 * * 0` | Weekly cleanup | | Monthly (1st at midnight) | `0 0 1 * *` | Monthly reports | | Quarterly (Jan/Apr/Jul/Oct 1st) | `0 0 1 1,4,7,10 *` | Quarterly processing |

**Timezone handling:**

# System crontab, set TZ per job
CRON_TZ=America/New_York
0 9 * * * /path/to/job.sh    # Runs at 9 AM Eastern (handles DST)

# Or set in the crontab header
TZ=UTC
// Node.js (node-cron), explicit timezone
import cron from 'node-cron';

cron.schedule('0 9 * * *', () => {
  runDailyReport();
}, {
  timezone: 'America/New_York'
});

Step 3: Implement Overlap Prevention

**Lock-based approach (recommended):**

import { acquireLock, releaseLock } from './lockService';

async function runJob() {
  const lockKey = 'job:daily-report';
  const lockTTL = 3600; // seconds, must exceed max job duration

  const acquired = await acquireLock(lockKey, lockTTL);
  if (!acquired) {
    console.log('Job already running: skipping this execution');
    return;
  }

  try {
    await executeJobLogic();
  } finally {
    await releaseLock(lockKey);
  }
}

**Lock implementations by platform:**

| Platform | Lock Method | TTL Support | |----------|-----------|-------------| | Redis | `SET key value NX EX ttl` | Yes | | PostgreSQL | `pg_advisory_lock(id)` | Session-based | | File system | `flock` or PID file | Manual cleanup | | DynamoDB | Conditional put with TTL | Yes | | Kubernetes | CronJob `concurrencyPolicy: Forbid` | Built-in |

**PID file approach (simple servers):**

#!/bin/bash
PIDFILE="/tmp/myjob.pid"

if [ -f "$PIDFILE" ] && kill -0 $(cat "$PIDFILE") 2>/dev/null; then
    echo "Job already running (PID $(cat $PIDFILE))"
    exit 0
fi

echo $$ > "$PIDFILE"
trap "rm -f $PIDFILE" EXIT

# Job logic here

Step 4: Structured Logging

interface JobLog {
  job: string;
  runId: string;        // Unique per execution
  phase: 'start' | 'progress' | 'complete' | 'error';
  timestamp: string;
  durationMs?: number;
  itemsProcessed?: number;
  error?: string;
  metadata?: Record<string, any>;
}

function logJob(entry: JobLog): void {
  console.log(JSON.stringify(entry));
}

// Usage
const runId = crypto.randomUUID();
const start = Date.now();

logJob({ job: 'daily-report', runId, phase: 'start', timestamp: new Date().toISOString() });

try {
  const result = await executeJobLogic();
  logJob({
    job: 'daily-report', runId, phase: 'complete',
    timestamp: new Date().toISOString(),
    durationMs: Date.now() - start,
    itemsProcessed: result.count
  });
} catch (error) {
  logJob({
    job: 'daily-report', runId, phase: 'error',
    timestamp: new Date().toISOString(),
    durationMs: Date.now() - start,
    error: error.me
Read more
Ships withmemstack

The structured skill framework for Claude Code: 131 professional skills for deployment, security, databases, content, marketing, and more. Skills activate automatically when you need them. Say "deploy this to Railway" and the right skill loads on demand.

Get the whole plugin

Other skills on memstack.