guide · ai

Hermes Cron Job Authoring: The Shape and the Rules

A practical Hermes cron playbook: jobs.json fields, supported schedule shapes, a daily-traffic example, and four checks that catch bad jobs before they run.

July 15, 2026 · By Alastair Fraser

A retro-futurist operator at a terminal assembling a cron job from labeled JSON field cards, with a clock and four verification checkmarks behind them.

A Hermes cron job is a small contract between the scheduler and a command that should run later. The scheduler does not infer missing intent. If the job is vague, malformed, or pointed at the wrong provider, it can be perfectly valid JSON and still be operationally useless. This is the shape-and-rules playbook I use before putting a job into jobs.json.

Start with the eight required fields

Every job needs these eight keys:

{
  "id": "daily-traffic",
  "name": "Daily traffic report",
  "prompt": "Fetch yesterday's traffic and report anomalies.",
  "schedule": "0 7 * * *",
  "provider": "openai",
  "model": "gpt-4o-mini",
  "delivery": "telegram",
  "enabled": true
}
  • id is the stable machine identifier. Keep it short, unique, and unchanged after deployment.
  • name is the human-readable label shown in lists and logs.
  • prompt is the complete task instruction. Include what to inspect, the expected output, and important constraints; do not rely on conversational context.
  • schedule is the cron expression.
  • provider selects the configured model backend.
  • model selects the model within that backend. Use a model name that is actually configured on this VPS.
  • delivery describes where the result goes. Match the delivery integration and its expected value rather than inventing a channel name.
  • enabled is the explicit on/off switch. Set it to false while staging or debugging.

The values are not interchangeable: an identifier belongs in id, not in name; a model belongs in model, not in prompt; and a disabled job should not be “disabled” only by deleting its schedule.

The six optional fields

Add optional keys only when they carry useful state or configuration:

  • paused_at records when an intentionally paused job was paused.
  • paused_reason records why it is paused, so the next operator has context.
  • scripts lists scripts the job may invoke or depend on.
  • tags adds searchable classification. Keep numeric-looking tags quoted, for example ["daily", "7"], so YAML/JSON tooling does not silently coerce them.
  • skills names Hermes skills that should be available to the run.
  • timeout sets the execution limit appropriate to the work; do not use a huge timeout to hide a stuck script.

Paused metadata is especially useful during maintenance: preserve the job and its stable id, set enabled to false, and explain the pause instead of making a second replacement job.

The three schedule classes

Keep schedules in one of three easily-audited shapes:

  1. Every N minutes: */15 * * * * runs every 15 minutes. Use this for polling or heartbeat work, and consider API rate limits.
  2. Daily time: 0 7 * * * runs daily at 07:00 in the scheduler’s configured timezone.
  3. Weekday time: 30 9 * * 1-5 runs at 09:30 Monday through Friday.

Confirm the VPS timezone before promising a local time. Cron’s five positions are minute, hour, day-of-month, month, and day-of-week. Avoid adding clever special cases when one of these three forms expresses the requirement clearly.

Worked example: authoring daily-traffic on this VPS

Suppose the requirement is “send one traffic summary every morning.” First choose a stable id, a precise prompt, and the configured provider/model. Then add the object to the active jobs.json array (preserving the file’s surrounding structure):

{
  "id": "daily-traffic",
  "name": "Daily traffic report",
  "prompt": "Review yesterday's site traffic, compare it with the previous 7-day baseline, call out material changes, and send a concise report with dates, numbers, and likely causes. Do not invent missing metrics.",
  "schedule": "0 7 * * *",
  "provider": "openai",
  "model": "gpt-4o-mini",
  "delivery": "telegram",
  "enabled": true,
  "tags": ["traffic", "daily"],
  "timeout": 300
}

On this VPS, the important authoring decisions are deliberate: the prompt defines the comparison window and forbids invented data; 0 7 * * * is daily rather than weekday-only; timeout gives a bounded run; and tags remain strings. If the reporting script is required, add it under scripts using the repository’s established path, rather than embedding shell commands in an ambiguous prompt.

Four verification steps

  1. Validate the shape. Parse jobs.json and confirm valid JSON, unique id values, all eight required fields, correct booleans, and no misspelled keys. A spell-checked-but-runtime-broken field name (porompt instead of prompt) is the most common silent failure.
  2. Validate the schedule. Confirm the expression belongs to the intended class, calculate its next run in the VPS timezone, and check that it is not accidentally every minute or weekend-only.
  3. Validate dependencies. Confirm the provider/model is configured, referenced scripts exist and are executable, required skills are available, and delivery is configured for the target chat/channel.
  4. Run safely, then inspect evidence. Keep enabled: false for a dry run or use the scheduler’s test/run facility; after enabling, inspect the first execution log and delivery. Verify the timestamp, prompt outcome, error state, and received message—not merely that the job appears in a list.

A cron authoring pass is complete only when the object is structurally valid, scheduled when intended, connected to real dependencies, and observed running successfully. That discipline turns jobs.json from a list of hopeful instructions into an auditable operating surface.

The common authoring traps

Three patterns show up in cron jobs that look right but misbehave:

  1. Field-name typos caught by the scheduler. prompt vs porompt vs promp is a 1-character mistake the JSON parser accepts but the scheduler silently drops the prompt. Always grep jobs.json for the eight canonical field names after edits.
  2. Schedule expressions that fire too often. * * * * * (every minute) is the default if you forget to write a schedule. Test the cron expression with python3 -c "from croniter import croniter; print(croniter('* * * * *').get_next())" before saving.
  3. Provider/model mismatch. A job with provider: anthropic but the operator’s config has no anthropic API key runs, fails authentication, and emits a confused error. Check hermes config show --provider <name> before pointing a new job at it.

Each of these has been seen in the wild this month on this VPS. None of them crash the build; all of them degrade operations silently. The verification checklist above catches all three.

Sources

Sources

#hermes#cron#jobs-json#authoring#playbook

Submit a take

Have a different read on this? Drop a comment below — your email isn't published, and I read every one. Nothing leaves the site until I approve it.

Your email address will not be published. Required fields are marked.