Validate Claude Code Platform Fable 5 License: MIT

Quickstart  ·  Usage  ·  Why Jeffy  ·  The receipts  ·  How a run works  ·  The rules  ·  White paper

Jeffy Loop is an autonomous improvement loop for Claude Code that works on your codebase the way a disciplined principal engineer would: audit first, fix one verified task at a time, prove every claim, and stop when the job is actually done.

Run /jeffy 10 and walk away. Jeffy maps your project's whole public surface, audits it breadth-first, and writes a backlog where every task carries a runnable acceptance check. Then it executes: one verified, checkpointed task per iteration, behind a verify gate that reverts anything that breaks your project. And "done" is never a feeling - a fresh audit must come back clean, an adversarial evaluator must countersign, and a plain shell script re-checks the whole claim before the run is allowed to end.

It has receipts. Widely used open-source libraries have been run to convergence, surfacing shipped, reproducible bugs - many hiding behind green test suites - and three greenfield builds converged from empty directories under external judges the loop could not edit, one of them against a deliberately mutated specification. The receipts are below.

New to autonomous agent loops, or want the full argument? The Jeffy Loop is a 33-page white paper written for readers with no prior knowledge of agents: how loops got here from ReAct to AutoGPT to the Ralph Loop, what Anthropic recommends and what that guidance leaves open, then every rule below explained from first principles - including an honest account of what this method still cannot do. It cites 28 sources, all linked. Download it here.

Jeffy Loop vs a raw prompt loop - the head-to-head

The head-to-head vs a raw prompt loop. Every row is a guarantee you can verify in the code: the engine is skills/jeffy/hooks/stop-hook.sh, the discipline is skills/jeffy/references/iteration-prompt.txt, and the receipts live under evals/.

Jeffy Loop descends from Geoffrey Huntley's Ralph technique - the insight that a coding agent re-fed one prompt in a loop compounds into real work. The head-to-head above is engine versus method: the raw loop is the engine pattern Jeffy is built on, and Jeffy is the engineering method wrapped around it. The method distills what the people running loops at scale have published - Anthropic's Claude Code best practices and Boris Cherny's public workflow: give the agent a check it can run, one task at a time, promote every hard-won lesson into a file the next run reads, and prefer small fresh-context runs over one long one.

Quickstart

You need exactly two things. The installer handles everything else, including jq.

  1. Claude Code - installed and signed in once
  2. git - confirm with git --version

No git yet? One command for your platform:

Windows

winget install Git.Git

macOS

brew install git

Debian/Ubuntu

sudo apt-get install git

Install Jeffy:

git clone https://github.com/lenamonj/jeffy-loop.git
cd jeffy-loop
./install.sh        # Windows PowerShell: .\install.ps1

[!NOTE] If PowerShell blocks the installer with "running scripts is disabled on this system", run it once with powershell -ExecutionPolicy Bypass -File .\install.ps1 - the bypass applies to that single invocation only.

The installer verifies the Claude Code CLI and jq (offering to install it via winget, Homebrew, or apt), copies the /jeffy and /cancel-jeffy skills - engine included - to ~/.claude/skills, and registers the loop's hook in ~/.claude/settings.json. Every step prints an [OK] or the exact fix.

  • Re-run - always safe: it skips what is installed, upgrades in place, and never duplicates the hook registration.
  • Update - see Already installed? Upgrade below.
  • Uninstall - delete ~/.claude/skills/jeffy and ~/.claude/skills/cancel-jeffy, and remove the hook entry from ~/.claude/settings.json.

Then open Claude Code in the project you want to improve and type /jeffy 10.

[!TIP] /jeffy is a slash command inside the Claude Code session, not a shell command.

When that run ends, close the session and start a new one to run it again. That restart is doing real work, and why is worth two minutes.

Already installed? Upgrade

Upgrading touches ~/.claude and nothing else. No project you have run against is modified. Pull and re-install:

cd jeffy-loop && git pull
./install.sh        # Windows PowerShell: .\install.ps1

Then start a new Claude Code session - skills and hook registrations are read at session start, so an open session keeps running the old engine until you restart it. That is the whole upgrade. /jeffy names the engine version on its first line, so you will see the new one immediately.

Deleted the clone since installing? Clone it again and run the installer from there; it only ever reads from the clone.

[!NOTE] Do not upgrade underneath a live run. The Stop hook is read from disk every time it fires, so replacing it mid-run changes that run's rules halfway through. Let the run finish, or stop it with /cancel-jeffy first.

[!NOTE] The installer copies over the top and never deletes, so a file removed in a later version stays behind. For a clean slate, delete ~/.claude/skills/jeffy and ~/.claude/skills/cancel-jeffy before re-running it - the installer puts both back.

Usage

/jeffy [N] [focus...]
  • N - iteration budget, default 10. Sizing is low-stakes in both directions: the loop ends itself at convergence, so unused budget costs nothing, and a budget that runs dry loses no work - the next /jeffy picks up where the run stopped. The floor for converging in one run is the opening audit, one iteration per expected finding, and a closing audit; when that arithmetic outgrows the default, prefer a second run over a bigger number (see Good to know).
  • focus - optional directive for the run, e.g. /jeffy 8 test coverage and error handling.
/jeffy                                     # 10 iterations, full-spectrum improvement
/jeffy 5                                   # 5 iterations
/jeffy 12 accessibility and performance    # 12 iterations with a focus directive

[!NOTE] Enhance mode (/jeffy [N] enhance <topic>, shipped in v1.6.0) was removed in v1.11.0: it was a second product surface no published receipt ever used, and the defect loop is the product. The launcher refuses the keyword, and it refuses a standard launch over an Enhance-mode PLAN.md left behind by an earlier version - archive those state files (commit and delete them, or keep them on a branch) and relaunch, or run v1.10.0, the last release that carries the mode.

Scoped mode. By default /jeffy runs in Improvement mode: an open-ended audit-and-fix loop. To run it against a concrete target instead, edit PLAN.md - replace the Goal and Definition of done with the target, seed BACKLOG.md with the finite tasks, then run /jeffy. Everything else (envelope, verify gate, checkpoints, journal, report) behaves the same.

Cancel. Run /cancel-jeffy. It reports which loop it found, deletes the loop state file, and leaves PLAN.md, BACKLOG.md, and JOURNAL.md untouched, so the next /jeffy picks up exactly where it left off. (Equivalent manual action: delete .claude/jeffy-loop.local.md at the project root.)

Good to know

  • One loop per project at a time. A crashed session can leave a stale state file behind; the skill detects it at launch and asks before cleaning up.
  • You can talk to the session mid-run. Your message gets answered, then the loop resumes on its own. The turn counts against the budget.
  • Permission prompts pause the loop. For unattended runs, allowlist your test and file tools or use acceptEdits mode. Never allowlist push or force operations for a loop.
  • Budget counts turns, and a single turn is unbounded in time and cost. Keep N small on a first run and watch it. Check spend anytime with /cost.
  • Prefer several small runs over one big one. Context accumulates across iterations within a run, and the state files carry everything between runs, so two runs of 5 beat one run of 10 - but the clean context only arrives with a new session. Close the session and start a fresh one in the same directory; nothing is lost.
  • Edit PLAN.md or BACKLOG.md between iterations, not mid-iteration. The Proposed section is the designed channel for decisions.
  • A .jeffy/ directory appears at the root of a project the loop has swept. It holds the known-answer probe batteries a later sweep re-runs instead of rebuilding, and from 1.7.0 the evaluator artifacts under .jeffy/evaluator/ - one per gate invocation, naming the commands the adversarial gate actually ran and what they exited. The checkpoints commit all of it on purpose - loop memory, exactly like the three state files. Only the transient loop state file is gitignored.

[!IMPORTANT] Trust model. The entire engine is one auditable shell script in this repo (skills/jeffy/hooks/stop-hook.sh), registered as a Claude Code Stop hook. It fires at turn end but exits instantly unless the current project has a live Jeffy state file naming that session - zero cost and zero behavior outside a run. The installer's only writes outside this repo are the two skill folders it copies into ~/.claude/skills (engine included), that one hook registration in ~/.claude/settings.json, and - only when jq is missing and you answer yes to its prompt - a jq install through your system package manager (winget, Homebrew, or apt).

Why Jeffy

  • An engineer's judgment, not a linter's. Every run starts from a real audit - architecture, correctness, security, testing, error handling, performance, accessibility, developer experience, and more - and every finding becomes a prioritized task with a concrete acceptance check. Evidence over assertion: a finding exists only if the loop can point at it.

  • It cannot wreck your repo. Every iteration ends in a local checkpoint commit, and a repo-level verify gate reverts any iteration that breaks the project on the spot. Nothing is ever pushed and no branches are created, so you review with git log, revert any single iteration, and squash the run when you are happy.

  • It doesn't invent problems. Severity is judged against a declared operating envelope - your project's real input surfaces, not imagined attackers - so out-of-envelope findings cannot inflate the backlog. Only you can widen the envelope: the loop files a proposal and moves on.

  • Done means no High, no Medium, and every Low on the record. A declaration needs a fresh full audit finding zero High and zero Medium, with any open Low carried, named in the closing entry, and published beside the receipt rather than quietly dropped. A surface inventory bounds the claim, so "no findings" can never mean "nowhere looked".

  • Convergence needs a second signature. An agent grading its own work praises it, so before the loop may claim anything an adversarial evaluator - a fresh-context sub-agent carrying none of the run's self-persuasion - re-runs the verify gate and every closed task's acceptance check, hunts for what was missed, and must return PASS. Each verdict is written to its own committed artifact naming every command it ran and that command's real exit status.

  • The stop is machine-checked, and the machine is tested. The Stop hook is plain shell, not a model, and it refuses a converged stop that fails any of its conditions, re-feeding the evidence to the loop instead of letting the claim through. The engine itself is held to 216 behavioural checks on each CI leg, Linux, Windows, and macOS, with a shellcheck lint pass riding the Linux leg on top.

  • It has a self-learning mechanism, and it is pointed at itself. A rule learned the hard way binds every iteration after it, and a rule that has to be written a second time is proposed for promotion into a mechanism, on the reasoning that a rule needing to be written twice is a rule the text is not enforcing. That is not an intention: it has happened on real targets whose published journals carry the loop's own words for it, and it is how a defect met in a stranger's repository becomes this engine's next version.

  • It stops on purpose, and it shows its work. Budget spent, convergence reached, progress stalled, or a decision only you can make - the loop ends itself and says which, instead of burning budget spinning. The run report lists iterations used, tasks closed with severities, the diffstat, anything blocked, and decisions waiting on you, over an append-only journal and checkpoint commits that hold the full greppable record.

Each of these is stated as an enforced condition in the rules a run lives by, and the mechanism behind the seventh is in the loop improves the loop.

Every converged public target, by the language it was written in. Counts are derived from the receipts table below at render time by scripts/render-language-pie.py, and the alt text above is derived from the same table by the repo validator, so neither the chart nor its description can disagree with it. Both read the slices largest first, ties alphabetical. Chart source: media/language-pie.html.

External Validation: Public Open-Source Projects

Testing a tool against its own codebase proves little, so Jeffy was run against widely-used open-source projects with no connection to this repository. 34 of those runs converged, across 12 languages. That breadth is evidence of something specific rather than decoration: the engine ships no language-specific analyzer, no ruleset, and no per-ecosystem plugin. It works from what a project already has, its own test suite and its own verify command, so what carries from a Rust CLI to a Ruby linter to a C++ parser is the method itself.

Every run used a local clone, and nothing went upstream without a filed issue or PR. Each was held to the rules current at its date, and those rules tightened as the engine matured - earliest runs converged on a clean closing audit and an empty backlog, later ones under the shell-enforced stop, the most recent under the adversarial evaluator's countersignature. They tightened on every axis but one, and the record says so plainly: from v1.9.0 a declaration no longer waits on Low-severity findings, which are published with the receipt instead, while severity scoring came under the evaluator's independent re-score at the same moment. Of the 34, 29 converged under the adversarial evaluator's countersignature, 1 recorded the evaluator as unavailable and says so, and 4 predate the gate entirely. Every receipt names the standard its run met, so none of this requires taking our word for it.

A method that always converges is not measuring anything, so the record shows what the standard costs when a project resists it. python-dotenv was published here as not converged through four runs and 25 findings, and needed eight runs and 73 iterations before an audit finally filed nothing. sqlparse makes the point under a stricter rule: its budget of five runs was fixed in writing before its first iteration, four runs failed, and it converged on the fifth and last - one more rejection and it would have been published as a non-convergence, because the rule said so in advance. One row is not a loop run at all. PapaParse is an audit under the same method, kept in the table rather than hidden, and its conversion waits on four open upstream PRs.

Ordered by severity of findings, most severe first.

Project Stars Language Iters Upstream Headline
quantstats 7,489 Python 40 issue filed 29 findings behind 125 green tests; the library ended smaller than it started
fasthttp 23,422 Go 58 FIX MERGED 31 findings in a tagged release; a Content-Length no parser should accept became a wrong number
swift-algorithms 6,323 Swift 24 - the twelfth language: one real High in Apple's code, then fourteen findings tracing back to a single cause - nothing compiled or ran the doc-comment examples, so ten of them did not compile. Run 2 refused a loophole that would have earned it a third evaluator invocation
validator 20,110 Go 31 - four Highs behind a green suite in the struct validator under Gin and Echo: a cyclic struct graph exhausted the goroutine stack and killed the process unrecoverably, and the unix_addr tag accepted every string, so a validation its own docs describe never rejected anything
cJSON 12,916 C 10 - the eleventh language, and the first target picked by shape rather than by oracle: a pre-registered two-run budget, converged in one. Sorting an object silently dropped every later append. The gate rejected a leaking test the project's own suite could not see
magic_enum 6,165 C++ 19 - six broken public members of a header-only library that no compiler had ever seen - C++ does not compile a class template member nothing instantiates, and nothing did. One of them answered all() == false on a full bitset without crashing. The structural fix makes the suite instantiate them, so a seventh cannot ship
clap 16,634 Rust 31 - the target this cohort predicted would fail, named at risk in writing before launch: 35 inventory rows, and after 30 iterations it had swept 20 of them with the gate never once invoked - then run 4 swept the remaining 15 and passed first time, while the second-smallest surface in the same cohort did not converge at all
go-cmp 4,672 Go 15 - the smallest surface in the corpus at 620KB, from the same wave whose 4-file target did not converge: Google's comparison library was gated by CI on Go 1.21 alone, and two genuine grouping bugs in its diff reporter hid from a 4,000-case suite. The whole shipped diff is +31/-13 - on a well-kept codebase the loop's bill is small and precise
zod 43,471 TypeScript 39 - the largest surface in its cohort at 196 files converged while the 4-file target in the same wave did not: catch and success wrappers absorbed the engine's own stack overflow so a cyclic value validated, and z.success() carried an unreachable false branch. Three gate REJECTs before the PASS, and the stack-overflow class took three attempts before the probes could not break it
FluentValidation 9,753 C# 8 - one run of eight iterations, the budget's first and last: CreditCard() accepted " - - " as a card number - the Luhn checksum of no digits is zero - and any unsupported neutral culture turned every validation message into the empty string, both behind 865 green tests. Converged with an empty ledger; a 907-culture differential proved the fallback fix moved nothing else
commander.js 28,358 JavaScript 10 - the most-used CLI framework in Node, and the cleanest target in the corpus: 1,373 green tests, no High findings, and an error message naming an argument the user never typed. The gate rejected on the last iteration and the fix landed inside the one-transaction rule
claude-code-action (attempt 2) 8,618 TypeScript 30 - the acceptance test for the engine's own release, and the sharpest limit in the corpus: attempt 1 swept 17 of 23 rows and never once reached the gate, attempt 2 swept 28 of 28 and converged - and did not rediscover three High findings attempt 1 had filed on identical code
claude-agent-sdk-python (attempt 2) 7,881 Python 30 - the same limit, proved by an instrument instead of a diff: attempt 1 swept 19 of 44 rows and never reached the gate, attempt 2 swept 31 of 31 and converged - then attempt 1's own battery, run unmodified against the converged tree, exited 1 with create_sdk_mcp_server published an empty schema. The run had swept that row and edited that very function
path-to-regexp 8,598 TypeScript 27 - three runs and five evaluator invocations, four of them REJECTs - and not one rejection was a missed defect in the library. Every one was a defect in the run's own evidence, including a verify command whose ReDoS assertions were randomized enough to pass without searching
rust-url 1,570 Rust 30 PR open 20 findings, 10 High, in the URL crate under cargo and reqwest; a class was settled and withdrawn three times before it held, and the PR closes a conformance case open upstream since 2023
PHP-Parser 17,450 PHP 29 PR open the tenth language; told nothing, it found its project's largest oracle by itself, then proved one of the suite's own test classes passes without executing the code it names
rrule 3,738 TypeScript 33 - 23 findings, 10 High, in the RFC 5545 library behind much of the JavaScript calendar ecosystem; the reference implementation overruled one of the loop's own High findings and the loop withdrew it
go-yaml 2,217 Go 29 PR open (not a loop finding) 20 findings, 6 High, including a regression the run introduced and the gate caught; the vendored conformance corpus never ran, and scores identically before and after
records 7,220 Python 7 issue filed four High data-loss bugs behind a green suite
PyPortfolioOpt 5,905 Python 58 PR open CI-red baseline to 356 passing; the evaluator rejected five convergence attempts
dayjs 48,657 JavaScript 74 PR open 45 findings, 10 High, in a 63M-downloads-a-week library
mustache.js 16,725 JavaScript 11 issue filed revived a suite that could not start; npm audit 107 to 2
ta 5,129 Python 64 - wrong numbers shipped since 2023; caught its own regression and wrote "It is mine"
bat 59,915 Rust 10 FIX MERGED a just-merged security flag did nothing when piped; caught before it ever shipped
jsoncpp 8,876 C++ 10 PR open the documented secure-memory build never compiled on MSVC; the evaluator caught the fix being half done
yfinance 24,837 Python 9 PR open closed a High that upstream's own failing test was advertising
speedtest-cli 14,080 Python 5 - the restraint case: small findings, nothing invented
chalk 23,288 JavaScript 8 FIXED UPSTREAM the control: one Medium found - the maintainer wrote and merged the fix himself, shipped in chalk v6.0.0
Spectre.Console 11,567 C# 8 issue filed a panel header wider than its content was dropped, not truncated - invisible to 3,618 tests
cobra 44,436 Go 32 - the CLI framework under kubectl and Hugo, converged with an empty ledger and no High at all: SOURCE_DATE_EPOCH was resolved in the host timezone, so the one variable whose purpose is byte-reproducible output produced different bytes per machine
gson 24,229 Java 2 - the fastest run: one audit, one gate, one priced-and-declined Low, not a line changed
RuboCop 12,892 Ruby 7 - the null result: every cop department swept, the last 20 commits re-proven, zero findings, zero lines changed
python-dotenv 8,830 Python 73 PR open the grind: 8 runs, 48 findings, seven audits that each filed something before the eighth came back empty; suite 220 to 511
sqlparse 4,009 Python 47 - the pre-registered budget was five runs and it took five; the loop declined a finding the gate handed it, after running the claim
PapaParse 13,532 JavaScript audit 4 PRs open four Highs in the streaming path; conversion waits on four open PRs

Disclosure is deliberate and selective. Filing a machine-generated issue costs a maintainer real attention, so these findings go upstream only where the defect is severe and the project takes outside contributions; where nothing was filed, the receipt says so and why.

What is not in the table above is in evals/ATTEMPTS.md: every target ever started, the runs each one cost, which of three convergence standards it met, and the one public target that was abandoned without a receipt. Convergence here is per-run and a blocked run gets relaunched, so run counts are published with the wins attached rather than a bare success rate. Targets from here on carry a pre-registered run budget committed before their first iteration.

Greenfield: three builds judged by suites the loop did not write

Every receipt above is brownfield - the project arrived with a test suite the loop did not author. The white paper's own limits section names the residual weakness anyway: the loop writes many of the tests that certify the loop. Greenfield is that weakness at its maximum, so the answer was pre-registered: start from an empty directory, commit the goal and a Verify command naming an external judge before iteration 1, ship the backlog empty so the task decomposition is the loop's own work, and never intervene in a run. The engine is unmodified. All three targets converged.

Target Judge Final position Rows swept Iterations Runs
TOML 1.0 decoder, Rust toml-test v2.2.0 - 679 external assertions 205 of 205 valid, 474 of 474 invalid 17 of 17 11 1
gitignore matcher, Rust git check-ignore 2.50.1, differential 106 cases, 300 queries, 0 disagreements 12 of 12 42 5
TOML-M decoder, Rust - mutated spec toml-test v2.2.0 through a frozen output adapter 205 of 205 valid, 474 of 474 invalid (and 169 of 205 against standard TOML) 17 of 17 14 1

The TOML decoder's zero measurement was the suite failing because the binary did not exist; eleven iterations later every one of 679 externally authored assertions passed, and even the surface-inventory rows were the suite's own test groups rather than the loop's choice. The gitignore matcher is the stopping-discipline story: its frozen corpus was fully green from the first iteration of run 2, and everything after that was the adversarial evaluator probing beyond the corpus and refusing to countersign - invoked 8 times, rejecting 7, filing findings that marched from matcher semantics into wildmatch.c's escaped-slash clause, git's four-byte isspace, NTFS case folding, 8.3 short-name aliases, and the Win32 normalization layer git's own file opens bypass. Three runs ended blocked and are published as the receipts they are; once, the loop reproduced two of its gate's three rejection reasons and refuted the third with direct oracle evidence, filing exactly what reproduced.

The third target answers the objection the first two invite: TOML and gitignore saturate any plausible training set, so convergence there might measure recall. TOML-M is TOML v1.0.0 with two rules deliberately inverted - true and false swap payloads, and tab and line feed swap inside string values - so remembering the real format produces wrong answers. Both amendments are involutions, which lets the unmodified toml-test binary stay the judge through a frozen 61-line adapter that inverts the decoder's output. The receipt reports two numbers from one binary: 205 of 205 against the mutated suite, and 169 of 205 against standard TOML, failing on exactly the cases the mutation touches. It built the dialect rather than recalling the format, at a cost of 14 iterations against 11 for the unmutated build. That receipt also discloses a rule violation on its own front page: an audit ran inside the closing extension window, which the engine forbids, and the convergence declaration cites it.

Stated as narrowly as the result deserves: the same engine, unmodified, converged on builds whose completeness was decided by judges it did not write - including one where recalling the real specification actively hurts. Not claimed: any completion rate (three chosen targets are not a sample), or invention (a two-rule dialect is not a novel format). The gitignore corpus is self-authored - frozen at 53 cases, grown monotonically to 106, never shrunk, and since scored cold against 119 blind-authored queries with no disagreements - and the white paper weighs that honestly against the TOML target's fully external 679. All three repositories ship their complete run record - pre-registration, journal, backlog, every iteration commit - because for a greenfield build the process is the evidence.

How a run works

How one command becomes a run. Solid arrows are control flow; dashed arrows are the file reads and writes that steer it. Outside a run the Stop hook exits instantly - no live state file, no behavior - and /cancel-jeffy ends a run at any time. Diagram source: media/flowchart.mmd.

Running /jeffy in a Claude Code session:

  1. Bootstraps the loop's memory at the project root: PLAN.md (goal, operating envelope, surface inventory, verify command, lessons, definition of done), BACKLOG.md (the task ledger - findings prioritized most severe first, plus proposals awaiting your decision, settled defect classes, and the Converged record), and JOURNAL.md (append-only iteration log). They persist between runs.
  2. Runs the budgeted loop. The first audit fills the surface inventory and the backlog. Each iteration after that either audits or executes exactly one task, verifies it, and checkpoints it; a task that newly breaks the verify command is reverted. Once one full audit comes back clean of High and Medium, the run stops auditing and finishes the ledger.
  3. Stops for a reason and reports. Convergence - a clean audit, an empty ledger, a fully swept inventory, the adversarial evaluator's PASS, all re-checked in shell by the Stop hook - or the budget, a stall, a hard blocker, or your cancel. The run report lists tasks closed with severities, the diffstat, rows swept of rows total, and anything waiting on your decision.

Use several short runs, not one long one

A budget is a ceiling, not a target, and the practice that produced every receipt here is the same one worth copying: run /jeffy 10, let it finish, close the session, then open a new one and run /jeffy 10 again. The receipts that took 40 or 58 or 74 iterations got there as four to eight budgeted runs, never as one enormous budget.

That is not superstition about round numbers. The loop is built to start each iteration from written state - PLAN.md, BACKLOG.md and the last few journal entries - precisely because a fresh reading of the record is more reliable than a long conversation's memory of it. Inside a single session, though, that conversation keeps growing: every audit, every diff, every command output stays in context, and the loop ends up reasoning over its own accumulated transcript instead of the files it was designed to reason over. A new session throws that away and forces it to re-read what it actually wrote. The receipts bear the cost of skipping this out: in the python-dotenv run, later runs kept filing Highs and Mediums on surface that earlier runs had already swept and scored clean.

The restart also gives you the natural review point. Between runs the tree is committed, the report is written, and the handoff names what comes next, so it costs nothing to read the journal, answer anything filed under Proposed, and decide whether to keep going. And because every journal entry is stamped with the run that produced it, a bug introduced in run 3 stays attributable to run 3 rather than dissolving into one undifferentiated log.

Nothing breaks if you hand it a bigger budget. The state files persist, the ratchet skips a re-audit on an unchanged tree, and a fresh run picks up exactly where the last one stopped. Short runs are simply how you get the loop's design working for you instead of against it.

Jeffy built this repository by running on itself, and that convergence is re-earned, not archived - every fresh run has to reach it again with fresh evidence. It last did on 2026-07-31, the day after v1.5.0 shipped, running Fable 5 at x-high effort: the opening audit filed three Mediums against the repo's own trust-model and check-count claims, one iteration each fixed them, and the stop still had to be earned. The dev journal stays out of the published tree, since state files are the loop's memory rather than the product, but the closing sequence, abridged, shows the texture of a converged stop:

## iter 5/8 | e64f9b2c-160059 | 2026-07-31 | AUDIT | audit

Verification: bash scripts/validate.sh exit 0 fresh this iteration,
119 OK, 0 FAIL, 96s wall against the hook's 240s verify budget.
Inventory: all 12 rows stand. Dimension scores over all 12 swept rows,
fresh eviden