Quick Install · Usage · How it works · The receipts · Headless · White paper · STE100 description
A Claude Code loop that finds bugs in your repo, fixes them, and proves every fix before it commits. Maintainers have merged 69 of its patches.
The model writes the patch. The harness around it decides whether that patch can be trusted. Jeffy Loop is a verification harness for Claude Code: type /jeffy and it audits your codebase, finds a problem, fixes it, checks the fix, and commits the result locally, one task at a time. It keeps going until the codebase passes a fresh audit.
The harness is deliberately hard to fool. If a fix breaks your tests, it is undone. Nothing is ever pushed. A standard run counts as complete only when a fresh audit is clean, your tests pass again, and an adversarial review by a second AI agent with no part in the run signs off. The goal is fixes that survive scrutiny.
There is a real-world test for that. Maintainers with no stake in this project have merged 69 of its pull requests, each filed from a local clone, into projects run by NVIDIA, Arm, Meta, Tesla, Google, Apple, Microsoft, Netflix, Apache, Palo Alto Networks, Oracle, IBM, HashiCorp, Cisco, Uber, Square, Cloudflare, and more. See them all.
The record
Jeffy was run against 132 open-source projects with no connection to this repository, each judged by its own test suite. Every run is published, failures included.
| Projects tested | Converged | Failed | PRs merged | PRs open | Issues filed |
|---|---|---|---|---|---|
| 132 | 103 | 28 | 69 | 17 | 4 |
Converged: the closing audit came back clean and the loop's adversarial evaluator, a fresh-context sub-agent, countersigned it, a standard this repository set and checks itself. That happened in 103 projects across 13 languages with no language-specific analyzer. Failed: the run spent the budget declared before it started without converging, or, for libuv, was abandoned before it had one. That leaves PapaParse, an audit held to the same method rather than a loop run, which the receipts page counts as Fixed alongside the 103.
See every project, every patch, and every failure
Quick Install
You need Claude Code, signed in once, and git. Each installer checks for everything else and asks before installing jq.
Install Jeffy with pip
Step 1. Install the package.
pip install jeffy-loop
Step 2. Install Jeffy as a Claude Code skill.
jeffy install
Install Jeffy with uv
You need uv. If you do not have it, pip install uv gets it.
Step 1. Install the package.
uv tool install jeffy-loop
Step 2. Install Jeffy as a Claude Code skill.
jeffy install
Install Jeffy by cloning the repo locally
Mac and Linux
git clone https://github.com/lenamonj/jeffy-loop.git
cd jeffy-loop
./install.sh
Windows PowerShell
git clone https://github.com/lenamonj/jeffy-loop.git
cd jeffy-loop
powershell -ExecutionPolicy Bypass -File .\install.ps1
The -ExecutionPolicy Bypass form runs the installer on a machine where PowerShell scripts are disabled by default and changes no policy.
Running Jeffy
Open Claude Code in the project you want to improve and type /jeffy 10 into the session.
Start a new session for each run, so each run reads its state files with a clean context.
A High hunt fixes only the Highs and stops at the first audit that finds none, so it is usually the faster run.
Usage covers every flag.
What the engine enforces
Each rule is enforced by the iteration prompt, the state files or the Stop hook, and each can be checked in this repository. How.
| Rule | What it means |
|---|---|
| A finding needs proof | The loop must point at the defect and prove it with a check that runs. |
| Three gates decide done | A fresh audit with no High or Medium findings, an adversarial evaluator's countersignature, and a shell re-run of your tests. In one greenfield build the evaluator was invoked 8 times and rejected 7. A High hunt skips the evaluator and never claims convergence. |
| No convergence over unswept surface | The Stop hook refuses to converge while any row of the public-surface checklist is unswept, and a row reopens when its code changes. |
| Lessons become checks | A rule learned once binds every later iteration. The engine passes at least 477 behavioural checks on each of Linux, Windows and macOS. How the loop improves itself. |
Trust model
| Question | Answer |
|---|---|
| What runs | One Stop hook, skills/jeffy/hooks/stop-hook.sh, plus the small library beside it in skills/jeffy/hooks/lib/. With no live Jeffy state file it exits at once and does nothing. |
| What it can touch | The loop acts through your Claude Code session with that session's permissions. Its no-push rule lives in the iteration prompt, so never allowlist push or force operations for it. Usage, Blast radius. |
| What the installer writes | Two skill folders under ~/.claude/skills and one hook entry in ~/.claude/settings.json. Upgrading and removing them. |
| How to stop it | /cancel-jeffy ends a run at any time. |
Independent Validation - The Receipts
Each finding below was accepted upstream by the project's own maintainers.
| Merged by | Finding |
|---|---|
| go-nvml #207 - merged in 12 days The buffer handed to dlinfo for a library's directory was allocated with zero bytes, so the first Path() on a library opened by soname wrote past it and the next dlclose crashedk8s-device-plugin #2002 - merged in 13 days An empty deviceListStrategy list in the config file passed validation, and the container started with no GPU access and no errornvidia-container-toolkit #2108 - merged in 11 days A GPU id repeated in the visible devices request left an empty device in the list, and in CDI mode the container failed to start with an unresolvable device go-nvml #206 - merged in 30 days Shutdown() called nvmlShutdown() whether or not the library had loaded, so on a host without libnvidia-ml.so.1 the shutdown paired with a failed Init() took the process down with a symbol lookup error instead of returning ERROR_UNINITIALIZED |
|
| astc-encoder #668 - merged in 28 days On F16C builds float16_to_float(vint4) packed the half-float codes with signed saturation, so every negative half decoded as NaN and a half-float normal map compressed differently on AVX2 than on SSE4.1 |
|
stylex #1850 - merged in 17 daysstylex.positionTry emitted every declaration twice, one of them with the property name as its own value, and an RTL variant for every rulestylex #1849 - merged in 17 days Configuring a custom importSources on the rollup plugin or unplugin narrowed the compile gate, so ordinary @stylexjs/stylex modules stopped reaching the compiler and the custom source was never transformed |
|
| vehicle-command #479 - merged in 15 days The proxy read every client request body with no size limit, so any client that reached the listener could drive memory allocation without bound |
|
| snappy #257 - merged in 1 day Every release build compressed a 4 GiB input into a stream whose header claimed 0 bytes benchmark #2294 - merged in 7 days The complexity report gave its BigO coefficient in nanoseconds whatever time unit the benchmark declared brotli #1537 - merged in 45 days brotli --rm deleted its input and exited 0 after the output write had failed, because the only flush sat in a metadata copy whose failure returned silently; the close now flushes, reports the failure and keeps the input |
|
| swift-log #504 - merged in 2 days A documented no-op setter asserted instead swift-log #503 - merged in 5 days A handler implementing only log(event:) overflowed the stack on the SwiftLog 1.0 entry pointswift-protobuf #2164 - merged in 15 hours The project's own CMake build of protoc-gen-swift had not compiled since Juneswift-format #1286 - merged in 4 days Formatting in place replaced the file, so a 0600 source came back 0644 and a read-only one lost its bit swift-metrics #244 - merged in 4 days The package's own test kit crashed on a repeated dimension name swift-certificates #317 - merged in 16 days A /0 iPAddress name constraint matched nothing, so a CA barred from every IP address by 0.0.0.0/0 and ::/0 exclusions still verified a leaf with an IP SAN |
|
| mimalloc #1385 - merged in 8 hours The zeroing allocator returned uninitialized memory above the small-size threshold snmalloc #878 - merged in 2 hours The header-only build recipe named a CMake target removed in 2021 and include paths that resolved nowhere GSL #1271 - merged in 4 days The documented conversion from an iterator to its const_iterator was an access error at every useGSL #1272 - merged in 9 days dyn_array_iterator declared random access but had no relational operators, no -> and no n + it, so std::sort over a dyn_array did not compile |
|
zuul #2213 - merged in 17 daysHttpQueryParams.get lower-cased the lookup key against names stored verbatim, so every query parameter with an uppercase letter in its name came back empty |
|
| commons-text #768 - merged in 2 days A StringMatcher overload forwarded the buffer end as its startcommons-csv #633 - merged in 3 days The record counter's Javadoc said headers were not counted while the constructor's header was commons-lang #1784 - merged in 1 day Fraction.add and subtract overflowed on operands not in lowest terms, or returned them unreducedcommons-lang #1783 - merged in 1 day MethodUtils.invokeMethod threw on an instance of any non-public class, every JDK collection factory result includedcommons-lang #1787 - merged in 6 hours Fraction's zero shortcuts returned the other operand unreduced, and threw where the reduced result fitscommons-codec #443 - merged in 9 days The Git tree-id builder sorted entries by UTF-16 code units where Git sorts UTF-8 bytes, so a name outside the Basic Multilingual Plane gave a different id from git write-treecommons-io #875 - merged in 48 days MemoryMappedFileInputStream.read() returned 0xFF80 to 0xFFFF for every byte from 0x80 to 0xFF, outside the 0 to 255 that the InputStream.read() contract promises |
|
| docusaurus-openapi-docs #1625 - merged in 31 days A query or header parameter declared explode: true with a scalar value was enumerated as an object, so status=available went out as status= in the API explorer request and its code snippet, and a null header threwdocusaurus-openapi-docs #1624 - merged in 31 days clean-api-docs deleted every *.json in the output directory and all of schemas/, so a hand-authored _category_.json went with the generated filesdocusaurus-openapi-docs #1626 - merged in 31 days Every copy of the docs spelled the credential-persistence option authPersistance while the theme reads authPersistence, so a site set to false still wrote reader API keys and passwords to sessionStorage |
|
| macaron #1466 - merged in 3 days The build spec dropped the JDK version read from the JAR whenever the artifact recorded no language version |
|
| sarama #3740 - merged in 14 days The round-robin balancer never returned when the topics map held a topic no consumer group member subscribed to fp-go #236 - merged in 10 hours A provider graph that referred back to itself deadlocked the dependency injector forever with no diagnostic; it now reports the chain that closes the circle |
|
| hcl #841 - merged in 18 days hclwrite's Expression.Variables returned nothing for an object value and RenameVariablePrefix left its references untouched, so a rename tool silently missed every variable inside an object |
|
| libsrtp #821 - merged in 4 days Encrypted packets carrying no authentication tag failed to unprotect, because the key lookup stepped a full tag length back from the packet end to find the key identifier |
|
h3 #1235 - merged in 39 daysgridRing zeroed the caller's output buffer with a width computed from k before checking k, so a negative k became a size near 1.8e19 and the write ran off the end of the buffer; the domain check now runs first |
|
| kotlinpoet #2380 - merged in 4 days String literals it emitted turned CRLF into LF, and raw strings in a constant context let newlines pick up indentation kotlinpoet #2382 - merged in 4 days A class in the default package got a ClassName whose toString() threwkotlinpoet #2391 - merged in 19 days trimTrailingNewLine() rewrote the last String argument of any trailing placeholder, so initializer("%S", "cheese
") emitted a literal denoting "cheese" and nothing reported the change |
|
| circl #700 - merged in 1 day The PKI marshal functions panicked on the library's own post-quantum keys instead of returning an error circl #699 - merged in 1 day The hybrid KEM derived a different key pair from the same seed on a random subset of calls |
|
| kotlinx-datetime #650 - merged in 90 minutes Deprecation quick-fixes pointed developers at the wrong replacement kotlinx-datetime #649 - merged in 4 days The Unicode pattern parser dropped the escaped quote inside a literal |
|
| ada #1244 - merged in 12 minutes The URL parser Node.js ships reported host_end one byte short |
|
| pebble #938 - merged in 16 days With more than one service or check, the metrics endpoint repeated each # HELP and # TYPE family and the Prometheus parser rejected the whole responsepebble #937 - merged in 43 days Appending a layer that parsed but failed plan validation left the daemon's live layer list changed, because slices.Insert shifted the existing slice in place before validation ran; the insert now goes into a copy |
|
| natsort #196 - merged in 8 days The locale sentinel meant to sort last was three ASCII bytes, so PyICU keys sorted after it (19 million PyPI downloads a month) |
|
| uuid #907 - merged in 6 days The UUIDv7 counter lost its top four bits to the version nibble (179 million crates.io downloads in the last 90 days) |
Fixed upstream on our report
Each finding below was reproduced by the project's own maintainers, who shipped a fix of their own instead of merging ours.
| Fixed by | Finding | Report to fix |
|---|---|---|
| toxiproxy #770 A slicer toxic with the default attributes recursed until the daemon died with a stack overflow on the first byte through the proxy; a maintainer shipped his own guard and 400 validation in #774 and closed ours as handled there |
30 days | |
| libsrtp #822 The autotools configure aborted on stock Ubuntu because pkg-config was forced static, so no OpenSSL build was possible; reported with either patch offered, and another contributor's #823 citing the report was merged |
3 days | |
| casbin #1752 Set comparisons sorted live policy rows in place, so later Enforce calls returned the wrong answer; the maintainer closed the issue and our patch pointing at his own commit, which drops the sort for a counting map |
8 days | |
| osv-scanner #3107 Vulnerability grouping was order dependent: a record that only linked two existing groups when it arrived last left them split, so one alias could appear in two groups of a scan; the maintainer rewrote the grouper around a union-find in his own pull request, resolving the issue |
16 days |
Private security
| Reported to | Outcome | Answered in |
|---|---|---|
| A security issue in claude-code-action, scored Low (2.3), reported through their program and reproduced and triaged by their own security team. The details stay unpublished at their request until the report resolves. | Scored Low (2.3) in 6 days |
Documentation
| Page | What it covers |
|---|---|
| Usage | Flags, rounds and budgets, High hunt, scoped mode, cancelling, upgrading, uninstalling, and what to know before a first run |
| How it works | The run lifecycle, what the engine enforces, the full rule set, what a converged stop looks like, and how the loop improves itself |
| Headless runs | Running budgeted rounds unattended from bash or PowerShell |
| The receipts | Every open-source target with its outcome, the merged patches, the greenfield builds |
| Contributing | The validator and the review bar |
| White paper | For readers new to agent loops: how loops got here, every rule from first principles, and what this method still cannot do |
| STE100 description | Description and operation in ASD-STE100 Simplified Technical English: the components, the start, the audit, the fix cycle, and the end of a run, in three pages |
License
MIT
Metadata
Release files for jeffy-loop 1.26.5
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | |
|---|---|---|---|---|
| jeffy_loop-1.26.5.tar.gz | 146.0 kB | With provenance | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| jeffy_loop-1.26.5-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 301.7 kB
Release files / jeffy_loop-1.26.5.tar.gz
| Download URL | jeffy_loop-1.26.5.tar.gz |
|---|---|
| Size | 146.0 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
805b9ff50e6d680affe066ba2eaaa861f65573b2d7e5d4b154b97df2d3b91a5f
|
|
BLAKE2b-256 checksum How to use checksums |
61df927e4d75d1e9eabc95da02f053570efbe268bd5773a92bd892943426efcc
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logRelease files / jeffy_loop-1.26.5-py3-none-any.whl
| Download URL | jeffy_loop-1.26.5-py3-none-any.whl |
|---|---|
| Size | 155.7 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
a947dafcd7abcf144ebfcae2586a130948d0d3adb7405795c0d16f9079f02c80
|
|
BLAKE2b-256 checksum How to use checksums |
982ef1d472b2610ce335b3440c897c49a480e8eb475e52fcb2bc6e5e2f92868d
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logSecurity for jeffy-loop 1.26.5
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for jeffy_loop-1.26.5.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Oct 25, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.