Engineering

Claude Code Background Jobs: /loop vs cron vs OpenHelm

Three ways to run Claude Code without babysitting it — what each approach handles well, and where each falls short.

O
OpenHelm Team· Engineering
··8 min read
Claude Code Background Jobs: /loop vs cron vs OpenHelm

Running Claude Code interactively is straightforward. You type a goal, watch it work, step in when it gets stuck. The feedback loop is tight.

Running Claude Code as a background job is a different problem. When you're not watching, several things can silently go wrong: the process can stall waiting for input, context accumulates until later responses degrade in quality, logs go nowhere useful, and failures stay invisible until you happen to check. A job that ran for six hours overnight and produced nothing useful is worse than a job that failed loudly at 2:03am.

The difference between a reliable claude code background job setup and an unreliable one isn't in the prompt you write — it's in the infrastructure around execution. Here's a direct comparison of the three real options.

What We Mean by "Background Job"

A Claude Code background job runs without an open terminal, without interactive oversight, and ideally without any human presence at all. The process starts, works through the goal, and completes (or fails cleanly) on its own.

This is categorically different from a /loop session you're actively monitoring. True background execution requires the process to be genuinely autonomous: self-starting, self-correcting when possible, and self-reporting when done.

Option 1: Claude Code /loop

/loop is Claude Code's built-in iteration primitive. In an active terminal session:

/loop 10m Check test failures and suggest fixes

This re-runs your prompt every 10 minutes. For interactive work where you're watching the output, it's useful and requires no setup. For true background execution, it has fundamental constraints:

Session-bound. The loop stops when the terminal window closes. Put your laptop to sleep and it's gone.

No persistent logging. Output lives in terminal scroll history. There's no structured record of what ran, when, and what it produced.

No scheduling. You trigger it manually each time. It can't fire at 2am while you sleep.

No failure feedback. When a run fails, the next run starts from scratch with no context about what went wrong. The system can't learn from its previous attempts.

/loop is the right tool for a quick exploratory session. It's not a background automation system.

Option 2: Shell Script + cron or launchd

The DIY approach: write a wrapper script that calls claude -p "your goal", log the output to a file, and schedule it with crontab -e or a launchd plist.

#!/bin/bash
TIMESTAMP=$(date +%Y%m%d_%H%M%S)
LOG="$HOME/claude-logs/nightly-$TIMESTAMP.log"
mkdir -p "$HOME/claude-logs"

claude -p "Check all failing tests in the auth module and fix any that have clear causes. Run the full test suite at the end." \
  --project /Users/you/my-api \
  >> "$LOG" 2>&1

Wired to cron as 0 2 * * * /usr/local/bin/run-nightly-job.sh, this gets a Claude Code session running unattended every night. The approach has genuine strengths: it's transparent, requires no extra software, and every line of output is preserved in a file you can inspect.

The gaps that matter for production-grade background execution:

No silence detection. This is the most expensive failure mode. Claude Code can pause waiting for interactive input — a terminal prompt, a dangling API call, an unexpected confirmation — and your shell script has no idea. The process keeps running indefinitely, consuming tokens, until you manually find and kill it. Six undetected hours of silence is a very real and very expensive outcome.

No run history. Logs are flat files sorted by timestamp in a directory. Answering "did the job succeed last Tuesday?" means grepping through them. There's no dashboard, no status summary, no quick answer.

No concurrency management. If three cron jobs fire simultaneously, all three compete for system resources and API rate limits with no coordination. You either add locking logic to every script or accept the risk.

No failure context. Each run is stateless. If Monday's job failed partway through, Tuesday's job starts from scratch with no knowledge of Monday's failure. Claude Code can't learn from what went wrong yesterday.

macOS permission friction. Cron on modern macOS requires Full Disk Access to reach development directories, and launchd plists add their own maintenance overhead. Neither is deal-breaking, but both add friction.

For a single, simple overnight job on a project you actively monitor: this approach works. For multiple projects, or jobs where reliability matters, the gaps compound.

Option 3: OpenHelm

OpenHelm is a macOS desktop app built specifically to run Claude Code jobs unattended. Every feature in it exists because someone had a background job fail silently in a way that raw cron wouldn't catch.

Here's what it adds over the shell-script approach:

Silence Detection

OpenHelm monitors the Claude Code output stream in real time. If no output is produced for 10 minutes, the run is flagged and the process is stopped. This is the single most important reliability feature for background execution — it directly prevents the six-hour-silent-job-with-nothing-to-show-for-it scenario.

Structured Run History

Every run produces a timestamped record with status (queued / running / succeeded / failed / permanent_failure), full output transcript, and duration. Checking last night's run is a click in the dashboard, not a file search.

Pre-Flight Checks

Before launching Claude Code, OpenHelm verifies:

  • The project directory exists and is accessible
  • The Claude Code binary is on PATH
  • The job hasn't been disabled since it was last scheduled

If any check fails, the run is marked permanent_failure immediately. Claude Code never launches into a broken environment.

Self-Correction Loop

When a run fails, OpenHelm can automatically queue a corrective retry — re-running Claude Code with the failure output appended as context. Claude Code's second pass often succeeds precisely because it's working with information about what went wrong the first time.

Priority Queue

PrioritySourceBehaviour
0 (highest)Manual "Run now"Always executes next
1Scheduled runsStandard queue position
2 (lowest)Corrective retriesNever blocks normal work

Manual triggers jump the queue. Corrective retries run last. Scheduled jobs sit in between. This means a "run now" click on an urgent job doesn't wait behind last night's scheduled batch.

Crash Recovery

If OpenHelm or your Mac was force-quit while a job was running, the next startup detects runs stuck in running state and marks them correctly before the scheduler resumes. No phantom running jobs, no misleading status.

Which Should You Use?

The decision comes down to what you're optimising for.

`/loop` — right for interactive exploration where you're watching the output. Wrong for anything unattended.

cron + shell script — right for a single, simple overnight job on a project you check daily. Wrong when you need structured history, silence detection, or are running jobs across multiple projects.

OpenHelm — right when you want reliable unattended execution, clean run history, self-correction on failures, and aren't willing to debug silent job failures at 3am.

The infrastructure you choose determines how much trust you can place in your overnight automation. A background job you have to babysit isn't a background job — it's just a longer interactive session. The point of running Claude Code unattended is to genuinely not need to be there. That requires the right execution layer beneath it.

More from the blog