These are the canonical template files installed into target projects. They live in the rauf tool’s repo under artifacts/variants/backlog-json/ and are embedded into the compiled binary.
File Inventory
artifacts/variants/backlog-json/
├── CLAUDE_ADDON.md # Block to merge into existing CLAUDE.md
├── CLAUDE_GREENFIELD.md.tmpl # Full CLAUDE.md template for new projects
├── backlog.schema.json # JSON Schema for backlog.json
└── progress.md # Empty progress template
.gitignore Entries
During install() and update(), the installer appends rauf runtime entries to the target project’s .gitignore (idempotently, never duplicated). The set mirrors RUNTIME_EXCLUDE_PATHSPECS in packages/loop/src/git-commit.ts:
**/.rauf/.loop.lock
**/.rauf/state.json
**/.rauf/DONE
**/.rauf/CANCEL
**/.rauf/iteration-status.json
**/.rauf/rauf.log
**/backlog.json.bak
These cover the root .rauf/ directory as well as nested backlog dirs (specs/<feature>/.rauf/). The intentionally tracked files (backlog.json, progress.md, RAUF.md, REVIEW.md, archive/) are not listed.
Already tracking a runtime file? If a runtime file was committed before the .gitignore was in place (common for projects installed before rauf’s .gitignore deployment), untrack it once. The most commonly tracked files are state.json (written on every loop run) and .loop.lock (written while a loop is active):
git rm --cached .rauf/state.json
git rm --cached .rauf/.loop.lock
Run git status to see which other runtime files are tracked and repeat as needed. The rauf install/rauf update warning lists all of them. After untracking, git will ignore these files on future loop runs.
Loop Runner
The autonomous loop is implemented in packages/loop as a TypeScript LoopRunner class, replacing the legacy shell scripts. The loop is started via:
rauf loop run --detached: server mode (via LoopManager)
rauf loop run: direct mode (in-process, no server)
Design Principle: Loop Runner Owns Status
The loop runner manages ALL backlog status transitions. Claude does NOT modify backlog.json. This is the design decision that enables safe concurrent access: the manager tool can add items to backlog.json while the loop runs, because the loop uses core’s updateItem() for atomic status transitions.
Critical Requirements
Atomic writes for status management: The loop runner selects items, marks them in_progress, runs Claude, then marks them done/blocked based on the exit signal. Each write goes through core’s updateItem(), which uses atomic write (write .tmp → rename) with .bak backup.
state.json writes: Write .rauf/state.json on every major state change via writeLoopState().
Dependency-aware item selection:selectNextItem() returns the highest-priority pending item where all items in dependsOn have status done. Returns null if no eligible items.
Focused prompt: Claude receives RAUF.md + the specific item JSON + full backlog as read-only context. The prompt explicitly states: “Do NOT modify .rauf/backlog.json or .rauf/state.json.”
Exit signal detection: Parse Claude’s stdout for:
RAUF_DONE → mark item done, commit changes
RAUF_BLOCKED:reason → mark item blocked, continue to next
RAUF_REVIEW:{"items":[...],"summary":"..."} → review found issues, runner creates fix items
No signal → not auto-blocked; outcome classified by exit context (clean / non-zero / timeout / usage-limit), already-committed work reconciled (see the Signal placement note below)
Claude exits non-zero with usage limit message in stderr → see Usage Limit Handling below
Signal placement (canon §4.5): the runner scans backwards from the end
of Claude’s stdout and uses the last signal line, so trailing summaries or
commit text after the signal do not break detection (signal-parser.ts:27-69).
A signal must be the whole trimmed line. No signal is not auto-blocked: the
outcome is classified by exit context (clean / non-zero / timeout / usage-limit)
and already-committed work is reconciled (runner.ts:677-705).
Crash cleanup:try/finally resets any in_progress item back to pending so it’s not left stranded.
Git commit: After RAUF_DONE, the loop commits with [rauf] <id>: <title>.
DONE file + rauf.log: Written on all terminal exit paths for status derivation.
Model Resolution
The loop runner resolves which model to use at each iteration. Resolution priority (highest to lowest), per CANON §4.6:
1. item.model — the selected backlog item's `model` field (per-task override)
2. --model / options — per-run override (CLI `--model`, or run options)
3. project default — the project's configured default model (MarkerOptions.model)
4. provider default — none set → no `--model` passed; provider/CLI uses its default
Implementation: item.model ?? options.model ?? projectModel (runner.ts:493-494); the --model flag is only passed when set (claude-process.ts:78-79), so an unset cascade falls through to the provider default.
Model IDs follow Anthropic conventions: claude-opus-4-6, claude-sonnet-4-6, claude-haiku-4-5-20251001.
Auto-Sweep
The loop runner checks .rauf.json for options.autoSweep on startup (before the main loop). If true, it calls core’s sweepBacklog() (optionally with sweepMinAgeDays from marker options) before the first iteration. This keeps the active backlog clean automatically.
Auto-sweep behavior:
- Triggered by: options.autoSweep = true in .rauf.json
- Optional age filter: options.sweepMinAgeDays (integer, default 0 = sweep all done)
- Failure is NON-FATAL — loop continues regardless of sweep result
- .rauf/archive/ files are NOT auto-gitignored — users may add to .gitignore if preferred
MarkerOptions fields:
autoSweep?: boolean: default false
sweepMinAgeDays?: number: default 0 (sweep all done items)
Session Timeout
Claude sessions can stall indefinitely: the process stays alive but stops making progress. The loop runner wraps the claude -p invocation with a configurable timeout.
Session timeout behavior:
- Default: 60 minutes per Claude session
- Configurable via: sessionTimeoutMinutes in LoopStartOptions or options.sessionTimeout in .rauf.json
- Uses SIGTERM first, then SIGKILL after 30s grace period
- Timeout triggers the retry flow (same as no-signal)
When a timeout fires:
1. Claude receives SIGTERM (graceful shutdown)
2. If still alive after 30s, receives SIGKILL
3. Item retry count is incremented
4. If retries < maxRetries: reset to pending, retry next iteration
5. If retries >= maxRetries: mark as blocked with reason
MarkerOptions field:
sessionTimeout?: number: default 60 (minutes). Must be a positive integer.
Graceful Cancel
The loop runner supports graceful cancellation via both AbortController (programmatic) and .rauf/CANCEL signal file:
User-customizable: if .rauf/REVIEW.md exists locally, it’s used instead of the embedded template
Expected outputs:RAUF_DONE (clean, no issues) or RAUF_REVIEW:{json} (issues found, JSON matches ReviewPayload schema)
Installed during:install() and re-rendered during update(), removed during uninstall()
Usage Limit Handling
When claude -p exits non-zero with a usage limit message in stderr (matching “usage limit”, “rate limit”, “Claude AI Usage Limit”, or “too many requests”), the loop:
1. Reads OAuth credentials from ~/.config/claude-code/credentials.json
2. Queries GET https://api.anthropic.com/api/oauth/usage to determine limit type
3. Resets current item back to "pending" so it is retried after recovery
Contains two sections: managed (tool-updated) and user-customizable.
# Rauf — Per-Iteration Instructions
<!-- rauf:managed:start -->
## Verification Commands
Before marking any task as complete, run the full verification pipeline:
{{verifyCommand}}
Individual commands:
- Test: `{{testCommand}}`
- Typecheck: `{{typecheckCommand}}`
- Lint: `{{lintCommand}}`
- Build: `{{buildCommand}}`
- Format: `{{formatCommand}}`
If any command is not configured (empty), skip it.
Do NOT background, `nohup`, or `&`-detach the verification command (or
any command inside it). Run it in the foreground and wait for it to
exit before reading its result or emitting your exit signal — a
backgrounded verify command exits immediately with no output, which
looks like a clean run and will be misread (or produce no signal at
all, wasting a retry).
<!-- rauf:managed:end -->
## Workflow
1. You are one iteration of an autonomous coding loop
2. Read the backlog — find the current `in_progress` item
3. Read the item's `acceptanceCriteria` — each must pass
4. Read `progress.md` for context from previous iterations
5. Implement the task
6. Run verification: `{{verifyCommand}}` (run it in the foreground and wait — do not background it)
7. Leave your changes in the working tree — do NOT commit. The iteration agent never commits or stages; the loop runner owns the commit (it commits as `[rauf] <id>: <title>` after you signal `RAUF_DONE`).
8. Output your exit signal on a line by itself, as your final line:
- `RAUF_DONE` — all criteria met, verification passes