Loops aren't a fixed list of recipes — they're compositions of three orthogonal axes.
Name the axes and the patterns fall out; you can also compose ones not named here. The
named patterns below are the well-trodden points in this space. loop-scaffold seeds a
loop.config.yaml keyed by --pattern <name> (the canonical keys); the rest of the space
you compose by hand. Start every pattern at L1 and graduate only once its reports prove
its judgment.
1. Trigger — what starts a tick.
| Trigger | Fires when | Mechanism | Best for |
|---|---|---|---|
cadence |
a clock interval elapses | /loop (supervised), Desktop task, cloud routine, or a daemon |
steady polling — backlog, PRs, deps |
event |
an external thing happens (CI fail, error, deploy, message) | a Channel (MCP webhook receiver) pushes it into a live session | responsiveness + low cost — no idle polling |
goal |
runs continuously until a condition holds, then stops | /goal (+ auto mode) |
run-to-completion — migrations, metric targets |
Event beats poll when you can get it. A CI webhook firing the tick is cheaper and faster than a 10-min poll. The cost: a Channel needs a persistent session (
claude --channels …in a background process), and it's a research-preview, Anthropic-auth-only feature. Cadence stays fully detached; event trades that for responsiveness. See claude-code-loops.md.
2. Posture — how much autonomy (the risk tier): L1 report · L2
propose-and-human-gates · L3 autonomous-in-a-denylist.
3. Locus — where it runs / what it can touch.
| Locus | Mechanism | Can touch | Use when |
|---|---|---|---|
connector |
cloud routine (/schedule) |
your claude.ai connectors (email, Asana, Slack, issues) — no local files | the work lives in services, not your repo |
local |
Desktop task / daemon / /loop |
the repo, build, models, local tools | the work touches local state |
The recipe-selector in claude-code-loops.md is just these axes resolved to a mechanism. A loop = (trigger × posture × locus) + the state spine.
Each row: the axes, the recommended native mechanism, the job (gate → what it escalates), and the failure mode to watch (failure-modes.md).
| Pattern | Trigger · Locus | Start tier | Mechanism | Job → escalates | Watch |
|---|---|---|---|---|---|
daily-scan |
cadence · local | L1 | Desktop task (off-peak) | sweep backlog/alerts, write STATE.md → all to a human |
silent-stop |
pr-watch |
event|cadence · connector | L1 | Channel (PR webhook) or cloud routine | flag stuck/failing/conflicted PRs → never merges | runaway tokens if polled tight |
ci-watch |
event · local | L2 | Channel (CI webhook) → fix in a worktree | failing test passes + full guard → flaky/deploy/secrets | gate reward-hacking |
dep-bump |
cadence · local | L2 | Desktop task/daemon | patch-only behind cooldown + guard → minor/major, advisories | supply-chain |
changelog-gen |
event(on tag)|cadence · local | L1 | tag-event or Desktop task | draft RELEASE_NOTES_DRAFT.md → human publishes |
— |
merge-hygiene |
cadence · local | L1 | Desktop task (off-peak) | dead branches / stale flags → ambiguous deletes | worktree-boundaries |
issue-sort |
cadence|event · connector | L1 | cloud routine | classify + suggest labels → priority/dupe-close | — |
metric-chase |
goal · local | L2 | /goal driving iterate |
drive coverage/latency/bundle/eval-score to target → unreachable / guard fails | gate reward-hacking · high cost |
regression-watch |
cadence|event(on release) · local | L1→L2 | Desktop task/daemon | run a benchmark/eval, diff vs baseline → a real regression | flaky bench = false alarm · high/run |
digest |
cadence · connector | L1 | cloud routine | summarize email/Asana/calendar/news → nothing (read-only) | over-scoped connector |
backfill |
goal · local | L2/L3 | /goal (+ worktree/container) |
drain a migration/queue to completion → an item needing judgment | runaway budget · long |
monitor |
event · local | L1 | Channel (error/log/deploy webhook) | triage the event → page a human on anomaly | alert fatigue · needs a live session |
freshness |
cadence · local | L1 | Desktop task (daily/weekly) | re-check docs/data/deps vs reality → confirmed drift | transient failure ≠ drift |
ci-watch / pr-watch — prefer event over poll. Wire a CI/PR webhook through a
Channel so the tick fires on the event, not a timer. A polled pr-watch at 5 min costs
~3× a 15-min one for marginal freshness; the event-driven version costs ~nothing while
quiet. At L2, ci-watch opens a fix in a worktree and hands the branch to fleet-ops;
never auto-merges main.metric-chase is the bridge to iterate. The loop's
trigger is a /goal ("coverage ≥ 90, or stop after N turns"); the work each turn is
an iterate step (modify → measure → keep/discard). Use it for any measurable target —
including an eval score (this is the GLM/Opus-bench shape). Highest cost class; bound it.digest is the canonical cloud-routine pattern. It needs connectors, not code, so
it's the one archetype where the fresh-clone cloud routine is exactly right — it keeps
your claude.ai connectors and runs with the machine off. Read-only: no write scopes.backfill is run-to-completion, not recurring. A /goal drains the queue/migration;
when the condition holds it stops and clears. Bound it (or stop after N, a token budget)
— it's the runaway-budget risk made flesh. For arbitrary execution, run it in a container.monitor is the purest event loop. An error-tracker/deploy webhook → a Channel → a
persistent background session that triages and pages on anomaly. No polling at all. The
trade-off is keeping that session alive.regression-watch runs a real suite each tick (expensive), so cadence it slowly or
trigger it on a release event. Treat a transient/flaky failure as advisory (don't page on
one red run) — the same exit-7-vs-exit-10 discipline our staleness verifiers use.Pick a point in the space the named patterns don't cover. Examples:
/goal, updating tasks
through the connector.The discipline is identical regardless of the point: bounded scope, a gate, an escalation rule, a kill switch, a budget — and start at L1.
local (Desktop task/daemon). Pure
connector work? → connector (cloud routine).event (Channel) — cheaper + faster.
A clear finish line? → goal. Otherwise → cadence, slowest that still catches the work.land_via)
only once the reports earn it; re-run loop-check + loop-doctor --live at the new tier.