cd /news/ai-agents/loop-harness-is-here Β· home β€Ί topics β€Ί ai-agents β€Ί article
[ARTICLE Β· art-24612] src=github.com pub= topic=ai-agents verified=true sentiment=Β· neutral

Loop Harness Is Here

Loop Harness, an autonomous loop engineering tool for coding agents, is now available, enabling scheduled Claude sessions to perform tasks like code review and PR creation in isolated git worktrees with a second verification step before shipping output. The system runs LLMs with shell access unattended against user repositories, capable of committing code and opening pull requests, with a safety model that requires users to start with read-only loops and never grant unnecessary tools.

read5 min publishedJun 11, 2026

An autonomous "loop engineering" harness for Coding Agents. You don't prompt Claude ; loops do. Each loop wakes up on a cadence, gives a Claude session a task-specific skill, lets it work in an isolated git worktree, has a second Claude session verify the work, and only then ships the output (PR, comments, Slack message).

Warning

This harness runs an LLM with shell access, unattended, against your repositories. Write-capable loops can commit code, open PRs, and (with output: commit

) push to branches. Read the safety model before pointing it at anything you care about, start with read-only loops (pr-reviewer

, issue-groomer

), smoke-test with run-once

in the foreground, and never grant tools a loop doesn't need. You are responsible for what your loops ship. No warranty β€” see LICENSE.

scheduler tick ─▢ due loop ─▢ git worktree ─▢ primary agent (claude -p + skill)
                                  β”‚
                                  β–Ό
                     staged output (commits / outbox files)
                                  β”‚
                                  β–Ό
                  verifier agent (claude -p, skeptical) ── FAIL ─▢ log + retry next cycle
                                  β”‚ PASS
                                  β–Ό
                  ship: push + PR / post comments / Slack ─▢ state updated

bash

3.2+ (macOS default works),git

,jq

,curl

β€” authenticated (gh

gh auth login

)β€” Claude Code CLI, logged inclaude

Variable Required Purpose
GITHUB_TOKEN
no* GitHub auth for gh (*or use gh auth login )
LOOP_SLACK_WEBHOOK
no Slack incoming-webhook URL for notifications. Unset = Slack silently skipped
LOOP_WORKTREE_ROOT
no Where worktrees are created (default: $TMPDIR/loop-worktrees )

Never hardcode tokens anywhere in this tree. The env var names are configurable in config.yaml

(github_token_env

, slack_webhook_env

).

cp config.example.yaml config.yaml
$EDITOR config.yaml

chmod +x orchestrator.sh dashboard.sh connectors/*.sh

./orchestrator.sh run-once triage-ci ~/projects/my-app

./orchestrator.sh start

./dashboard.sh
./orchestrator.sh logs            # orchestrator log
./orchestrator.sh logs triage-ci  # latest run of one loop

./orchestrator.sh stop

./orchestrator.sh tick

runs exactly one scheduler pass β€” useful if you'd rather drive the harness from cron/launchd instead of the built-in daemon.

./dashboard.sh

gives a live status table across all configured loops:

Orchestrator: RUNNING (pid 48121)

LOOP                   CADENCE      STATUS     LAST RUN   RUNS   SUCCESS%  ITEMS    AVG DUR   FAILURES
----                   -------      ------     --------   ----   --------  -----    -------   --------
dependency-updater     0 6 * * *    success    06:01      4      100%      6        312s      0
doc-sync               0 7 * * *    success    07:02      4      100%      2        198s      0
issue-groomer          every 1h     success    14:30      9      100%      14       87s       0
pr-reviewer            every 3m     running    15:21      112    98%       31       64s       2
triage-ci              every 10m    success    15:14      38     95%       9        241s      2
Loop Cadence Writes code? Ships
triage-ci
every 10m yes (worktree) PR with verified CI fix, or diagnosis
issue-groomer
every 1h no labels + P0 implementation plans
pr-reviewer
every 3m (polls for new PRs) no inline comments, summary, approval if clean
dependency-updater
daily 06:00 yes (worktree) PR with test-verified updates
doc-sync
daily 07:00 yes (worktree) PR patching doc drift

Worktree isolationβ€” write loops never touch your checkout; each run gets a fresh worktree on aloop/<name>/<ts>

branch.Staged outputsβ€” the primary agent cannot post or push. It stages commits, aPR_BODY.md

, or "outbox" action files (issue-comment-<n>.md

,pr-approve-<n>.md

, ...).Verification gateβ€” a secondclaude -p

session with read-only-ish tools inspects the diff and staged outputs, runs cheap checks, and must printVERDICT: PASS

. Only then does the orchestrator push/post.Scoped permissionsβ€” each loop'sallowed_tools

is passed toclaude --allowedTools

.Idempotenceβ€” every loop's state file dedups processed item IDs (CI run IDs, issue/PR numbers, package@version, commit SHAs). Re-running is always safe.Graceful degradationβ€” a failing loop logs, records the failure in state, and the orchestrator moves on. The daemon never crashes because a loop did.

One JSON file per loop-instance at state/<loop>@<repo>.json

:

{
  "last_run": 1760000000, "last_status": "success",
  "processed": {"12345": "2026-06-10T09:00:00+0000"},
  "in_progress": {},
  "failures": [{"item": "98", "error": "verification failed", "retries": 1, "ts": "..."}],
  "metrics": {"runs": 42, "successes": 40, "items_processed": 61, "total_duration_s": 9000}
}

Delete a state file to make a loop reprocess everything. state/.run/

holds pidfiles and locks β€” safe to delete when nothing is running.

Skillβ€”skills/my-loop.md

: role, steps, success criteria, failure handling, and a finalRESULT:

line format (RESULT: DONE items=<id,...>

/NOTHING_TO_DO

/BLOCKED reason=...

). Item IDs initems=

drive deduplication β€” make them stable. - Definitionβ€”definitions/my-loop.yaml

:

name: my-loop
cadence: every 30m        # or a 5-field cron expression: "0 9 * * 1-5"
trigger: schedule
skill: my-loop.md
worktree: true            # true for anything that writes code
output: pr                # pr | issue-comment | slack-message | commit | log-only
state_file: state/my-loop.json
verify: true
timeout_minutes: 15
allowed_tools: "Bash,Read,Edit,Write,Glob,Grep"
notify_slack: false

Wire itβ€” add the loop name to a repo'sloops:

list inconfig.yaml

. - Testβ€”./orchestrator.sh run-once my-loop

, read the logs, tune the skill. Thenstart

.

pr

β€” agent commits in its worktree + writesPR_BODY.md

(uncommitted); orchestrator pushes the branch and opens the PR after verification.issue-comment

/slack-message

β€” agent stages action files in$LOOP_OUTBOX

; orchestrator posts them after verification. Formats:NN-issue-comment-<n>.md

,NN-issue-label-<n>.txt

,NN-pr-comment-<n>.md

,NN-pr-inline-<n>.jsonl

,NN-pr-approve-<n>.md

,NN-slack.md

.commit

β€” push directly to the default branch (use sparingly).log-only

β€” no external output.

schedule

is native. git-push

/ webhook

/ file-change

are implemented as fast polling (see pr-reviewer

: 3-minute cadence + state dedup β‰ˆ "on new PR"). For true push triggers, call ./orchestrator.sh run-once <loop>

from a webhook handler or git hook β€” it's idempotent, so duplicate triggers are harmless.

repos:                      # repo β–Έ loops mapping (inline list required)
  - path: ~/projects/my-app
    loops: [triage-ci, pr-reviewer]
concurrency: 5              # max parallel Claude sessions
log_retention_days: 7
scheduler_tick_seconds: 30
slack_webhook_env: LOOP_SLACK_WEBHOOK
github_token_env: GITHUB_TOKEN
claude_bin: claude          # override to pin a path/version
defaults:                   # per-loop fallbacks
  worktree: true
  verify: true
  max_retries: 2
  timeout_minutes: 15

The config parser is intentionally small (portable shell): keep the file flat, inline [a, b]

lists for loops:

, no anchors or multiline values.

Loop never firesβ€” check./dashboard.sh

(cadence parsed?), thenlogs

. Cron cadences need the daemon running during the matching minute.PRs not openingβ€”gh auth status

; check the run's.log

file for push errors; verifier may be failing (see.verify.log

next to the run log).Everything BLOCKEDβ€” usuallygh

auth or rate limits; the RESULT line in.agent.log

says why.Stuck lockβ€” if a machine crash leavesstate/.run/<instance>.lock

behind, delete it.

── more in #ai-agents 4 stories Β· sorted by recency
aisecurityandsafety.org Β· Β· #ai-agents
Anthropic
sponsored brought to you by zahid.host 4,200+ EU-deployed projects
reading about agents? ship yours in a single git push.

Run your AI side-project on zahid.host

EU-based hosting, git-push deploys, automatic HTTPS, no cold starts. Free tier with a custom domain β€” perfect for shipping the agent you just read about.

$git push zahid main
β†’ Live at https://your-agent.zahid.host βœ“
Get free account β†’ Pricing
from €0/mo Β· no card required
LIVE [news/loop-harness-is-here] indexed:0 read:5min 2026-06-11 Β· β€”