Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 15 additions & 3 deletions .claude/skills/loop-guardrails/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,19 @@ Audit a loop for safety and fix gaps. The standard is `guardrails/checklist.md`.
For the given loop (file path, command, or config), verify all of:

1. **Max iteration count.** Is there a hard cap on iterations/turns?
- bash ralph → a counter that breaks the `while` loop
- `/ralph-loop` → `--max-iterations N`
- bash ralph → a counter that breaks the `while` loop, **plus** per-call
`claude -p --max-turns N` and a wall-clock `timeout` (a counter between
calls can't stop a call that never returns)
- `/ralph-loop` → `--max-iterations N` — but its count lives in
`.claude/ralph-loop.local.md` in the agent's worktree and `0` means
unlimited, so for an unattended run flag it ⚠️ unless something outside the
agent's reach also caps the run
- Agent SDK → `max_turns` / `maxTurns`
- `/goal` → bounded by the validator, but confirm the condition is actually
reachable and provable from the agent's surfaced output
If missing, add it. Default cap: 20.
Anything relying on `--max-turns`/`max_turns` needs **Claude Code v2.1.281+**
(earlier versions could retry a turn indefinitely, ignoring it). If missing,
add it. Default cap: 20.

2. **No-progress / stall detection.** Does the loop bail when it stops making
progress (no git diff, repeated identical tool calls, unchanged test state)
Expand All @@ -37,6 +44,11 @@ For the given loop (file path, command, or config), verify all of:
4. **Verification inside the loop.** Is there a single deterministic success
check (test/lint/typecheck) the agent runs *itself*, declaring done only on
pass? If the loop trusts the model's self-assessment of correctness, fix it.
Then check the check can't be forged: if the agent can edit the tests, the
prompt, or the harness, the loop must detect it (fingerprint them, as
`templates/ralph/run.sh` does with `PROTECTED_PATHS`) or run them read-only.
The same rule applies to 1–3: no counter or budget may live in a file the
agent can write.

5. **Right tool for the job.** Loops are for well-defined, checkable work. Flag
if the task needs human judgment / design decisions / one-shot ops / prod
Expand Down
15 changes: 10 additions & 5 deletions .claude/skills/new-loop/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ that returns clear pass/fail (e.g. `pytest test/users`, `npm test`,
| Pattern | Use when | Mechanism |
|---|---|---|
| **`/goal`** (default) | Iterate-to-completion on a well-scoped task with a provable success condition. | `/goal` with a separate validator model (defaults to Haiku). Requires Claude Code v2.1.139+. |
| **ralph** (`/ralph-loop` or bash) | Long autonomous build where each iteration should reset context to anchor files. | Official `ralph-wiggum` plugin (`--max-iterations` defaults to **unlimited** — always set it), or `templates/ralph/run.sh`. |
| **ralph** (bash or `/ralph-loop`) | Long autonomous build where each iteration should reset context to anchor files. | `templates/ralph/run.sh` for anything unattended (all stops enforced outside the agent's reach, per-call caps, needs Claude Code v2.1.281+). The official `ralph-wiggum` plugin only for supervised in-session runs: `--max-iterations` defaults to **unlimited**, and its count lives in `.claude/ralph-loop.local.md` in the agent's own worktree, where `0` means no cap. |
| **`/loop`** | Recurring maintenance on an interval (babysit PRs, poll a deploy). | Bundled `/loop` skill (v2.1.72+); cron-backed, session-scoped; recurring tasks expire after 7 days. |

Default to `/goal` for "do this task once, correctly." Use ralph for
Expand All @@ -51,10 +51,15 @@ Create `loops/<slug>/` (slug derived from the task) containing:

## Step 3 — Bake in the three hard stops (always)

1. **Max iterations** — set a concrete cap (default 20; raise deliberately).
2. **No-progress detection** — instruct the loop to bail if N consecutive
iterations produce no git diff / no test-state change (default N=3), and
document blockers instead of looping forever.
1. **Max iterations** — set a concrete cap (default 20; raise deliberately),
and cap each `claude -p` call too (`--max-turns`, a wall-clock `timeout`;
defaults in `guardrails/budget.env`). Require Claude Code v2.1.281+.
2. **No-progress detection** — the harness bails if N consecutive iterations
produce no worktree change (default N=3), and the prompt tells the agent to
document blockers. An instruction in the prompt alone is not a stop — the
agent grades itself; `run.sh` enforces it from outside.
Set `PROTECTED_PATHS` to the prompt, harness and test files so the loop stops
if the agent edits its own checks.
3. **Budget ceiling** — set `MAX_BUDGET_USD` (ralph `run.sh` / Agent SDK
`max_budget_usd`), or pass `--max-budget-usd` on the Claude Code invocation
(since v2.1.217 it also halts background subagents at the cap). A reminder
Expand Down
22 changes: 17 additions & 5 deletions .claude/skills/update-knowledge/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -106,11 +106,23 @@ changes guidance — and quote it verbatim rather than paraphrasing a summary.

### 5. Diff and draft
Compare findings to the current knowledge base. For each genuinely new or
changed fact: update the relevant section of `00-primer.md`, add/adjust the
entry in `sources.md` with its confidence, and write a dated `CHANGELOG.md`
entry (newest first, above the marker line) summarizing what changed and why.
Do not churn wording for its own sake — only substantive changes. Preserve
existing corrections/caveats.
changed fact: add/adjust the entry in `sources.md` with its confidence, and
write a dated `CHANGELOG.md` entry (newest first, above the marker line)
summarizing what changed and why. Do not churn wording for its own sake — only
substantive changes. Preserve existing corrections/caveats.

**The primer is a briefing, not a log — it has a size budget.** Touch
`00-primer.md` only when a finding changes the *state of the practice* or the
guidance that follows from it (a stop mechanism breaks or ships, a correction
to something the primer asserts, a shift in what a loop author should do).
Per-release changelog items, paper-by-paper digests, quotes, and backlog status
go in `sources.md`; the primer gets at most a sentence of synthesis and a
pointer. Prefer replacing a primer passage over appending to it. The primer must
stay **≤ 750 lines** — `.github/workflows/kb-size.yml` fails the PR otherwise —
so if a pass needs room, condense or move older primer detail to `sources.md`
or `knowledge/archive/` in the same PR. (Context: the primer grew from 559 to
3,303 lines in ten weekly passes before this rule; it was condensed back to a
briefing on 2026-09-29.)

### 6. Prune — archive, don't accumulate
The knowledge base's whole value is being *current*, not exhaustive. Before
Expand Down
30 changes: 30 additions & 0 deletions .github/workflows/kb-size.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: kb-size

# Keeps knowledge/00-primer.md a briefing, not a log. The update-knowledge
# routine is told to respect this budget (SKILL.md step 5), but a budget the
# agent grades itself on is no budget — the same reason a cost alert is not a
# ceiling — so the limit is checked here. Raising it is a human decision: edit
# MAX_PRIMER_LINES below in a human-authored PR, with the reason in the commit.

on:
pull_request:
types: [opened, synchronize, reopened, edited]

permissions:
contents: read

jobs:
primer-size:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Check primer length
env:
MAX_PRIMER_LINES: "750"
run: |
lines=$(wc -l < knowledge/00-primer.md)
echo "knowledge/00-primer.md: $lines lines (limit $MAX_PRIMER_LINES)"
if [ "$lines" -gt "$MAX_PRIMER_LINES" ]; then
echo "::error file=knowledge/00-primer.md::Primer is $lines lines, over the $MAX_PRIMER_LINES-line budget. Move per-release detail, paper digests and backlog status to knowledge/sources.md or knowledge/archive/ (see update-knowledge SKILL.md step 5)."
exit 1
fi
8 changes: 6 additions & 2 deletions .github/workflows/self-edit-guard.yml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name: self-edit-guard

# Enforces the update-knowledge routine's self-improvement envelope
# (.claude/skills/update-knowledge/SKILL.md step 7). Runs on EVERY PR to main —
# (.claude/skills/update-knowledge/SKILL.md step 7). Runs on EVERY PR —
# deliberately no `paths:` filter, so a scope-violating `self-edit:` commit
# cannot dodge the check by not touching SKILL.md. The script no-ops in a few
# seconds when a PR has no `self-edit:` commits.
Expand All @@ -10,9 +10,13 @@ name: self-edit-guard
# add it as a required status check in the repo's branch-protection rules for
# `main`. Without that, human merge remains the enforcement floor.

# Runs on PRs to ANY base, not just main: the weekly routine stacks each pass on
# the previous unmerged one, so its PRs target claude/* branches. `edited` is in
# the list because retargeting a PR (e.g. to main after its parent merges) fires
# `edited`, not `synchronize` — without it a retargeted PR would skip the check.
on:
pull_request:
branches: [main]
types: [opened, synchronize, reopened, edited]

permissions:
contents: read
Expand Down
18 changes: 13 additions & 5 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,19 @@ every project you point a loop at.

Every loop this repo produces or documents MUST carry all three hard stops:

1. **Max iteration count** (`--max-iterations`, `max_turns`, or a bash counter).
1. **Max iteration count** (a bash counter, `max_turns`, or `--max-turns` on
Claude Code v2.1.281+ — plus a per-call cap, since a counter between calls
can't stop a call that never returns).
2. **No-progress / stall detection** (bail if N iterations make no change).
3. **Token or dollar budget ceiling** (`max_budget_usd` or an external meter).
3. **Token or dollar budget ceiling** (`max_budget_usd` / `--max-budget-usd` or
an external meter).

A cost *alert* is not *enforcement* — Anthropic's billing layer has soft alerts
but does not auto-disable. The ceiling must live in the loop harness. Never
commit or recommend an uncapped loop. See `guardrails/`.
but does not auto-disable. The ceiling must live in the loop harness. And the
meter must not live inside the thing it meters: no stop or success check may
read state the agent can write (the `ralph-wiggum` plugin's iteration count, for
one, sits in a worktree file). Never commit or recommend an uncapped loop. See
`guardrails/`.

## Verification is part of the loop, not an afterthought

Expand All @@ -49,7 +55,9 @@ self-assessment of correctness.

## Knowledge base discipline

- `knowledge/00-primer.md` is the canonical briefing.
- `knowledge/00-primer.md` is the canonical briefing — a briefing, not a log,
with a **750-line budget** enforced in CI (`.github/workflows/kb-size.yml`).
Per-release detail, paper digests and backlog status belong in `sources.md`.
- `knowledge/sources.md` is the curated link list; every claim carries a source
and a confidence level.
- `knowledge/CHANGELOG.md` records dated updates. The `update-knowledge` skill
Expand Down
34 changes: 27 additions & 7 deletions guardrails/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,19 +7,39 @@ the ceiling has to live in your loop harness.

## The three hard stops

1. **Max iteration count** — bound the number of turns. Anthropic's Agent SDK
ships this as `max_turns`; `/ralph-loop` as `--max-iterations`; bash ralph as
a counter. Default cap: **20**.
1. **Max iteration count** — bound the number of iterations *and* the turns
inside each one. Anthropic's Agent SDK ships this as `max_turns`; the CLI as
`claude -p --max-turns N` (or `CLAUDE_CODE_MAX_TURNS`, which rejects a bad
value at startup instead of treating it as "no cap"); bash ralph as a
counter. Default cap: **20** iterations. **Require Claude Code v2.1.281+** —
before it, a turn could retry indefinitely while ignoring `--max-turns`
(changelog, Sep 23 2026). A bash counter alone would not have caught that: it
only ticks between calls, and a runaway call never returns — so also cap each
call's turns, dollars and wall-clock time from outside.
2. **No-progress / stall detection** — bail when the loop stops advancing
(no git diff, repeated identical actions, unchanged test state) for **3**
consecutive iterations; document blockers instead of looping.
(no worktree change, repeated identical actions, unchanged test state) for
**3** consecutive iterations; document blockers instead of looping. Measure
the worktree, not commits — empty commits are not progress.
3. **Token / dollar budget ceiling** — a hard enforcement stop. Agent SDK ships
`max_budget_usd`. In a bash loop, wire a real cost meter and check it
*before* the next paid call.
`max_budget_usd`; the CLI `--max-budget-usd` (since v2.1.217 it also halts
background subagents). In a bash loop, read spend from the CLI's own
`--output-format json` result (`total_cost_usd`) and check it *before* the
next paid call.

Plus, always: **verification inside the loop** — one deterministic check
(test/lint/typecheck) the agent runs itself, declaring done only on pass.

**The meter must not live inside the thing it meters.** Each stop, and the
success check, has to be enforced from state the agent cannot write. The
counterexample shipped in the official `ralph-wiggum` plugin: its iteration
count lives in `.claude/ralph-loop.local.md` inside the agent's worktree, and
the stop hook only enforces it while `max_iterations` is above 0 — so an agent
that writes `max_iterations: 0` has removed its own cap. Likewise a success
check that runs tests the agent can edit is forgeable. Keep counters in the
harness, read spend from the CLI's result rather than the agent's report, and
fingerprint the prompt, harness and tests so the loop stops if they change.
`templates/ralph/run.sh` implements all of this.

## Containment — the required companion (not a fourth hard stop)

The three hard stops bound what a loop **spends** and **how long it runs**. None
Expand Down
9 changes: 9 additions & 0 deletions guardrails/budget.env
Original file line number Diff line number Diff line change
Expand Up @@ -14,5 +14,14 @@ LOOP_MAX_BUDGET_USD=10
# Per-iteration timeout (seconds) for a single agent turn
LOOP_TURN_TIMEOUT=600

# Per-call caps, applied to every `claude -p` invocation inside a loop, so a
# single runaway call is bounded even though the loop counter can't see it.
# Requires Claude Code v2.1.281+ (earlier, --max-turns could be ignored).
LOOP_MAX_TURNS_PER_ITER=50 # claude -p --max-turns
LOOP_ITER_TIMEOUT=1800 # wall-clock seconds per call (timeout/gtimeout)
# Native equivalent of the per-call turn cap, for runs that don't pass the flag.
# Unlike a missing flag, an invalid value is rejected at startup, not "no cap".
CLAUDE_CODE_MAX_TURNS=50

# Retry budget for a transient failing step before escalating
LOOP_MAX_RETRIES=3
14 changes: 11 additions & 3 deletions guardrails/checklist.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ Run through this before starting any loop. The `loop-guardrails` skill automates
it. Don't start until every box is checked.

## Stop conditions
- [ ] **Iteration cap** set (`max_turns` / `--max-iterations` / bash counter).
- [ ] **No-progress detection** in place — bails after N no-diff passes and
records blockers (default N=3).
- [ ] **Iteration cap** set (`max_turns` / `--max-turns` / bash counter), on
**Claude Code v2.1.281+** (earlier versions could ignore `--max-turns`).
- [ ] **Each call is capped from outside**, not just the loop: per-call
`--max-turns`, `--max-budget-usd` and a wall-clock `timeout` — a counter
that only ticks between calls can't stop a call that never returns.
- [ ] **No-progress detection** in place — bails after N passes with no
worktree change and records blockers (default N=3).
- [ ] **Budget ceiling** set as a *hard enforcement* stop (not just an alert).
- [ ] **Stops live outside the agent's reach** — no counter, budget or success
check reads a file the agent can edit. On an unattended run, `/ralph-loop
--max-iterations` is not enough on its own (its count is in a worktree
file); prompt, harness and tests are fingerprinted or read-only.

## Containment (required companion to the stop conditions)
- [ ] **Network egress scoped** to the domains the loop needs, not full access
Expand Down
Loading
Loading