cron-doctor – skill Agent & Tự động hóa cho agent AI
Chẩn đoán và kiểm tra biểu thức cron trước khi đưa lên production. Phát hiện năm cạm bẫy âm thầm: ngày không tồn tại khiến job không bao giờ chạy, ngữ nghĩa OR khiến chạy quá thường xuyên, spike lúc nửa đêm, trôi lệch do bước không đều và ngày 29/2 năm nhuận.
Cài skill cron-doctor bằng một lệnh
Chọn công cụ bạn đang dùng, copy lệnh và dán vào terminal. Skill sẽ nằm trong thư mục skills của công cụ đó và sẵn sàng dùng ở phiên làm việc tiếp theo.
Claude Code
mkdir -p ~/.claude/skills && curl -fsSL https://skill.dn1nguoi.com/z/cron-doctor-8b8b3.tgz | tar xz -C ~/.claude/skills
Gọi trong Claude Code: /cron-doctor – rồi mô tả công việc bạn muốn làm.
Codex
mkdir -p ~/.codex/skills && curl -fsSL https://skill.dn1nguoi.com/z/cron-doctor-8b8b3.tgz | tar xz -C ~/.codex/skills
Gọi trong Codex: $cron-doctor – rồi mô tả công việc bạn muốn làm.
Hermes Agent
mkdir -p ~/.hermes/skills && curl -fsSL https://skill.dn1nguoi.com/z/cron-doctor-8b8b3.tgz | tar xz -C ~/.hermes/skills
Gọi trong Hermes Agent: /cron-doctor – rồi mô tả công việc bạn muốn làm.
DeepSeek
mkdir -p ~/.deepcode/skills && curl -fsSL https://skill.dn1nguoi.com/z/cron-doctor-8b8b3.tgz | tar xz -C ~/.deepcode/skills
Gọi trong DeepSeek: /cron-doctor – rồi mô tả công việc bạn muốn làm.
Nội dung SKILL.md
Nội dung SKILL.md gốc bằng tiếng Anh.
cron-doctor
Overview
Cron is deceptively error-prone. The failure mode is silent — a syntactically
valid expression that simply never fires, or fires far more often than intended.
0 0 30 2 * parses cleanly and then sits dead forever (February has no 30th).
0 0 1,15 * 1 looks like "1st and 15th if Monday" but actually means "1st, 15th,
OR every Monday" — ~6 fires/month instead of ~2.
This skill teaches an agent to catch those before they reach production. It comes
with a zero-dependency validation engine (scripts/cron-engine.js, no install
needed) that parses, describes, deep-validates, and computes next fire times.
When to Use This Skill
- Use when a user writes, edits, reviews, or deploys a cron expression — in a
crontab, a Kubernetes CronJob, a GitHub Actions schedule, an Airflow DAG,
a Celery beat schedule, a systemd timer, or any scheduled task.
- Use when debugging a job that "didn't fire" or "fired at the wrong time."
- Use when a user asks "what does this cron expression mean?" or "when will this
run next?" or "how often does this run per year?"
- Use when reviewing a CI/CD pipeline or infrastructure config that contains a
schedule field.
- Use when a user pastes a 5-field cron expression and asks for a sanity check.
Scheduler and calculation limits
Identify the scheduler/dialect and timezone first. systemd OnCalendar is not a five-field cron expression; Quartz and library day semantics differ. This engine uses the Node process local timezone, not CRON_TZ/spec.timeZone, and scans at most 600,000 minutes (~416 days): fewer than requested results are partial, not proof a schedule never fires. It does not model platform delays, DST policy, missed-job recovery or overlap control. Confirm next runs in the actual scheduler. No scripts were runtime-tested during this content review.
How It Works
Step 1: Parse the expression
Split on whitespace into 5 fields: minute, hour, day-of-month, month, day-of-week.
Confirm valid ranges:
| Field | Position | Range | Notes |
|-------|----------|-------|-------|
| minute | 1 | 0–59 | |
| hour | 2 | 0–23 | |
| day-of-month | 3 | 1–31 | |
| month | 4 | 1–12 | names (JAN–DEC) accepted |
| day-of-week | 5 | 0–7 | 0 and 7 both = Sunday; names (SUN–SAT) accepted |
Step 2: Describe it in plain English
State what the user *thinks* it does vs. what it *actually* does. Be explicit
about OR-vs-AND semantics for day-of-month + day-of-week (see death-trap #2).
Step 3: Run the trap checklist
Check the five death-traps below and flag any that apply.
Step 4: Calculate next runs and annual fire count
Compute the next 5 fire times as concrete dates so the user can verify the
schedule behaves as expected. Estimate annual fire count — a schedule that fires
365×/year vs. 12×/year is a ~30× cost and load difference.
The Five Cron Death-Traps
These are the bugs that pass crontab -l validation but break in production.
1. Impossible dates — the "never fires" bug
0 0 30 2 *Valid syntax. Never fires. February has no 30th. This schedule is a dead job
that silently sits forever. The same applies to day 31 in any 30-day month:
0 0 31 4 *, 0 0 31 6 *, 0 0 31 9 *, 0 0 31 11 *.
Fix: use 0 0 28-31 * * and check for end-of-month in the script, or use L
(last day) syntax if your scheduler supports it.
2. OR-semantics — the "fires too often" bug
0 0 1,15 * 1Does NOT mean "midnight on the 1st and 15th if it's Monday."
Does mean "midnight on the 1st, the 15th, OR every Monday." That's ~6
fires/month instead of ~2.
This is the single most misunderstood cron rule. When both day-of-month AND
day-of-week are restricted (neither is *), cron uses OR logic, not AND.
Fix: if you need "1st and 15th only if Monday," run daily and check in the
script:
0 0 * * 1 [ "$(date +\%d)" = "01" -o "$(date +\%d)" = "15" ] && your-command3. Midnight spike — the "everything at once" bug
0 0 * * *Every job scheduled at 0 0 competes for resources at exactly 00:00. Database
backups, log rotations, cert renewals, report generation — all fire simultaneously.
This causes load spikes, connection-pool exhaustion, and cascading timeouts.
Fix: stagger jobs across the hour. Use 17 2 * * * or 43 3 * * * instead of
0 0. Jitter is your friend.
4. Uneven steps — the "drift" bug
*/7 * * * *Does NOT mean "every 7 minutes evenly." It means "every 7 minutes starting at
0, then resets at 60." So: 0, 7, 14, 21, 28, 35, 42, 49, 56 — then 0 again
(a 4-minute gap). The intervals drift: 7,7,7,7,7,7,7,7,4.
Fix: 60 is not divisible by 7. Use step values that divide 60 evenly: */5,
*/10, */15, */20, */30. If you truly need every-7-minutes, use a supervised monotonic/interval scheduler; a sleep loop adds task duration and needs restart/overlap handling.
5. Leap-year February 29 — the "annual surprise"
0 0 29 2 *Fires only on leap years — February 29, 2024 / 2028 / 2032… If someone writes this
expecting "end of February," they'll be confused for 3 out of every 4 years.
Fix: for last day of February, run on 28 and 29 and use an explicit next-day-is-March guard; running only on the 28th cannot select the 29th.
Using the validation script
This skill ships a zero-dependency engine at scripts/cron-engine.js (Node.js, no
npm install needed). You can use it programmatically or from the CLI:
// Programmatic — Node.js, zero dependencies
const { describe, validate, nextRuns, formatNextRuns } = require('./scripts/cron-engine.js');
// Parse + describe -> returns { text, error, parsed }
const d = describe('0 0 30 2 *');
console.log(d.text); // "At 00:00, on day-of-month 30 in in FEB"
// Deep validation -> catches the traps
const result = validate('0 0 30 2 *');
console.log(result.valid); // true (syntax is valid)
console.log(result.observations); // includes the "never fires" insight
console.log(result.suggestions); // e.g. "Midnight is a common spike..."
// Next 5 fire times -> returns Date[]
const runs = nextRuns('0 9 * * 1-5', new Date(), 5);
console.log(formatNextRuns(runs, new Date())); // [{ date, relative, formatted }, ...]# CLI (via the bundled wrapper)
node scripts/cli.js describe "*/5 * * * *"
node scripts/cli.js validate "0 0 30 2 *"
node scripts/cli.js next "0 9 * * 1-5" 5Common cron presets
| Expression | Description | Use case |
|-----------|-------------|----------|
| */5 * * * * | Every 5 minutes | Health checks, polling |
| 0 * * * * | Every hour | Hourly aggregation |
| 0 */2 * * * | Every 2 hours | Semi-frequent sync |
| 0 9 * * 1-5 | 9am Mon–Fri | Business-hours task |
| 0 2 * * * | 2am daily | Off-peak batch (avoid midnight) |
| 0 0 * * 0 | Midnight Sunday | Weekly maintenance |
| 0 0 1 * * | Midnight 1st of month | Monthly report |
| 0 0 1 1 * | Midnight Jan 1st | Annual task |
Best Practices
- ✅ Always provide the plain-English description AND run the trap checklist.
- ✅ Stagger midnight jobs to avoid the spike.
- ✅ Prefer step values that divide 60 evenly (
*/5,*/15,*/30). - ✅ Add a comment above every crontab line explaining intent.
- ✅ Set an explicit timezone (
CRON_TZ) on schedulers that support it. - ❌ Don't trust
crontab -lvalidation — it lists the installed crontab; it is not a syntax or semantic validator. - ❌ Don't restrict both day-of-month and day-of-week without confirming OR-logic.
- ❌ Don't schedule everything at
0 0.
Common Pitfalls
- Problem: "My cron job isn't running."
Solution: Check for an impossible date (trap #1) and confirm the daemon is
running (service cron status / systemctl status crond). Verify the file
ends with a newline and has correct ownership.
- Problem: "My job runs far more often than expected."
Solution: You hit OR-semantics (trap #2). If both day-of-month and
day-of-week are set, cron ORs them. Move one to * or guard in-script.
- Problem: "Intervals are uneven — sometimes 7 min, sometimes 4."
Solution: Step value doesn't divide 60 evenly (trap #4). Use a divisor of 60.
- Problem: "My job works locally but not in the cluster."
Solution: Timezone mismatch. Kubernetes uses the controller local timezone unless spec.timeZone is set; use spec.timeZone: Etc/UTC deliberately. Do not embed TZ/CRON_TZ in its schedule string. Check the exact GitHub Actions schedule support and execution delays.
Limitations
- This skill targets standard 5-field cron as implemented by Vixie-style cron and Kubernetes
CronJob, GitHub Actionsschedule, and most libraries. It
does not validate Quartz 6/7-field expressions with seconds/years, nor
non-standard @reboot / L / # extensions without a note.
- Estimated annual fire counts assume a non-leap reference year; February 29
schedules (trap #5) are flagged expli
… (nội dung đầy đủ có trong gói tải về)
Skill liên quan
- mcp-oauth-triage – Chẩn đoán vì sao một MCP connector trước đây xác thực được nay lại lỗi: phân loại lỗi…
- crypto-bd-agent – Mẫu tham chiếu (từ báo cáo upstream, chưa được xác minh độc lập tại đây) để xây dựng AI…
- session-guard – Dùng khi làm tác vụ nhiều bước phức tạp, khi phiên làm việc kéo dài (40+ lần gọi tool)…
- mcp-server-builder – Thiết kế và triển khai MCP (Model Context Protocol) server sẵn sàng production từ hợp…
- cursor-delegate – Giao tác vụ lập trình cho Cursor Agent CLI (`cursor-agent`) chỉ khi người dùng yêu cầu…
- scrapling – Thu thập dữ liệu website với trình duyệt tàng hình và vượt Cloudflare.
- memory-engineering – Dùng khi thiết kế, rà soát hoặc chi trả cho hệ thống bộ nhớ của agent — thêm bộ nhớ cho…
- memory-review – Phân tích auto-memory để tìm ứng viên thăng cấp, mục cũ, cơ hội hợp nhất và chỉ số sức…