Ver Fonte

feat(skills): loop-ops pattern catalog v2 — morphology + event-driven + archetypes

Turns the borrowed 7-item list into an original generative framework.

- Morphology, not a flat list: every loop = trigger (cadence | EVENT via a Channel
  | goal) x posture (L1/L2/L3) x locus (connector->cloud | local->desktop). Named
  patterns are points in that space; you compose your own from the axes.
- Event-driven trigger (Channels): a CI/error/deploy webhook pushes the tick in —
  cheaper + faster than polling — documented in claude-code-loops.md with the
  persistent-session trade-off. The right trigger for ci-watch/pr-watch/monitor.
- Six new archetypes beyond the GitHub-CI slice: metric-chase (drive a metric via
  iterate), regression-watch (benchmark/eval diff), digest (connector-only cloud
  routine), backfill (run-to-completion via /goal), monitor (event triage),
  freshness (doc/dep drift). Each carries its dominant failure mode + cost profile.
- loop-scaffold seeds all 13 (tier-aware); model-pricing knows all 13; budget seed
  is now pattern-aware (metric-chase's iterate fan-out gets 400k so it doctors clean
  out of the box — caught by a new doctor-clean assertion per archetype).
- SKILL pattern table reframed as the morphology; README/CHANGELOG updated.

Suite 96 -> 116; check-resources, doc-drift, validate all green; e2e re-confirmed
(metric-chase/digest scaffold -> check -> doctor).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
0xDarkMatter há 15 horas atrás
pai
commit
eb6ca17220

+ 10 - 0
CHANGELOG.md

@@ -54,6 +54,16 @@ feature releases live in the README "Recent Updates" section.
   an executable runner-agnostic `loop-run.sh` for external schedulers (cron / Task Scheduler /
   systemd / process-compose) and GitHub Actions demoted to one optional path. No GitHub
   Actions dependency anywhere. 96-assertion suite.
+- **pattern catalog v2 (morphology + event-driven + new archetypes)**: patterns are now a
+  generative **morphology** — `trigger` (cadence / **event** via a Channel / `goal`) ×
+  `posture` (L1/L2/L3) × `locus` (connector→cloud / local→desktop) — not a flat list, so
+  any point in the space composes. Adds the **event-driven trigger** (Channels: a CI/error/
+  deploy webhook pushes the tick in — cheaper + faster than polling) and six archetypes
+  beyond the GitHub-CI slice: `metric-chase` (drive a metric via `iterate`),
+  `regression-watch` (benchmark/eval diff), `digest` (connector-only cloud routine for
+  email/Asana), `backfill` (run-to-completion via `/goal`), `monitor` (event-driven
+  triage), `freshness` (doc/dep drift). `loop-scaffold` seeds all 13, the cost model knows
+  them, each carries its dominant failure mode + cost profile. 109-assertion suite.
 
 ## [3.2.0] - 2026-06-22
 

+ 2 - 2
README.md

@@ -23,7 +23,7 @@ From Python async patterns to Rust ownership models, from AWS Fargate deployment
 ## Recent Updates
 
 **v3.3.0** (June 2026)
-- 🔁 **`loop-ops` skill** — the *outer-loop* design discipline, twin to [`iterate`](skills/iterate/) (the inner loop). Where `iterate` drives one metric in one session, `loop-ops` is the orchestration layer above it: how to design, scaffold, cost, and **safely** run scheduled discover→triage→implement→verify→escalate-or-land agent loops. Its spine is the **risk-tier ladder** — L1 report → L2 assisted → L3 unattended — mapped onto Claude Code's *actual* permission model (the part a generic-agent methodology can't reach): each tier is a concrete permission mode, with the *enumerate-vs-isolate* fork and the load-bearing rule that **a scheduler invokes `claude -p`, not a session that spawns ungated children**. Ships a STATE/run-log/budget state spine, a 7-pattern catalog (PR watch, CI watch, dependency bump, changelog gen, merge hygiene, issue/daily scan), multi-loop coordination + kill switch, and three Resource-Protocol scripts — `loop-scaffold` (scaffold), `loop-check` (readiness scorer that refuses a green light on an unbounded scope / missing gate / undefined escalation), `loop-estimate` (token-$ estimate by pattern × cadence × model). Composes `fleet-worker` (spawn) and `fleet-ops` (land); 96-assertion offline suite. Builds on the public *loop engineering* discipline (Steinberger, Osmani) and the [Ralph loop](https://ghuntley.com/ralph/), grounded in this repo's auto-mode-classifier reference.
+- 🔁 **`loop-ops` skill** — the *outer-loop* design discipline, twin to [`iterate`](skills/iterate/) (the inner loop). Where `iterate` drives one metric in one session, `loop-ops` is the orchestration layer above it: how to design, scaffold, cost, and **safely** run scheduled discover→triage→implement→verify→escalate-or-land agent loops. Its spine is the **risk-tier ladder** — L1 report → L2 assisted → L3 unattended — mapped onto Claude Code's *actual* permission model (the part a generic-agent methodology can't reach): each tier is a concrete permission mode, with the *enumerate-vs-isolate* fork and the load-bearing rule that **a scheduler invokes `claude -p`, not a session that spawns ungated children**. Ships a STATE/run-log/budget state spine, a **13-pattern catalog framed as a morphology** (trigger × posture × locus — incl. event-driven Channels and `/goal`-completion archetypes like metric-chase & backfill), multi-loop coordination + kill switch, and three Resource-Protocol scripts — `loop-scaffold` (scaffold), `loop-check` (readiness scorer that refuses a green light on an unbounded scope / missing gate / undefined escalation), `loop-estimate` (token-$ estimate by pattern × cadence × model). Composes `fleet-worker` (spawn) and `fleet-ops` (land); 109-assertion offline suite. Builds on the public *loop engineering* discipline (Steinberger, Osmani) and the [Ralph loop](https://ghuntley.com/ralph/), grounded in this repo's auto-mode-classifier reference.
 
 **v3.2.0** (June 2026)
 - 🤖 **`fleet-worker` skill** — delegate tool-using, multi-step tasks to *cheaper headless Claude Code workers* — a cheaper Anthropic model (Sonnet/Haiku) or any Anthropic-compatible endpoint (e.g. GLM 5.2 via z.ai) — while an Opus orchestrator fans them out in parallel and gates their results before anything lands. Each worker is a real `claude -p` with Claude Code's full tool harness (Read/Write/Edit/Bash/Glob/Grep/Task) and any skills you provision into it, but a cheaper brain — isolated in its own git worktree + `CLAUDE_CONFIG_DIR`. Ships bash + PowerShell launchers, a result-gating collector, an endpoint health verifier, and the fleet-ops handoff recipes. fleet-worker is the **spawn** layer; [`fleet-ops`](skills/fleet-ops/) is the test-gated **landing** layer it hands winning branches to. Provider-agnostic.
@@ -312,7 +312,7 @@ See [skill-creator](skills/skill-creator/) for the complete guide.
 | [refactor-ops](skills/refactor-ops/) | Safe refactoring patterns, code smell detection, test-driven methodology |
 | [scaffold](skills/scaffold/) | Project scaffolding - generate boilerplate for APIs, web apps, CLIs, monorepos |
 | [iterate](skills/iterate/) | Autonomous improvement loop - modify, measure, keep or discard, repeat. Inspired by Karpathy's autoresearch. |
-| [loop-ops](skills/loop-ops/) | Outer-loop design discipline - the orchestration layer above `iterate`: risk-tier ladder (L1 report → L2 assisted → L3 unattended) mapped onto Claude Code's permission model, STATE/run-log/budget spine, 7-pattern catalog (PR watch, CI watch, dependency bump…), multi-loop coordination, kill switch. Composes iterate/fleet-worker/fleet-ops/native-loop. loop-scaffold/loop-check/loop-estimate scripts. |
+| [loop-ops](skills/loop-ops/) | Outer-loop design discipline - the orchestration layer above `iterate`: risk-tier ladder (L1 report → L2 assisted → L3 unattended) mapped onto Claude Code's permission model, STATE/run-log/budget spine, a 13-pattern morphology (cadence/event/goal × L1–L3 × cloud/local), multi-loop coordination, kill switch. Composes iterate/fleet-worker/fleet-ops/native-loop. loop-scaffold/loop-check/loop-estimate scripts. |
 | [testing-ops](skills/testing-ops/) | Test strategy patterns - mocking, CI testing, test data design |
 | [claude-code-ops](skills/claude-code-ops/) | Claude Code internals - full hook event catalog, skill frontmatter spec, headless/CLI reference, extension debugging |
 | [playwright-ops](skills/playwright-ops/) | Playwright e2e testing - selector hierarchy, fixtures, network mocking, CI sharding, flake hunting |

+ 21 - 12
skills/loop-ops/SKILL.md

@@ -1,6 +1,6 @@
 ---
 name: loop-ops
-description: "Design, scaffold, and safely run OUTER loops — scheduled discover→triage→implement→verify→escalate-or-land agent loops, the orchestration layer above a single run. Risk-tier ladder (L1 report → L2 assisted → L3 unattended) mapped onto Claude Code's permission model, a persistent STATE/run-log/budget spine, a production pattern catalog, multi-loop coordination, and a kill switch. Composes iterate (inner loop), fleet-worker (spawn), fleet-ops (land), and native /loop + /schedule. Triggers on: loop engineering, outer loop, loop design, design a loop, scheduled agent, autonomous loop, background agent loop, PR watch, CI watch, dependency bump, changelog gen, issue sort, daily scan, loop check, loop estimate, loop readiness, ralph loop, agent harness, escalation gate, risk tier, kill switch, run it overnight on a schedule."
+description: "Design, scaffold, and safely run OUTER loops — scheduled discover→triage→implement→verify→escalate-or-land agent loops, the orchestration layer above a single run. Risk-tier ladder (L1 report → L2 assisted → L3 unattended) mapped onto Claude Code's permission model, a persistent STATE/run-log/budget spine, a production pattern catalog, multi-loop coordination, and a kill switch. Composes iterate (inner loop), fleet-worker (spawn), fleet-ops (land), and native /loop + /schedule. Triggers on: loop engineering, outer loop, loop design, design a loop, scheduled agent, autonomous loop, background agent loop, PR watch, CI watch, dependency bump, changelog gen, issue sort, daily scan, metric chase, regression watch, digest loop, backfill, monitor loop, freshness check, event-driven loop, channel webhook, loop check, loop estimate, loop readiness, ralph loop, agent harness, escalation gate, risk tier, kill switch, run it overnight on a schedule."
 when_to_use: "Use when designing or running a recurring/scheduled agent loop rather than a one-shot task — e.g. 'set up a loop that triages PRs every 10 minutes', 'design an autonomous CI-failure recovery loop', 'how risky is this loop / is it ready to run unattended', 'estimate what this loop costs per month', 'build a loop-engineering setup'. For a single-session improvement loop against one metric, use iterate instead."
 license: MIT
 allowed-tools: "Read Write Edit Bash Glob Grep"
@@ -112,22 +112,31 @@ read/write contract in [references/state-spine.md](references/state-spine.md)):
 - **`loop.config.yaml`** — the loop's definition (goal, tier, cadence, scope, gate,
   budget, escalation). Scaffolded by `loop-scaffold`, scored by `loop-check`.
 
-## Pattern catalog
+## Pattern catalog (a morphology, not a fixed list)
 
-Seven battle-tested shapes, each with a cadence, a risk tier, and an escalation rule.
-Full skeletons in [references/pattern-catalog.md](references/pattern-catalog.md):
+Patterns are **compositions of three axes** — `trigger` (cadence / **event** via a Channel
+/ `goal`) × `posture` (L1/L2/L3) × `locus` (connector→cloud routine / local→Desktop task).
+The named patterns are well-trodden points in that space; compose your own from the axes.
+Full recipes + the morphology in [references/pattern-catalog.md](references/pattern-catalog.md):
 
-| Pattern | Cadence | Tier | One-line job |
+| Pattern | Trigger · Locus | Tier | One-line job |
 |---|---|---|---|
-| Daily Scan | 1–2 h | L1 | discover + prioritize, report only |
-| PR Watch | 5–15 min | L1 | watch review state, surface stuck PRs |
-| CI Watch | 5–15 min | L2 | triage build failures, propose a fix |
-| Dependency Bump | 6 h–1 d | L2 | patch-only bumps behind the cooldown + guard |
-| Changelog Gen | 1 d / tag | L1 | draft release notes for human approval |
-| Merge Hygiene | 1–6 h | L1 | hygiene: dead branches, stale flags |
-| Issue Sort | 2 h–1 d | L1 | classify + label, propose only |
+| `daily-scan` | cadence · local | L1 | discover + prioritize, report only |
+| `pr-watch` | event\|cadence · connector | L1 | watch review state, surface stuck PRs |
+| `ci-watch` | **event** · local | L2 | triage build failures, propose a fix |
+| `dep-bump` | cadence · local | L2 | patch-only bumps behind cooldown + guard |
+| `changelog-gen` | event(tag)\|cadence · local | L1 | draft release notes for approval |
+| `merge-hygiene` | cadence · local | L1 | dead branches, stale flags |
+| `issue-sort` | cadence · connector | L1 | classify + label, propose only |
+| `metric-chase` | **goal** · local | L2 | drive a metric (coverage/latency/eval) via `iterate` |
+| `regression-watch` | cadence\|event · local | L1 | run a benchmark/eval, flag a regression |
+| `digest` | cadence · **connector** | L1 | summarize email/Asana/news (cloud routine) |
+| `backfill` | **goal** · local | L2 | drain a migration/queue **to completion** |
+| `monitor` | **event** · local | L1 | error/deploy webhook → triage + page |
+| `freshness` | cadence · local | L1 | re-check docs/data/deps vs reality |
 
 Start any pattern at L1. Graduate to L2 only after the L1 reports prove its judgment.
+**Prefer `event` over `cadence`** where a webhook exists (cheaper, faster than polling).
 
 ## Multi-loop coordination & the kill switch
 

+ 6 - 0
skills/loop-ops/assets/model-pricing.json

@@ -17,6 +17,12 @@
     "changelog-gen":   { "input": 25000,  "output": 9000,  "subagents": 1 },
     "merge-hygiene":  { "input": 20000,  "output": 4000,  "subagents": 1 },
     "issue-sort":        { "input": 18000,  "output": 4000,  "subagents": 1 },
+    "metric-chase":      { "input": 50000,  "output": 20000, "subagents": 3 },
+    "regression-watch":  { "input": 45000,  "output": 8000,  "subagents": 1 },
+    "digest":            { "input": 30000,  "output": 7000,  "subagents": 1 },
+    "backfill":          { "input": 40000,  "output": 15000, "subagents": 2 },
+    "monitor":           { "input": 12000,  "output": 3000,  "subagents": 1 },
+    "freshness":         { "input": 35000,  "output": 6000,  "subagents": 1 },
     "custom":              { "input": 30000,  "output": 8000,  "subagents": 1 }
   }
 }

+ 26 - 4
skills/loop-ops/references/claude-code-loops.md

@@ -7,10 +7,11 @@ children* — is in [risk-tiers.md](risk-tiers.md); this is the how.
 
 ---
 
-A loop is two things, and Claude Code has **native** answers to both: a **cadence** (when
-a tick fires) and a **completion** rule (when the work stops). **Prefer the native
-mechanisms — they're zero/low-infra and need no GitHub Actions.** Reach for an external
-scheduler only when you want non-Claude-Code control.
+A loop's **trigger** answers *when a tick fires* — a **cadence** (poll on a clock) or an
+**event** (something pushed in by a Channel) — and its **completion** rule answers *when
+the work stops*. Claude Code has native answers to all three. **Prefer the native
+mechanisms — zero/low-infra, no GitHub Actions.** Reach for an external scheduler only for
+non-Claude-Code control.
 
 ## Cadence — when a tick fires
 
@@ -33,6 +34,27 @@ human-configured **authorizer** — no parent auto-mode session, so nothing bloc
 headless child. Many loop frameworks are CI/Actions-centric; loop-ops is
 runner-agnostic and **native-first** on purpose.
 
+## Event — when something happens (Channels)
+
+Polling burns tokens while nothing changes and lags the thing it watches. A
+[**Channel**](https://code.claude.com/docs/en/channels) (v2.1.80+, research preview) is an
+MCP plugin that **pushes** an external event — a CI failure, an error-tracker alert, a
+deploy webhook, a chat message — straight into a running session, so the tick fires *on the
+event* instead of on a timer.
+
+- **Cheaper + faster than polling** — no idle ticks; the loop reacts the instant the event
+  lands. The right trigger for `ci-watch`, `pr-watch`, `monitor`.
+- **The trade-off:** an event arrives only while a session is open, so an unattended
+  event-loop is a **persistent background session** (`claude --channels plugin:<name> …`,
+  or `-p` for non-interactive) kept alive — not a fully-detached cron. Detachment traded
+  for responsiveness.
+- **Setup:** install a channel plugin (Telegram/Discord/iMessage ship in the preview; build
+  a [webhook receiver](https://code.claude.com/docs/en/channels-reference) for CI/error/
+  deploy), launch with `--channels`, lock the sender allowlist. Anthropic-auth only (not
+  Bedrock/Vertex/Foundry).
+- **Still gated** — an event-driven tick runs under the same permission mode + allowlist as
+  any other; a webhook firing the loop never widens what it may do.
+
 ## Completion — when the work stops: `/goal`
 
 [`/goal <condition>`](https://code.claude.com/docs/en/goal) (v2.1.139+) keeps the session

+ 91 - 111
skills/loop-ops/references/pattern-catalog.md

@@ -1,138 +1,118 @@
-# Pattern Catalog — seven production loop shapes
+# Pattern Catalog — a morphology of loop shapes
 
-Each pattern is a proven outer-loop shape with a cadence, a starting risk tier, a gate,
-and an escalation rule. **Start every pattern at L1** (read-only) and graduate it only
-after its reports prove its judgment. The `loop-scaffold` script seeds a `loop.config.yaml`
-keyed by `--pattern <name>`; the names below are the canonical keys.
+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.
 
-The columns that matter for every pattern: **cadence** (how often), **tier** (starting
-autonomy), **gate** (what must pass before landing), **escalate** (what it must hand to a
-human instead of doing).
+## The three axes
 
----
-
-## `daily-scan` — discover + prioritize
-
-| | |
-|---|---|
-| Cadence | 1–2 h (weekday business hours) |
-| Tier | L1 (report only) |
-| Job | sweep the backlog/inbox/alerts; rank by priority; write the day's `STATE.md` |
-| Gate | none — it writes nothing but the report |
-| Escalate | everything; a human decides what to action |
-
-The off-peak, lowest-priority loop. It produces the work-list the other loops and the
-human draw from. Output is a `STATE.md` priority/watch/noise snapshot, nothing else.
-
----
-
-## `pr-watch` — watch review state
-
-| | |
-|---|---|
-| Cadence | 5–15 min |
-| Tier | L1 |
-| Job | list open PRs; flag stuck (no review N hours), failing checks, merge conflicts |
-| Gate | none — surfaces state, posts a summary comment at most |
-| Escalate | a human reviews/merges; the loop never merges |
-
-Skeleton: `gh pr list --json …` → classify each (waiting-on-review / failing / conflict /
-ready) → update `STATE.md` watch list → optionally one summary comment per PR.
-Public-comment text follows the repo's preview-before-send discipline.
+**1. Trigger — what starts a tick.**
 
----
-
-## `ci-watch` — triage build failures
+| 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 |
 
-| | |
-|---|---|
-| Cadence | 5–15 min |
-| Tier | L2 (after L1 proves triage quality) |
-| Job | detect red CI; classify the failure; at L2, propose a fix in a worktree |
-| Gate | `verify` (the failing test passes) **and** `guard` (full suite + typecheck) |
-| Escalate | flaky/infra failures, anything touching deploy/secrets, ambiguous root cause |
+> **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](claude-code-loops.md).
 
-The highest-priority loop in the multi-loop order — a red build blocks everyone. At L1 it
-classifies and reports; at L2 it opens a fix PR in an isolated worktree and hands the
-branch to `fleet-ops` for the gated merge. Never auto-merges to `main`.
+**2. Posture — how much autonomy** (the [risk tier](risk-tiers.md)): `L1` report · `L2`
+propose-and-human-gates · `L3` autonomous-in-a-denylist.
 
----
+**3. Locus — where it runs / what it can touch.**
 
-## `dep-bump` — patch-only bumps
+| 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 |
 
-| | |
-|---|---|
-| Cadence | 6 h – 1 d |
-| Tier | L2 |
-| Job | find outdated deps; bump **patch-only**, behind a release-age cooldown |
-| Gate | `guard` (full suite + build) passes; supply-chain cooldown satisfied |
-| Escalate | minor/major bumps, anything failing the guard, any flagged advisory |
-
-Pair with [`supply-chain-defense`](../../supply-chain-defense/SKILL.md): respect the
-7-day cooldown (`preinstall-check.sh`) and behavioural score before a bump lands. Patch
-bumps that pass both the cooldown and the guard are the *only* class safe to auto-land,
-and only on a feature branch, never `main`.
+The recipe-selector in [claude-code-loops.md](claude-code-loops.md) is just these axes
+resolved to a mechanism. A loop = **(trigger × posture × locus) + the [state spine](state-spine.md)**.
 
 ---
 
-## `changelog-gen` — release-note drafts
-
-| | |
-|---|---|
-| Cadence | 1 d, or on tag |
-| Tier | L1 (draft only) |
-| Job | summarize merged PRs since the last tag into a `RELEASE_NOTES_DRAFT.md` |
-| Gate | none — produces a draft for human approval |
-| Escalate | the human edits + publishes; the loop never runs `gh release create` |
-
-Drafts to a file, never publishes. Publishing a release is a one-way visibility
-commitment — it stays a human step (the repo's release-review discipline). Pair with
-[`github-ops`](../../github-ops/SKILL.md) for the human-driven publish.
+## The catalog
+
+Each row: the axes, the recommended native mechanism, the job (gate → what it escalates),
+and the **failure mode to watch** ([failure-modes.md](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`](../../iterate/SKILL.md) | 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 |
 
 ---
 
-## `merge-hygiene` — hygiene
-
-| | |
-|---|---|
-| Cadence | 1–6 h (off-peak) |
-| Tier | L1 |
-| Job | find merged-and-deletable branches, stale feature flags, orphaned artifacts |
-| Gate | none at L1; at L2, branch deletion behind a "merged + N days old" rule |
-| Escalate | anything ambiguous; never deletes a branch with unmerged commits |
-
-Honors [worktree boundaries](../../../rules/worktree-boundaries.md): never touches another
-session's `.claude/worktrees/`, never sweeps with `git add -A`.
+## Notes on the patterns that need them
+
+- **`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`](../../iterate/SKILL.md).** 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.
 
 ---
 
-## `issue-sort` — classify + label
+## Composing a pattern not in the catalog
 
-| | |
-|---|---|
-| Cadence | 2 h – 1 d |
-| Tier | L1 (propose-only) |
-| Job | classify new issues (bug/feature/question/dupe), suggest labels + priority |
-| Gate | none — proposes labels, applies only the mechanical ones at L2 |
-| Escalate | priority calls, dupe-closing, anything needing product judgment |
+Pick a point in the space the named patterns don't cover. Examples:
 
-At L1 it proposes; at L2 it may apply purely-mechanical labels (`needs-triage` →
-`bug`/`docs`) but never closes an issue or sets priority unattended.
+- *event · connector · L1* — a Slack message (Channel) triggers a read-only lookup against
+  a connector. (A "support-triage" loop.)
+- *goal · connector · L2* — drain an Asana backlog to empty via `/goal`, updating tasks
+  through the connector.
+- *cadence · local · L3* — a nightly autonomous refactor in an isolated container.
 
----
+The discipline is identical regardless of the point: bounded scope, a gate, an escalation
+rule, a kill switch, a budget — and **start at L1**.
 
-## Choosing a pattern → tier → cadence
+## Choosing — the short version
 
-1. **Match the job** to the closest pattern; use `custom` only if none fit.
-2. **Start at the pattern's L1 tier.** Run it read-only until you trust its reports.
-3. **Set the cadence** to the slowest that still catches the work in time — cadence is
-   the biggest cost lever (see [loop-estimate](../scripts/loop-estimate.py)). A 5-min PR
-   pr-watch loop costs 3× a 15-min one for marginal freshness gain.
-4. **Graduate** to L2 only with a `guard`, a `worktree`, an `escalation` rule, and a
-   `land_via` — then re-run `loop-check` at the new tier.
+1. **Locus first:** does it touch local code? → `local` (Desktop task/daemon). Pure
+   connector work? → `connector` (cloud routine).
+2. **Trigger next:** is there an event to react to? → `event` (Channel) — cheaper + faster.
+   A clear finish line? → `goal`. Otherwise → `cadence`, slowest that still catches the work.
+3. **Posture:** start **L1**. Graduate to L2 (with a guard, worktree, escalation, `land_via`)
+   only once the reports earn it; re-run `loop-check` + `loop-doctor --live` at the new tier.
 
 ## See also
 
-- [risk-tiers.md](risk-tiers.md) — what each tier may do and the permission-mode mapping.
-- [state-spine.md](state-spine.md) — the multi-loop priority order these patterns share.
+- [risk-tiers.md](risk-tiers.md) — the posture axis (permission-mode mapping).
+- [claude-code-loops.md](claude-code-loops.md) — the trigger/locus axes resolved to mechanisms + the recipe selector.
+- [failure-modes.md](failure-modes.md) — the "watch" column, in depth.
+- [state-spine.md](state-spine.md) — the multi-loop priority order these share.
 - [../assets/loop.config.template.yaml](../assets/loop.config.template.yaml) — the config every pattern fills in.

+ 29 - 2
skills/loop-ops/scripts/loop-scaffold.sh

@@ -120,7 +120,7 @@ esac
 # Seed a near-ready config for a known --pattern (the user reviews, doesn't start
 # from blank placeholders). Doctrine: always scaffold at the chosen tier; report/
 # propose/draft patterns carry no gate (VERIFY_SEED empty), code-changing ones do.
-SEEDED=0; SCOPE_SEED=""; GOAL_SEED=""; ESCAL_SEED=""; VERIFY_SEED=""; GUARD_SEED=""
+SEEDED=0; SCOPE_SEED=""; GOAL_SEED=""; ESCAL_SEED=""; VERIFY_SEED=""; GUARD_SEED=""; BUDGET_SEED=""
 case "$PATTERN" in
   daily-scan) SEEDED=1
     SCOPE_SEED="src/**"
@@ -152,6 +152,33 @@ case "$PATTERN" in
     SCOPE_SEED="src/**"
     GOAL_SEED="Classify new issues and suggest labels + priority; propose only; never close or set priority unattended."
     ESCAL_SEED="priority calls, dupe-closing, anything needing product judgment" ;;
+  metric-chase) SEEDED=1
+    SCOPE_SEED="src/**"
+    GOAL_SEED="Drive a measurable target (coverage/latency/bundle/eval score) to goal via iterate; keep gains, discard regressions."
+    ESCAL_SEED="target unreachable after the budget, guard failures, any change to the gate/test itself"
+    VERIFY_SEED="npm test -- --coverage"; GUARD_SEED="npm run typecheck"
+    BUDGET_SEED=400000 ;;   # iterate fan-out is the most expensive tick — fit it
+  regression-watch) SEEDED=1
+    SCOPE_SEED="bench/**"
+    GOAL_SEED="Run the benchmark/eval suite, diff against the recorded baseline; report a regression; never edit the suite."
+    ESCAL_SEED="a confirmed regression (a human triages); a single flaky run is advisory, not a page" ;;
+  digest) SEEDED=1
+    SCOPE_SEED="reports/**"
+    GOAL_SEED="Summarize email/Asana/calendar/news via connectors into a morning report; read-only, never act."
+    ESCAL_SEED="anything requiring a reply or an action; this loop only summarizes" ;;
+  backfill) SEEDED=1
+    SCOPE_SEED="src/**"
+    GOAL_SEED="Drain a migration/queue to completion via /goal; one item per step, verify each; stop when empty or after the bound."
+    ESCAL_SEED="any item needing a judgment call; never exceed the stop-after-N / token bound"
+    VERIFY_SEED="npm test"; GUARD_SEED="npm run typecheck" ;;
+  monitor) SEEDED=1
+    SCOPE_SEED="src/**"
+    GOAL_SEED="React to an error/log/deploy event (via a Channel); triage it and page a human on a real anomaly; never auto-remediate prod."
+    ESCAL_SEED="any anomaly worth a human; production remediation; anything destructive" ;;
+  freshness) SEEDED=1
+    SCOPE_SEED="docs/**"
+    GOAL_SEED="Re-check docs/data/deps/links against reality on a cadence; report confirmed drift; never auto-edit on a transient failure."
+    ESCAL_SEED="confirmed drift a human should fix; a transient/network failure is advisory only" ;;
 esac
 
 TARGET_DIR="$DIR/$NAME"
@@ -228,7 +255,7 @@ goal: "$GOAL_SEED"
 scope:
   - "$SCOPE_SEED"
 escalation: "$ESCAL_SEED"
-budget_tokens: 200000
+budget_tokens: ${BUDGET_SEED:-200000}
 kill_switch: ".loops/$NAME/PAUSED exists, OR the loop-pause label is set"
 EOF
   if [[ "$TIER" == "L1" ]]; then

+ 12 - 0
skills/loop-ops/tests/run.sh

@@ -134,6 +134,18 @@ bash "$AUDIT" "$SB/seed/seed-l2/loop.config.yaml" >/dev/null 2>&1; expect_exit "
 # an unknown pattern falls back to the generic placeholder template (not ready)
 bash "$INIT" --name seed-x --pattern custom --tier L1 --dir "$SB/seed" >/dev/null 2>&1
 case "$(cat "$SB/seed/seed-x/loop.config.yaml")" in *"<one sentence"*) ok "unknown pattern uses generic template";; *) no "unknown pattern did not use template";; esac
+# v2 archetypes: scaffold must be audit-clean AND doctor-clean at L1 + known to the cost model
+# (doctor-clean catches budget < tokens/run — the metric-chase trap: it seeds a bigger budget)
+for p in metric-chase regression-watch digest backfill monitor freshness; do
+  bash "$INIT" --name "a-$p" --pattern "$p" --tier L1 --dir "$SB/arch" >/dev/null 2>&1
+  bash "$AUDIT" "$SB/arch/a-$p/loop.config.yaml" >/dev/null 2>&1; expect_exit "archetype $p seeds audit-clean (L1)" 0 $?
+  bash "$DOCTOR" --offline "$SB/arch/a-$p/loop.config.yaml" >/dev/null 2>&1; expect_exit "archetype $p doctors clean (L1)" 0 $?
+  "$PYTHON" "$COST" --pattern "$p" --cadence 1h --model claude-haiku-4-5 >/dev/null 2>&1; expect_exit "cost model knows $p" 0 $?
+done
+# the most expensive archetype at L2: gate filled, budget fits the tick (audit + doctor clean)
+bash "$INIT" --name a-mc --pattern metric-chase --tier L2 --cadence 1h --dir "$SB/arch" >/dev/null 2>&1
+bash "$AUDIT" "$SB/arch/a-mc/loop.config.yaml" >/dev/null 2>&1; expect_exit "metric-chase L2 audits clean -> 0" 0 $?
+bash "$DOCTOR" --offline "$SB/arch/a-mc/loop.config.yaml" >/dev/null 2>&1; expect_exit "metric-chase L2 doctors clean (budget fits) -> 0" 0 $?
 
 # ── loop-check: a freshly-init'd config is NOT ready (placeholders) -> 10 ───
 echo "-- loop-check --"