jeffy-loop 1.23.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jeffy_loop-1.23.0/.gitignore +53 -0
- jeffy_loop-1.23.0/LICENSE +21 -0
- jeffy_loop-1.23.0/PKG-INFO +122 -0
- jeffy_loop-1.23.0/README.md +110 -0
- jeffy_loop-1.23.0/hatch_build.py +24 -0
- jeffy_loop-1.23.0/pyproject.toml +43 -0
- jeffy_loop-1.23.0/skills/cancel-jeffy/SKILL.md +15 -0
- jeffy_loop-1.23.0/skills/jeffy/SKILL.md +155 -0
- jeffy_loop-1.23.0/skills/jeffy/hooks/lib/check-claims.sh +167 -0
- jeffy_loop-1.23.0/skills/jeffy/hooks/lib/detect-sandbox.sh +46 -0
- jeffy_loop-1.23.0/skills/jeffy/hooks/lib/quiet-verify.sh +243 -0
- jeffy_loop-1.23.0/skills/jeffy/hooks/lib/run-probe.sh +103 -0
- jeffy_loop-1.23.0/skills/jeffy/hooks/stop-hook.sh +2528 -0
- jeffy_loop-1.23.0/skills/jeffy/references/backlog-default.md +33 -0
- jeffy_loop-1.23.0/skills/jeffy/references/backlog-highs.md +26 -0
- jeffy_loop-1.23.0/skills/jeffy/references/iteration-prompt-highs.txt +1 -0
- jeffy_loop-1.23.0/skills/jeffy/references/iteration-prompt.txt +1 -0
- jeffy_loop-1.23.0/skills/jeffy/references/journal-default.md +21 -0
- jeffy_loop-1.23.0/skills/jeffy/references/plan-default.md +104 -0
- jeffy_loop-1.23.0/skills/jeffy/references/plan-highs.md +98 -0
- jeffy_loop-1.23.0/src/jeffy_loop/__init__.py +1 -0
- jeffy_loop-1.23.0/src/jeffy_loop/cli.py +334 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
.DS_Store
|
|
2
|
+
Thumbs.db
|
|
3
|
+
|
|
4
|
+
# Python bytecode - evals/records/repro.py generates this when run or compiled,
|
|
5
|
+
# and the loop's git add -A checkpoints would otherwise sweep it in
|
|
6
|
+
__pycache__/
|
|
7
|
+
|
|
8
|
+
# Jeffy transient loop state - session-scoped, must never be committed
|
|
9
|
+
# (jeffy-loop.local.md is the 2.0 engine's file; ralph-loop.local.md is legacy)
|
|
10
|
+
.claude/jeffy-loop.local.md
|
|
11
|
+
.claude/ralph-loop.local.md
|
|
12
|
+
|
|
13
|
+
# Jeffy self-hosting state - dev logs from running Jeffy on this repo, not part
|
|
14
|
+
# of the product. Anchored to the repo root so a future nested product doc with
|
|
15
|
+
# one of these names is not silently ignored.
|
|
16
|
+
/PLAN.md
|
|
17
|
+
/BACKLOG.md
|
|
18
|
+
/JOURNAL.md
|
|
19
|
+
/JOURNAL-archive.md
|
|
20
|
+
/STATUS.md
|
|
21
|
+
|
|
22
|
+
/CHANGELOG.md
|
|
23
|
+
|
|
24
|
+
# Launch and announcement drafts - not part of the product
|
|
25
|
+
/reddit_post.md
|
|
26
|
+
/reddit_post.MD
|
|
27
|
+
|
|
28
|
+
# Avatar and profile art working files - the shipped product images live in
|
|
29
|
+
# media/, so these are anchored to the repo root only
|
|
30
|
+
/jeffy-avatar*.png
|
|
31
|
+
/jeffy.jpg
|
|
32
|
+
|
|
33
|
+
# Session dev logs - class patterns so future logs are covered by default
|
|
34
|
+
# (both casings spelled out: gitignore matching is case-sensitive on Linux)
|
|
35
|
+
/FABLE*.md
|
|
36
|
+
/FABLE*.MD
|
|
37
|
+
/Fable*.md
|
|
38
|
+
/Fable*.MD
|
|
39
|
+
|
|
40
|
+
# Versioned release-planning docs (plan4.7.md, plan4.8.md, ...) - maintainer
|
|
41
|
+
# working notes, not product. Digit-anchored so a future product doc called
|
|
42
|
+
# plan-something.md is not silently ignored, and both casings spelled out for
|
|
43
|
+
# the same reason as the dev-log patterns above. Without this a self-run's
|
|
44
|
+
# `git add -A` checkpoint sweeps them into the public history.
|
|
45
|
+
/plan[0-9]*.md
|
|
46
|
+
/plan[0-9]*.MD
|
|
47
|
+
/Plan[0-9]*.md
|
|
48
|
+
/PLAN[0-9]*.md
|
|
49
|
+
|
|
50
|
+
# Python package build output (uv build) - never product
|
|
51
|
+
/dist/
|
|
52
|
+
/build/
|
|
53
|
+
*.egg-info/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026-present Jeff Lenamon and the Jeffy Loop contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: jeffy-loop
|
|
3
|
+
Version: 1.23.0
|
|
4
|
+
Summary: The Jeffy Loop: an autonomous audit-and-fix loop for Claude Code, with proof.
|
|
5
|
+
Project-URL: Repository, https://github.com/lenamonj/jeffy-loop
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
License-File: LICENSE
|
|
8
|
+
Classifier: Operating System :: OS Independent
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Requires-Python: >=3.10
|
|
11
|
+
Description-Content-Type: text/markdown
|
|
12
|
+
|
|
13
|
+
<div align="center">
|
|
14
|
+
|
|
15
|
+
<picture>
|
|
16
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/lenamonj/jeffy-loop/main/media/banner-dark.png">
|
|
17
|
+
<img src="https://raw.githubusercontent.com/lenamonj/jeffy-loop/main/media/banner-light.png" alt="Jeffy Loop - point it at a project, give it a budget, come back to a better codebase and a report" width="900">
|
|
18
|
+
</picture>
|
|
19
|
+
|
|
20
|
+
[](https://github.com/lenamonj/jeffy-loop/actions/workflows/validate.yml)
|
|
21
|
+
[](https://claude.com/claude-code)
|
|
22
|
+

|
|
23
|
+
[](https://github.com/lenamonj/jeffy-loop/blob/main/LICENSE)
|
|
24
|
+
|
|
25
|
+
**[Quickstart](#quickstart)** · **[Usage](https://github.com/lenamonj/jeffy-loop/blob/main/docs/usage.md)** · **[How it works](https://github.com/lenamonj/jeffy-loop/blob/main/docs/how-it-works.md)** · **[The receipts](https://github.com/lenamonj/jeffy-loop/blob/main/evals/README.md)** · **[Headless](https://github.com/lenamonj/jeffy-loop/blob/main/docs/headless.md)** · **[White paper](https://github.com/lenamonj/jeffy-loop/raw/main/The-Jeffy-Loop.pdf)**
|
|
26
|
+
|
|
27
|
+
## Autonomous Engineering With Proof
|
|
28
|
+
|
|
29
|
+
> **Agents that don’t just act.**
|
|
30
|
+
> They audit · verify · attack · and prove.
|
|
31
|
+
|
|
32
|
+
</div>
|
|
33
|
+
|
|
34
|
+
Jeffy Loop is an autonomous engineering system built around a simple principle: **AI agents shouldn’t just produce work. They should produce evidence that the work is correct.** Instead of asking an agent to complete a task and trusting its conclusion, Jeffy creates a continuous **Audit → Attack → Verify → Prove** loop in which specialized agents inspect the work, challenge it, validate the result, and generate an auditable record of what happened. The goal isn’t simply autonomous code generation; it is **autonomous engineering with proof** - where every claimed result is accompanied by reproducible evidence that can be independently examined.
|
|
35
|
+
|
|
36
|
+
**Jeffy treats “done” as something that must be demonstrated, not declared.** The system is designed to turn autonomous engineering from a conversational interaction into an evidence-producing process: actions leave traces, decisions have provenance, failures are exposed rather than hidden, and successful outcomes produce a durable receipt of what was changed, why it was changed, and how the result was verified.
|
|
37
|
+
|
|
38
|
+
## The proof
|
|
39
|
+
|
|
40
|
+
Jeffy was run against <!-- count:tested -->132<!-- /count --> open-source projects with no connection to this repository, each judged by its own test suite, every run published, failures included.
|
|
41
|
+
|
|
42
|
+
| Projects tested | Converged | Failed | PRs merged | PRs open | Issues filed |
|
|
43
|
+
|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
44
|
+
| **<!-- count:tested -->132<!-- /count -->** | **<!-- count:converged -->103<!-- /count -->** | **<!-- count:failed -->28<!-- /count -->** | **<!-- count:merged -->24<!-- /count -->** | **<!-- count:prs-open -->37<!-- /count -->** | **<!-- count:issues -->4<!-- /count -->** |
|
|
45
|
+
|
|
46
|
+
<div align="center">
|
|
47
|
+
|
|
48
|
+
<picture>
|
|
49
|
+
<source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/lenamonj/jeffy-loop/main/media/language-pie-dark.png">
|
|
50
|
+
<img src="https://raw.githubusercontent.com/lenamonj/jeffy-loop/main/media/language-pie-light.png" alt="Pie chart of the 103 converged public targets by language: Python 20 at 19.4 percent, Rust 14 at 13.6 percent, Go 12 at 11.7 percent, JavaScript 10 at 9.7 percent, C++ 7 at 6.8 percent, Java 6 at 5.8 percent, Ruby 6 at 5.8 percent, Swift 6 at 5.8 percent, C 5 at 4.9 percent, PHP 5 at 4.9 percent, TypeScript 5 at 4.9 percent, Kotlin 4 at 3.9 percent, C# 3 at 2.9 percent." width="900">
|
|
51
|
+
</picture>
|
|
52
|
+
|
|
53
|
+
<sub><!-- count:converged -->103<!-- /count --> projects run to convergence across <!-- count:languages -->13<!-- /count --> languages with no language-specific analyzer or ruleset. Derived from the scorecard at render time.</sub>
|
|
54
|
+
|
|
55
|
+
</div>
|
|
56
|
+
|
|
57
|
+
## Independent Validation
|
|
58
|
+
|
|
59
|
+
A merged pull request is the one result Jeffy cannot award itself. It takes an independent maintainer, someone with no stake in this project, to review the patch and accept it into their own codebase. Maintainers across <!-- count:merged-projects -->21<!-- /count --> open source projects have done exactly that, including:
|
|
60
|
+
|
|
61
|
+
- **Google, [snappy](https://github.com/google/snappy/pull/257)** - every release build compressed a 4 GiB input into a stream whose header claimed 0 bytes; merged the morning after filing.
|
|
62
|
+
- **Apple (2)**
|
|
63
|
+
- [swift-log](https://github.com/apple/swift-log/pull/504) - a documented no-op setter asserted instead; merged after the maintainer asked for the doc-only form.
|
|
64
|
+
- [swift-protobuf](https://github.com/apple/swift-protobuf/pull/2164) - the project's own CMake build of `protoc-gen-swift` had not compiled since June; approved and merged the day it was filed.
|
|
65
|
+
- **Microsoft, [mimalloc](https://github.com/microsoft/mimalloc/pull/1385)** - the zeroing allocator returned uninitialized memory above the small-size threshold; merged by the author the same day.
|
|
66
|
+
- **Apache (2)**
|
|
67
|
+
- [commons-text](https://github.com/apache/commons-text/pull/768) - a `StringMatcher` overload forwarded the buffer end as its start; merged the morning after review.
|
|
68
|
+
- [commons-csv](https://github.com/apache/commons-csv/pull/633) - the record counter's Javadoc said headers were not counted while the constructor's header was; corrected, merged three days after filing.
|
|
69
|
+
- **JetBrains, [kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime/pull/650)** - deprecation quick-fixes pointed developers at the wrong replacement; merged within two hours.
|
|
70
|
+
- **Node.js, [ada](https://github.com/ada-url/ada/pull/1244)** - the URL parser reported `host_end` one byte short; merged twelve minutes after filing.
|
|
71
|
+
|
|
72
|
+
**[Every project, every patch, and every failure](https://github.com/lenamonj/jeffy-loop/blob/main/evals/README.md)**
|
|
73
|
+
|
|
74
|
+
## Quickstart
|
|
75
|
+
|
|
76
|
+
You need [Claude Code](https://claude.com/claude-code), signed in once, and [git](https://git-scm.com/downloads). The installer handles everything else, including `jq`.
|
|
77
|
+
|
|
78
|
+
```bash
|
|
79
|
+
git clone https://github.com/lenamonj/jeffy-loop.git
|
|
80
|
+
cd jeffy-loop
|
|
81
|
+
./install.sh # Windows PowerShell: .\install.ps1
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
Or, with [uv](https://docs.astral.sh/uv/), no clone needed:
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
uv tool install jeffy-loop
|
|
88
|
+
jeffy install
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
> [!NOTE]
|
|
92
|
+
> If PowerShell refuses with "running scripts is disabled on this system", run `powershell -ExecutionPolicy Bypass -File .\install.ps1` once.
|
|
93
|
+
|
|
94
|
+
Open Claude Code in the project you want to improve and type `/jeffy 10`. It is a slash command inside the session, not a shell command. If you only want the Highs found and fixed, type `/jeffy 10 --highs`; it stops at the first audit that finds none, so it is usually the faster run ([High hunt](https://github.com/lenamonj/jeffy-loop/blob/main/docs/usage.md#high-hunt)). When the run ends, start a new session to run it again; [the restart is doing real work](https://github.com/lenamonj/jeffy-loop/blob/main/docs/usage.md#use-several-short-runs-not-one-long-one).
|
|
95
|
+
|
|
96
|
+
## Five guarantees
|
|
97
|
+
|
|
98
|
+
Each one is enforced by the iteration prompt, the state files, or the Stop hook. [How.](https://github.com/lenamonj/jeffy-loop/blob/main/docs/how-it-works.md#five-guarantees)
|
|
99
|
+
|
|
100
|
+
1. **It audits like an engineer, not a linter.** A finding exists only when the loop can point at it and prove it with a runnable check.
|
|
101
|
+
2. **It cannot wreck your repo.** Every iteration is a local commit, a broken verify is reverted, and nothing is ever pushed.
|
|
102
|
+
3. **"Done" is not the agent's opinion.** An adversarial evaluator and a shell gate re-check every declaration.
|
|
103
|
+
4. **It cannot claim what it never looked at.** The whole public surface goes on a checklist before any finding is filed.
|
|
104
|
+
5. **Lessons become machinery.** A rule learned once binds every later iteration, and the engine itself passes <!-- count:checks -->**347 behavioural checks**<!-- /count --> on Linux, Windows and macOS.
|
|
105
|
+
|
|
106
|
+
## Documentation
|
|
107
|
+
|
|
108
|
+
| Page | What it covers |
|
|
109
|
+
|:---|:---|
|
|
110
|
+
| [Usage](https://github.com/lenamonj/jeffy-loop/blob/main/docs/usage.md) | Flags, rounds and budgets, scoped mode, cancelling, [upgrading](https://github.com/lenamonj/jeffy-loop/blob/main/docs/usage.md#already-installed-upgrade), uninstalling, and what to know before a first run |
|
|
111
|
+
| [How it works](https://github.com/lenamonj/jeffy-loop/blob/main/docs/how-it-works.md) | The run lifecycle, the five guarantees, the full rule set, what a converged stop looks like, and how the loop improves itself |
|
|
112
|
+
| [Headless runs](https://github.com/lenamonj/jeffy-loop/blob/main/docs/headless.md) | Running budgeted rounds unattended from bash or PowerShell |
|
|
113
|
+
| [The receipts](https://github.com/lenamonj/jeffy-loop/blob/main/evals/README.md) | Every open-source target with its outcome, the merged patches, the greenfield builds |
|
|
114
|
+
| [Contributing](https://github.com/lenamonj/jeffy-loop/blob/main/CONTRIBUTING.md) | The validator and the review bar |
|
|
115
|
+
| [White paper](https://github.com/lenamonj/jeffy-loop/raw/main/The-Jeffy-Loop.pdf) | For readers new to agent loops: how loops got here, every rule from first principles, and what this method still cannot do |
|
|
116
|
+
|
|
117
|
+
> [!IMPORTANT]
|
|
118
|
+
> **Trust model.** The engine is one shell script, `skills/jeffy/hooks/stop-hook.sh`, plus the small library beside it in `skills/jeffy/hooks/lib/`, registered as a Claude Code Stop hook. In a session with no live Jeffy state file it exits at once and does nothing. The installer writes two skill folders under `~/.claude/skills`, one hook entry in `~/.claude/settings.json`, and, only if you say yes when `jq` is missing, a `jq` install through your package manager.
|
|
119
|
+
|
|
120
|
+
## License
|
|
121
|
+
|
|
122
|
+
MIT
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
<div align="center">
|
|
2
|
+
|
|
3
|
+
<picture>
|
|
4
|
+
<source media="(prefers-color-scheme: dark)" srcset="media/banner-dark.png">
|
|
5
|
+
<img src="media/banner-light.png" alt="Jeffy Loop - point it at a project, give it a budget, come back to a better codebase and a report" width="900">
|
|
6
|
+
</picture>
|
|
7
|
+
|
|
8
|
+
[](https://github.com/lenamonj/jeffy-loop/actions/workflows/validate.yml)
|
|
9
|
+
[](https://claude.com/claude-code)
|
|
10
|
+

|
|
11
|
+
[](LICENSE)
|
|
12
|
+
|
|
13
|
+
**[Quickstart](#quickstart)** · **[Usage](docs/usage.md)** · **[How it works](docs/how-it-works.md)** · **[The receipts](evals/README.md)** · **[Headless](docs/headless.md)** · **[White paper](https://github.com/lenamonj/jeffy-loop/raw/main/The-Jeffy-Loop.pdf)**
|
|
14
|
+
|
|
15
|
+
## Autonomous Engineering With Proof
|
|
16
|
+
|
|
17
|
+
> **Agents that don’t just act.**
|
|
18
|
+
> They audit · verify · attack · and prove.
|
|
19
|
+
|
|
20
|
+
</div>
|
|
21
|
+
|
|
22
|
+
Jeffy Loop is an autonomous engineering system built around a simple principle: **AI agents shouldn’t just produce work. They should produce evidence that the work is correct.** Instead of asking an agent to complete a task and trusting its conclusion, Jeffy creates a continuous **Audit → Attack → Verify → Prove** loop in which specialized agents inspect the work, challenge it, validate the result, and generate an auditable record of what happened. The goal isn’t simply autonomous code generation; it is **autonomous engineering with proof** - where every claimed result is accompanied by reproducible evidence that can be independently examined.
|
|
23
|
+
|
|
24
|
+
**Jeffy treats “done” as something that must be demonstrated, not declared.** The system is designed to turn autonomous engineering from a conversational interaction into an evidence-producing process: actions leave traces, decisions have provenance, failures are exposed rather than hidden, and successful outcomes produce a durable receipt of what was changed, why it was changed, and how the result was verified.
|
|
25
|
+
|
|
26
|
+
## The proof
|
|
27
|
+
|
|
28
|
+
Jeffy was run against <!-- count:tested -->132<!-- /count --> open-source projects with no connection to this repository, each judged by its own test suite, every run published, failures included.
|
|
29
|
+
|
|
30
|
+
| Projects tested | Converged | Failed | PRs merged | PRs open | Issues filed |
|
|
31
|
+
|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
32
|
+
| **<!-- count:tested -->132<!-- /count -->** | **<!-- count:converged -->103<!-- /count -->** | **<!-- count:failed -->28<!-- /count -->** | **<!-- count:merged -->24<!-- /count -->** | **<!-- count:prs-open -->37<!-- /count -->** | **<!-- count:issues -->4<!-- /count -->** |
|
|
33
|
+
|
|
34
|
+
<div align="center">
|
|
35
|
+
|
|
36
|
+
<picture>
|
|
37
|
+
<source media="(prefers-color-scheme: dark)" srcset="media/language-pie-dark.png">
|
|
38
|
+
<img src="media/language-pie-light.png" alt="Pie chart of the 103 converged public targets by language: Python 20 at 19.4 percent, Rust 14 at 13.6 percent, Go 12 at 11.7 percent, JavaScript 10 at 9.7 percent, C++ 7 at 6.8 percent, Java 6 at 5.8 percent, Ruby 6 at 5.8 percent, Swift 6 at 5.8 percent, C 5 at 4.9 percent, PHP 5 at 4.9 percent, TypeScript 5 at 4.9 percent, Kotlin 4 at 3.9 percent, C# 3 at 2.9 percent." width="900">
|
|
39
|
+
</picture>
|
|
40
|
+
|
|
41
|
+
<sub><!-- count:converged -->103<!-- /count --> projects run to convergence across <!-- count:languages -->13<!-- /count --> languages with no language-specific analyzer or ruleset. Derived from the scorecard at render time.</sub>
|
|
42
|
+
|
|
43
|
+
</div>
|
|
44
|
+
|
|
45
|
+
## Independent Validation
|
|
46
|
+
|
|
47
|
+
A merged pull request is the one result Jeffy cannot award itself. It takes an independent maintainer, someone with no stake in this project, to review the patch and accept it into their own codebase. Maintainers across <!-- count:merged-projects -->21<!-- /count --> open source projects have done exactly that, including:
|
|
48
|
+
|
|
49
|
+
- **Google, [snappy](https://github.com/google/snappy/pull/257)** - every release build compressed a 4 GiB input into a stream whose header claimed 0 bytes; merged the morning after filing.
|
|
50
|
+
- **Apple (2)**
|
|
51
|
+
- [swift-log](https://github.com/apple/swift-log/pull/504) - a documented no-op setter asserted instead; merged after the maintainer asked for the doc-only form.
|
|
52
|
+
- [swift-protobuf](https://github.com/apple/swift-protobuf/pull/2164) - the project's own CMake build of `protoc-gen-swift` had not compiled since June; approved and merged the day it was filed.
|
|
53
|
+
- **Microsoft, [mimalloc](https://github.com/microsoft/mimalloc/pull/1385)** - the zeroing allocator returned uninitialized memory above the small-size threshold; merged by the author the same day.
|
|
54
|
+
- **Apache (2)**
|
|
55
|
+
- [commons-text](https://github.com/apache/commons-text/pull/768) - a `StringMatcher` overload forwarded the buffer end as its start; merged the morning after review.
|
|
56
|
+
- [commons-csv](https://github.com/apache/commons-csv/pull/633) - the record counter's Javadoc said headers were not counted while the constructor's header was; corrected, merged three days after filing.
|
|
57
|
+
- **JetBrains, [kotlinx-datetime](https://github.com/Kotlin/kotlinx-datetime/pull/650)** - deprecation quick-fixes pointed developers at the wrong replacement; merged within two hours.
|
|
58
|
+
- **Node.js, [ada](https://github.com/ada-url/ada/pull/1244)** - the URL parser reported `host_end` one byte short; merged twelve minutes after filing.
|
|
59
|
+
|
|
60
|
+
**[Every project, every patch, and every failure](evals/README.md)**
|
|
61
|
+
|
|
62
|
+
## Quickstart
|
|
63
|
+
|
|
64
|
+
You need [Claude Code](https://claude.com/claude-code), signed in once, and [git](https://git-scm.com/downloads). The installer handles everything else, including `jq`.
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
git clone https://github.com/lenamonj/jeffy-loop.git
|
|
68
|
+
cd jeffy-loop
|
|
69
|
+
./install.sh # Windows PowerShell: .\install.ps1
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Or, with [uv](https://docs.astral.sh/uv/), no clone needed:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
uv tool install jeffy-loop
|
|
76
|
+
jeffy install
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
> [!NOTE]
|
|
80
|
+
> If PowerShell refuses with "running scripts is disabled on this system", run `powershell -ExecutionPolicy Bypass -File .\install.ps1` once.
|
|
81
|
+
|
|
82
|
+
Open Claude Code in the project you want to improve and type `/jeffy 10`. It is a slash command inside the session, not a shell command. If you only want the Highs found and fixed, type `/jeffy 10 --highs`; it stops at the first audit that finds none, so it is usually the faster run ([High hunt](docs/usage.md#high-hunt)). When the run ends, start a new session to run it again; [the restart is doing real work](docs/usage.md#use-several-short-runs-not-one-long-one).
|
|
83
|
+
|
|
84
|
+
## Five guarantees
|
|
85
|
+
|
|
86
|
+
Each one is enforced by the iteration prompt, the state files, or the Stop hook. [How.](docs/how-it-works.md#five-guarantees)
|
|
87
|
+
|
|
88
|
+
1. **It audits like an engineer, not a linter.** A finding exists only when the loop can point at it and prove it with a runnable check.
|
|
89
|
+
2. **It cannot wreck your repo.** Every iteration is a local commit, a broken verify is reverted, and nothing is ever pushed.
|
|
90
|
+
3. **"Done" is not the agent's opinion.** An adversarial evaluator and a shell gate re-check every declaration.
|
|
91
|
+
4. **It cannot claim what it never looked at.** The whole public surface goes on a checklist before any finding is filed.
|
|
92
|
+
5. **Lessons become machinery.** A rule learned once binds every later iteration, and the engine itself passes <!-- count:checks -->**347 behavioural checks**<!-- /count --> on Linux, Windows and macOS.
|
|
93
|
+
|
|
94
|
+
## Documentation
|
|
95
|
+
|
|
96
|
+
| Page | What it covers |
|
|
97
|
+
|:---|:---|
|
|
98
|
+
| [Usage](docs/usage.md) | Flags, rounds and budgets, scoped mode, cancelling, [upgrading](docs/usage.md#already-installed-upgrade), uninstalling, and what to know before a first run |
|
|
99
|
+
| [How it works](docs/how-it-works.md) | The run lifecycle, the five guarantees, the full rule set, what a converged stop looks like, and how the loop improves itself |
|
|
100
|
+
| [Headless runs](docs/headless.md) | Running budgeted rounds unattended from bash or PowerShell |
|
|
101
|
+
| [The receipts](evals/README.md) | Every open-source target with its outcome, the merged patches, the greenfield builds |
|
|
102
|
+
| [Contributing](CONTRIBUTING.md) | The validator and the review bar |
|
|
103
|
+
| [White paper](https://github.com/lenamonj/jeffy-loop/raw/main/The-Jeffy-Loop.pdf) | For readers new to agent loops: how loops got here, every rule from first principles, and what this method still cannot do |
|
|
104
|
+
|
|
105
|
+
> [!IMPORTANT]
|
|
106
|
+
> **Trust model.** The engine is one shell script, `skills/jeffy/hooks/stop-hook.sh`, plus the small library beside it in `skills/jeffy/hooks/lib/`, registered as a Claude Code Stop hook. In a session with no live Jeffy state file it exits at once and does nothing. The installer writes two skill folders under `~/.claude/skills`, one hook entry in `~/.claude/settings.json`, and, only if you say yes when `jq` is missing, a `jq` install through your package manager.
|
|
107
|
+
|
|
108
|
+
## License
|
|
109
|
+
|
|
110
|
+
MIT
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""Rewrite the README for PyPI at build time.
|
|
2
|
+
|
|
3
|
+
PyPI renders the project description from README.md but cannot resolve
|
|
4
|
+
relative paths, so images under assets/ and links to files in the repository
|
|
5
|
+
render broken there. This hook turns every relative src, srcset and markdown
|
|
6
|
+
link into an absolute GitHub URL in the built metadata only; the README in the
|
|
7
|
+
repository stays relative, which is what GitHub wants.
|
|
8
|
+
"""
|
|
9
|
+
import os
|
|
10
|
+
import re
|
|
11
|
+
|
|
12
|
+
from hatchling.metadata.plugin.interface import MetadataHookInterface
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class ReadmeHook(MetadataHookInterface):
|
|
16
|
+
def update(self, metadata):
|
|
17
|
+
repo = self.config["repo"]
|
|
18
|
+
raw = f"https://raw.githubusercontent.com/{repo}/main/"
|
|
19
|
+
blob = f"https://github.com/{repo}/blob/main/"
|
|
20
|
+
with open(os.path.join(self.root, "README.md"), encoding="utf-8") as f:
|
|
21
|
+
text = f.read()
|
|
22
|
+
text = re.sub(r'((?:src|srcset)=")(?!https?://)([^"]+)"', lambda m: f'{m.group(1)}{raw}{m.group(2)}"', text)
|
|
23
|
+
text = re.sub(r'\]\((?!https?://|#|mailto:)([^)\s]+)\)', lambda m: f"]({blob}{m.group(1)})", text)
|
|
24
|
+
metadata["readme"] = {"content-type": "text/markdown", "text": text}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["hatchling"]
|
|
3
|
+
build-backend = "hatchling.build"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "jeffy-loop"
|
|
7
|
+
version = "1.23.0"
|
|
8
|
+
description = "The Jeffy Loop: an autonomous audit-and-fix loop for Claude Code, with proof."
|
|
9
|
+
dynamic = ["readme"]
|
|
10
|
+
license = "MIT"
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
dependencies = []
|
|
13
|
+
classifiers = [
|
|
14
|
+
"Programming Language :: Python :: 3",
|
|
15
|
+
"Operating System :: OS Independent",
|
|
16
|
+
]
|
|
17
|
+
|
|
18
|
+
[project.scripts]
|
|
19
|
+
jeffy = "jeffy_loop.cli:main"
|
|
20
|
+
|
|
21
|
+
[project.urls]
|
|
22
|
+
Repository = "https://github.com/lenamonj/jeffy-loop"
|
|
23
|
+
|
|
24
|
+
[tool.hatch.build.targets.wheel]
|
|
25
|
+
packages = ["src/jeffy_loop"]
|
|
26
|
+
|
|
27
|
+
[tool.hatch.build.targets.wheel.force-include]
|
|
28
|
+
"skills/jeffy" = "jeffy_loop/skills/jeffy"
|
|
29
|
+
"skills/cancel-jeffy" = "jeffy_loop/skills/cancel-jeffy"
|
|
30
|
+
|
|
31
|
+
[tool.hatch.build.targets.sdist]
|
|
32
|
+
only-include = [
|
|
33
|
+
"src/jeffy_loop",
|
|
34
|
+
"hatch_build.py",
|
|
35
|
+
"skills/jeffy",
|
|
36
|
+
"skills/cancel-jeffy",
|
|
37
|
+
"README.md",
|
|
38
|
+
"LICENSE",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[tool.hatch.metadata.hooks.custom]
|
|
42
|
+
path = "hatch_build.py"
|
|
43
|
+
repo = "lenamonj/jeffy-loop"
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: cancel-jeffy
|
|
3
|
+
description: Use when the user runs /cancel-jeffy to stop the active Jeffy improvement loop in the current project
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Cancel Jeffy
|
|
8
|
+
|
|
9
|
+
Stop the Jeffy loop in this project by removing the loop state file the Stop hook reads.
|
|
10
|
+
|
|
11
|
+
1. Project root is the directory Claude Code was started in, not wherever the shell currently sits: the Bash tool's cwd persists across calls and may have drifted into a subdirectory that holds its own orphaned state file from another session. Always check state files with absolute paths anchored at the project root, never bare relative ones.
|
|
12
|
+
2. Check `<project-root>/.claude/jeffy-loop.local.md`. If it exists, read its frontmatter and report its session_id, iteration, and started_at, then delete the file and confirm the loop is cancelled. Once the file is gone the Stop hook lets the session end normally.
|
|
13
|
+
3. Also check `<project-root>/.claude/ralph-loop.local.md`: it belongs to the ralph-loop plugin's engine or a pre-2.0 Jeffy. If it exists, report the same frontmatter fields and delete it too, so no legacy loop keeps re-feeding.
|
|
14
|
+
4. If neither file exists, report that no Jeffy loop is active in this project and stop.
|
|
15
|
+
5. Remind the user that PLAN.md, BACKLOG.md, and JOURNAL.md are untouched: the loop picks up where it left off the next time they run /jeffy.
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: jeffy
|
|
3
|
+
description: Use when the user runs /jeffy to start an autonomous Jeffy improvement loop on the current project
|
|
4
|
+
disable-model-invocation: true
|
|
5
|
+
argument-hint: "[N] [--highs] [focus...]"
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Jeffy
|
|
9
|
+
|
|
10
|
+
Bootstrap per-project Jeffy state files, write the loop state file, and begin iteration 1. The loop engine is Jeffy's own Stop hook, shipped with this skill at hooks/stop-hook.sh: at every turn end it reads `.claude/jeffy-loop.local.md` at the project root, and while that file names this session and budget remains it re-feeds the iteration prompt; it deletes the file and lets the session end when the budget is spent, the completion promise fires, or one of its own gates ends the run - a stall, an oscillation, a time ceiling, the sweep fail-fast, or an audit or refill inside the closing window. The hook anchors itself to CLAUDE_PROJECT_DIR, which hooks receive fixed at the directory Claude Code was started in, so shell cwd drift mid-iteration cannot kill the loop. The hook is registered machine-wide in `~/.claude/settings.json` by the installer but exits immediately in any session whose project has no state file. This skill only sets up and launches; the hook implements the loop mechanics.
|
|
11
|
+
|
|
12
|
+
Project root means the directory Claude Code was started in - the session's primary working directory shown in the environment, which is stable regardless of shell drift. Resolve it to an absolute path with forward slashes and use it everywhere below; never trust the shell's current cwd, which persists across Bash calls and may have drifted into a subdirectory.
|
|
13
|
+
|
|
14
|
+
## Arguments
|
|
15
|
+
|
|
16
|
+
Parse $ARGUMENTS: if the first token is an integer, it is the iteration budget N, default 10, or 5 on a `--highs` launch. Next parse the mode flag and the ceiling flags wherever they appear in the remainder. `--highs` takes no value and selects High-hunt mode for this run: the loop audits the whole surface, fixes only Highs, audits again once no High is open, and closes the first time an audit finds none - no sweep obligation, no Medium or Low queue, no evaluator gate, no closing extension and no convergence claim; a hunt is not a convergence and its receipt is the pull request its Highs produce. The ceiling flags each take one value: `--max-time <duration>` sets `max_wall_clock_seconds`, `--max-iter-time <duration>` sets `max_iteration_seconds`, and `--max-context <N>` sets `max_context_growth`; a duration is a bare number of seconds or a number suffixed `s`, `m`, or `h` (`45m`, `2h`, `900s`, `900`), and `0` means off, the default for all three. A flag missing its value, an unparseable duration, or an unrecognized `--` token is a refusal naming the token, never silently folded into the focus - a documented ceiling that becomes free text is a ceiling that is silently off. Whatever text remains after the budget and the flags is the focus directive for this run. If the token after the optional budget is exactly `enhance`, refuse the launch and stop: Enhance mode was removed in v1.11.0 - the defect loop is the product - and the last release that carries it is v1.10.0. On a fresh project, iteration 1 is always consumed by the audit that generates the backlog, so N=2 executes exactly one task; if the user picks N below 5, proceed but note that larger budgets make materially more progress.
|
|
17
|
+
|
|
18
|
+
## Step 1: Pre-flight
|
|
19
|
+
|
|
20
|
+
If any check fails, stop and report the exact fix needed. Do no other work first.
|
|
21
|
+
|
|
22
|
+
1. Hook dependency: `which jq`. If missing the Stop hook cannot parse its input and the loop cannot run. Suggest `winget install jqlang.jq` (Windows), `brew install jq` (macOS), or `sudo apt-get install jq` (Debian/Ubuntu) and stop.
|
|
23
|
+
2. Hook install: locate Jeffy's Stop hook script, substituting the absolute home directory and using forward slashes even on Windows (`~` is not expanded by this tool). Glob for `<home>/.claude/skills/jeffy/hooks/stop-hook.sh`. If it is missing, the install is broken or predates the self-owned engine; tell the user to re-run the installer (install.sh or install.ps1) and stop. If it exists, the hook must also be registered: read `<home>/.claude/settings.json` and confirm some Stop hook command contains `skills/jeffy/hooks/stop-hook.sh`. If the registration is missing, tell the user to re-run the installer, which registers it, and stop. When the project root itself carries `skills/jeffy/hooks/stop-hook.sh` - a checkout of Jeffy, the one project where the engine under development is also the engine driving the loop - compare that file with the installed one byte for byte and say so in one line when they differ: the loop is driven by the installed copy, so whatever the tree has fixed since is not in force, and the suite grades a file the run never executes. Compare content, never JEFFY_VERSION, which is equal across a divergence by construction because a development tree carries the next release's fixes under the current version string. This never blocks and never prompts: an unmatched pair is the ordinary state of a tree mid-change, and the remedy - re-running install.sh - belongs to the operator and must not be taken mid-run, since it would swap the engine between iterations.
|
|
24
|
+
3. Session identity: `echo "$CLAUDE_CODE_SESSION_ID"` must print a non-empty id. If empty, the state file would be written without session scoping and the loop would capture every Claude session in this project. Stop.
|
|
25
|
+
4. Existing loop state: if `.claude/jeffy-loop.local.md` exists at the project root, do not assume a loop is active. Read its frontmatter and compare its `session_id` with the current session id:
|
|
26
|
+
- Equal: this session already has a loop running. Stop.
|
|
27
|
+
- Different or missing: either another live session owns it or, far more often, it is an orphan from a closed session. The hook deletes the file only when its own session's run ends - at the budget, the promise, or one of its gates - so a crashed or closed session leaks it forever. Report the file's session_id, started_at, and iteration, then ask the user: if no other session is running Jeffy in this project, confirm deletion and continue; otherwise stop. Never delete the file without explicit confirmation.
|
|
28
|
+
Also check for a legacy `.claude/ralph-loop.local.md` at the project root: it belongs to the ralph-loop plugin's engine (or a pre-2.0 Jeffy). If present, a ralph-loop-driven loop may still be active in another session; report it and ask the user to cancel or delete it before launching, so two engines never interleave in one project.
|
|
29
|
+
5. Checkpoint baseline: if the project is a git repository (`git rev-parse --is-inside-work-tree` succeeds), run `git status --porcelain` and ignore any path under `.jeffy/metrics/`. That file is the engine's own telemetry, written at the end of every turn including the one that ends a run - so the last write of any completed run necessarily lands after that run's final checkpoint and leaves the path modified with no iteration left to commit it. It is never the user's work, the next checkpoint sweeps it up, and treating it as a dirty tree stops every unattended second round dead: the question goes to a headless session that cannot answer it. If what remains prints anything, tell the user: the loop ends every iteration with a local checkpoint commit made with `git add -A`, so these uncommitted changes would be swept into the first checkpoint. Ask them to choose: commit or stash first (then relaunch), proceed anyway (their changes ride along in the first jeffy checkpoint), or abort. Never proceed silently past a dirty tree. When every modified path is a symlink in the index (`git ls-files -s` reports mode `120000` for each), say so explicitly: the likely cause is a cross-filesystem tree - Windows git cannot stat symlinks over `\\wsl.localhost` and reports them all modified - and the fix is running the loop from a git that lives on the same filesystem as the tree, not committing or stashing. If the project is not a git repository, note once that checkpoints, salvage, the ratchet, the verify-gate revert, and the stall check degrade to journal-only discipline, and continue.
|
|
30
|
+
6. Verify command lint: if `PLAN.md` exists at the project root and its `## Verify command` section carries a `Command: ` line whose payload is neither `none` nor an unfilled `<...>` placeholder, sanitize that payload exactly as the hook does - trim the surrounding whitespace, then strip one wrapping pair of backticks when both ends carry one and nothing between them does - and run `bash -n` over the result. If it does not parse, report the exact defect (the first `bash -n` error line) and the exact corrected `Command: ` line to write into PLAN.md, then stop. Apply the same refusal when the payload contains a pipe and its final pipeline stage is a pager or truncator - `head`, `tail`, `less`, `more`, or `cat` - because the pipeline's exit status is then the truncator's, not the suite's, and a failing suite reports green; name the offending stage and tell the user to drop it. The hook executes that line verbatim at the converged stop, so a malformed line costs a rejected declaration at the end of a run instead of one message at its start. A missing PLAN.md, a section carrying no `Command: ` line, a payload of `none`, and a payload still wearing the template's `<first audit fills this in>` placeholder are all fine and stop nothing: the hook skips its own check on the first three, and the placeholder is the line the first audit exists to fill, so linting it would hard-stop every relaunch whose bootstrapped PLAN.md never reached that audit.
|
|
31
|
+
7. Line-ending safety: if the project is a git repository and the platform is not Windows (or the project root is a Linux or WSL filesystem path), run `git config core.autocrlf`. If it prints `true` or `input`, refuse to launch: the loop's verify-gate revert path runs `git checkout`, which would rewrite every text file in the tree to CRLF and break the build while looking like a clean revert. Report the exact fix - run `git config core.autocrlf false` at the project root - and stop.
|
|
32
|
+
8. Repository scope: if the project is a git repository, compare `git rev-parse --show-toplevel` with the project root. When they differ, the project is a subdirectory of a larger repository, so every checkpoint's `git add -A` stages changes across the whole parent tree. State both paths plainly and ask the user whether to proceed, exactly as the dirty-tree check does; working in a subdirectory is legitimate, so never refuse outright.
|
|
33
|
+
9. Nested Jeffy project: glob for `*/.claude/jeffy-loop.local.md` and `*/PLAN.md` below the project root. If a nested directory carries Jeffy state files, surface its path and ask whether the user meant to launch there instead: launching above an existing Jeffy project sweeps that project's state files into this project's checkpoints.
|
|
34
|
+
10. Budget echo: print one line the operator has to see before the state file is written - the resolved N, the project root, the base HEAD - and, when JOURNAL.md exists, two figures derived from its headings: the N of the most recent `## iter i/N` heading, and the count of distinct run ids across JOURNAL-archive.md and JOURNAL.md, so the line reads `Budget: N=<N> at <short HEAD> in <root>; run <k+1> of this project (previous run N=<n>)`. Derive them with `grep -h '^## iter [0-9]' JOURNAL.md | tail -n 1 | sed -n 's|^## iter [0-9]*/\([0-9]*\).*|\1|p'` and `cat JOURNAL-archive.md JOURNAL.md 2>/dev/null | grep '^## iter [0-9]' | cut -d'|' -f2 | tr -d ' ' | sort -u | wc -l`. The second reads the run id as a field rather than through a substitution, because the heading is pipe-delimited and a sed whose own delimiter appears three times inside its pattern is read as a truncated expression: written that way it printed an error and returned 0, so the line reported run 1 of every project forever. Both commands anchor on `[0-9]` so the grammar example in the journal template's preamble is not counted as an entry. Never ask and never stop on it: a headless session cannot answer a question and the campaign driver owns the budget; the line exists so a run launched at the wrong N, or a fifth run on a four-run pre-registration, is seen at launch rather than found afterwards.
|
|
35
|
+
|
|
36
|
+
## Step 2: Bootstrap state files
|
|
37
|
+
|
|
38
|
+
Create each file at the project root only if it is missing. The default contents live in this skill's references directory and are copied with cp, never read into context or retyped: the templates are large static payloads and the copy is byte-exact.
|
|
39
|
+
|
|
40
|
+
First resolve REF, the absolute path of this skill's references directory. Glob for `<home>/.claude/skills/jeffy/references/iteration-prompt.txt`, substituting the absolute home directory with forward slashes as in pre-flight check 2. REF is the directory of the match. If nothing matches, stop and report a broken install: the references directory is missing, so re-run the installer. Substitute the resolved REF below and wherever later steps say REF.
|
|
41
|
+
|
|
42
|
+
Mode guard: when PLAN.md already exists at the project root, read the first word of its `## Mode` section body. If it reads Enhance, refuse the launch: Enhance mode was removed in v1.11.0, and its ledger ranks work by impact rather than severity, so its state files must not continue under the standard rules. Tell the user to archive those state files first - commit them and delete them, or keep them on a separate branch or checkout - and relaunch standard, or to run v1.10.0, the last release that carries the mode; then stop. The same guard keys on the launch mode both ways: a `--highs` launch over a PLAN.md whose first Mode word is not High-hunt refuses, because a standard ledger carries Mediums a hunt must not work and a standard Converged history a hunt does not extend, and a launch without `--highs` over a PLAN.md whose first Mode word is High-hunt refuses, because a hunt ledger and its Hunted history mean nothing to the standard closing rule; both refusals name the same archive step and stop. Any other mode proceeds and reuses the existing state files exactly as any relaunch does; a PLAN.md with no `## Mode` section is a user-authored plan and is treated as standard.
|
|
43
|
+
|
|
44
|
+
```bash
|
|
45
|
+
REF="<resolved references dir>"
|
|
46
|
+
PR="<PROJECT_ROOT>"
|
|
47
|
+
PLAN_TPL=plan-default.md
|
|
48
|
+
BACKLOG_TPL=backlog-default.md
|
|
49
|
+
# On a --highs launch the two templates are the hunt ones instead:
|
|
50
|
+
# PLAN_TPL=plan-highs.md
|
|
51
|
+
# BACKLOG_TPL=backlog-highs.md
|
|
52
|
+
[ -f "$PR/PLAN.md" ] || cp "$REF/$PLAN_TPL" "$PR/PLAN.md"
|
|
53
|
+
[ -f "$PR/BACKLOG.md" ] || cp "$REF/$BACKLOG_TPL" "$PR/BACKLOG.md"
|
|
54
|
+
[ -f "$PR/JOURNAL.md" ] || cp "$REF/journal-default.md" "$PR/JOURNAL.md"
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
On a `--highs` launch set PLAN_TPL to plan-highs.md and BACKLOG_TPL to backlog-highs.md as the commented lines show, before the copies run. The standard templates define Improvement mode (PLAN.md with the Goal, Operating envelope, Method, severity rubric, and Definition of done), the BACKLOG.md ledger sections (Now, Next, Later, Proposed, Settled classes, Declined, Converged), and the append-only JOURNAL.md heading grammar. The hunt templates define High-hunt mode (the same envelope, inventory, Verify command and rubric, a Definition of done that is one fresh audit finding no High) and a ledger of Highs only (Now, Proposed, Declined, Hunted); JOURNAL.md is shared, and its grammar carries the `hunted` status a hunt's closing entry takes. Edit the copies in the project to customize one run; edit the templates in references/ only to change every future run.
|
|
58
|
+
|
|
59
|
+
All work happens directly in the current project folder on the current branch. When the project is a git repository, every iteration ends in a local checkpoint commit made with git add -A and a message prefixed jeffy:. The checkpoint is the loop's revert and recovery unit; nothing is ever pushed and no branches are created - the user reviews with git log and squashes if they want one commit. Because the checkpoint uses git add -A, also do this during bootstrap: if the project is a git repository and `git check-ignore -q .claude/jeffy-loop.local.md` fails, append `.claude/jeffy-loop.local.md` to the project's .gitignore (creating the file if needed) so the transient session-scoped loop state can never be committed.
|
|
60
|
+
|
|
61
|
+
## Step 3: Launch the loop
|
|
62
|
+
|
|
63
|
+
Verify bound first: when PLAN.md exists at the project root and carries a `Command: ` line under `## Verify command`, look there for a labeled line reading `Verify duration: <N>s` (a measured figure earlier runs record). The launcher computes no bound of its own: it resolves the same chain the hook resolves when the state file carries no key - `verify_timeout_seconds`, else `Verify duration` x3 floored at 240s, else 240s, capped at 1740s because the installer registers the hook with an 1800s timeout. Found: resolve that chain against the measured seconds and add a `verify_timeout_seconds: <bound>` line to the frontmatter written below, so the Stop hook's converged-stop verify re-run inherits a bound sized to this suite across relaunches. Absent, with a `Command: ` line present: ask the user one question - roughly how long does the verify command run? Resolve the chain against their answer: if it lands on the floor, write no line, because the hook applies that same floor by default; if it lands above the floor, write `verify_timeout_seconds: <that resolved bound>`. This threshold is the chain's rather than a separate one, so it moved when the chain became the single statement of the bound: the replaced text wrote no line for any answer under four minutes, where the chain writes one for any answer above eighty seconds, always wider than the floor it replaces and never narrower. An answer of a minute still writes no line, because three times it lands on the floor. No PLAN.md yet, or no `Command: ` line: write no line and move on; the first run measures, records `Verify duration:` in PLAN.md, and every later launch inherits it from there.
|
|
64
|
+
|
|
65
|
+
Write the loop state file yourself, at the project root, with an absolute path. If focus text was given, sanitize it first: remove double quotes, backticks, dollar signs, and newlines, which would break the heredoc or the frontmatter, then substitute it below; with no focus, leave the value empty (the line stays, its value blank). Substitute PROJECT_ROOT, REF (resolved in Step 2), and N. `base_head` records the commit the run starts on, or `none` outside a repository; the Stop hook uses it to tell a genuine convergence ratchet, which re-declares a tree an earlier run certified, from a run that did the work itself and typed RATCHET over it. The heredoc terminator EOF must stay at column 0.
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
PR="<PROJECT_ROOT>"
|
|
69
|
+
REF="<resolved references dir>"
|
|
70
|
+
mkdir -p "$PR/.claude"
|
|
71
|
+
cat > "$PR/.claude/jeffy-loop.local.md" <<EOF
|
|
72
|
+
---
|
|
73
|
+
session_id: $CLAUDE_CODE_SESSION_ID
|
|
74
|
+
iteration: 1
|
|
75
|
+
max_iterations: <N>
|
|
76
|
+
prompt_path: $REF/<iteration-prompt-highs.txt on a --highs launch, else iteration-prompt.txt>
|
|
77
|
+
focus: <sanitized focus, or empty>
|
|
78
|
+
completion_promise: <JEFFY HUNT COMPLETE on a --highs launch, else JEFFY CONVERGED>
|
|
79
|
+
<mode: highs on a --highs launch, else omit this line entirely>
|
|
80
|
+
started_at: $(date -u +%Y-%m-%dT%H:%M:%SZ)
|
|
81
|
+
run_started_at: $(date +%s)
|
|
82
|
+
iteration_started_at: $(date +%s)
|
|
83
|
+
max_wall_clock_seconds: <seconds from --max-time, else 0>
|
|
84
|
+
max_iteration_seconds: <seconds from --max-iter-time, else 0>
|
|
85
|
+
max_context_growth: <multiple from --max-context, else 0>
|
|
86
|
+
sandboxed: <yes|no|unknown from hooks/lib/detect-sandbox.sh>
|
|
87
|
+
base_head: $(git -C "$PR" rev-parse HEAD 2>/dev/null || echo none)
|
|
88
|
+
<verify_timeout_seconds line when derived above, else omit this line entirely>
|
|
89
|
+
---
|
|
90
|
+
Jeffy loop state. Session-scoped and transient: the Stop hook deletes it when
|
|
91
|
+
the run ends. Cancel with /cancel-jeffy, or delete this file to end the loop.
|
|
92
|
+
EOF
|
|
93
|
+
grep -n "session_id\|iteration:" "$PR/.claude/jeffy-loop.local.md"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
**Blast radius.** Run `bash <REF>/../hooks/lib/detect-sandbox.sh` at launch
|
|
97
|
+
and record its answer as `sandboxed` in the state file. When it answers `no`,
|
|
98
|
+
the launch banner carries one further line: *"Not sandboxed: this run has
|
|
99
|
+
whatever access this shell has - credentials, SSH keys, tokens - and an
|
|
100
|
+
unattended agent usually runs with permissions relaxed. See SECURITY.md."*
|
|
101
|
+
On any other answer, say nothing. **It never blocks and never prompts.**
|
|
102
|
+
The loop does not widen its own mandate, and it has no business narrowing the
|
|
103
|
+
operator's either; what it owes them is one honest sentence about what is
|
|
104
|
+
reachable, said once, before the run rather than after it.
|
|
105
|
+
|
|
106
|
+
**Context pressure.** The engine re-feeds one session, so context accumulates
|
|
107
|
+
within a run, and the corpus prices that: later runs of long targets re-filed
|
|
108
|
+
findings earlier runs had already swept and scored clean. The hook measures it
|
|
109
|
+
from the transcript the harness names on its stdin - the thing itself, rather
|
|
110
|
+
than an iteration ordinal standing in for it - as a multiple of this run's own
|
|
111
|
+
first measurement, which calibrates to the project instead of to a constant
|
|
112
|
+
invented here. `--max-context <N>` sets `max_context_growth`; past N times the
|
|
113
|
+
opening size, the re-feed carries a CONTEXT PRESSURE note recommending the run
|
|
114
|
+
finish its current task and close, so the next one reads the state files with a
|
|
115
|
+
clean window. **It is advice and never a stop**: the closing rule governs, and
|
|
116
|
+
a pre-registered budget is never cut short by it. Off unless set, and the
|
|
117
|
+
measured growth is reported in the run state either way.
|
|
118
|
+
|
|
119
|
+
**Time ceilings.** A turn budget counts turns, and a turn is unbounded in
|
|
120
|
+
time, so the state file carries two optional ceilings the Stop hook enforces
|
|
121
|
+
at every turn end. Both are **0 (off) unless the launch sets them**, and that
|
|
122
|
+
default is deliberate: this engine publishes no figure it has not measured,
|
|
123
|
+
and the right ceiling belongs to the project rather than to the tool. For
|
|
124
|
+
reference when choosing one, rounds of ten iterations in the published corpus
|
|
125
|
+
run roughly 60 to 130 minutes. `--max-time <duration>` sets
|
|
126
|
+
`max_wall_clock_seconds` and ends the run out of time the way exhaustion ends
|
|
127
|
+
it out of turns; `--max-iter-time <duration>` sets `max_iteration_seconds`,
|
|
128
|
+
after which a long iteration draws an ITERATION OVERRUN note and two
|
|
129
|
+
consecutive overruns end the run. Accept `45m`, `2h`, `900s` or a bare
|
|
130
|
+
integer of seconds, and `0` as an explicit opt-out. Neither ceiling can cut a
|
|
131
|
+
turn short - the hook fires after it - and neither preempts the closing
|
|
132
|
+
extension or a converged declaration. Whether a ceiling is set or not, the
|
|
133
|
+
run state line reports elapsed wall time every iteration, so a run can see
|
|
134
|
+
its own clock.
|
|
135
|
+
|
|
136
|
+
Verify the write: the grep output must show the current session id and `iteration: 1`. If the session id line is empty or wrong, delete the file, report the failure, and stop. The state file carries the mode as three keys the Stop hook reads: `mode: highs` (absent on a standard launch, where every hook path is the one it always was), the promise phrase, and the prompt path. The iteration prompt itself is a single line stored at `$REF/iteration-prompt.txt`, or `$REF/iteration-prompt-highs.txt` for a hunt, which is the standard prompt with the convergence machinery deleted so every shared sentence stays byte-identical; the hook reads it from disk at every turn end and JSON-encodes it with jq, so its content never needs to be injected through the shell. Never edit iteration-prompt.txt casually: the loop's journal grammar, checkpoint discipline, run report, and closing rule all live in it, and it must stay a single line.
|
|
137
|
+
|
|
138
|
+
Then announce the launch in one line - Jeffy v<version>, N iterations, the mode and its promise phrase (Improvement with JEFFY CONVERGED, or High-hunt with JEFFY HUNT COMPLETE), the focus if one was given, and the absolute path of the verify wrapper, `<REF>/../hooks/lib/quiet-verify.sh`, because the iteration prompt names it by its repository-relative path and a target project has no such path, and the absolute path of the Stop hook, `<REF>/../hooks/stop-hook.sh`, because the iteration prompt runs it in lint mode before every gate invocation - reading the version from the installed hook with `sed -n 's/^JEFFY_VERSION="\(.*\)"/\1/p' <home>/.claude/skills/jeffy/hooks/stop-hook.sh`, so every run's transcript opens by naming the engine version a bug report needs.
|
|
139
|
+
|
|
140
|
+
## Step 4: Begin iteration 1
|
|
141
|
+
|
|
142
|
+
Read "<REF>/iteration-prompt.txt" now, or "<REF>/iteration-prompt-highs.txt" on a `--highs` launch (REF as resolved in Step 2), its only in-context load, and immediately start following it yourself. Do not wait for input. Every later turn end triggers the Stop hook, which re-feeds the same prompt until N iterations complete, the promise fires, or the state file is deleted.
|
|
143
|
+
|
|
144
|
+
## Operational notes
|
|
145
|
+
|
|
146
|
+
- Cancel: run /cancel-jeffy (or delete `.claude/jeffy-loop.local.md`).
|
|
147
|
+
- Permission prompts pause the loop. Unattended runs need test and file tools allowlisted, or acceptEdits mode. Never allowlist push or force operations for a loop.
|
|
148
|
+
- A user message sent mid-loop gets answered and then the Stop hook re-feeds the iteration prompt, so a side question flows straight into the next iteration. The turn it consumed counts against the iteration budget, because the budget counts turns.
|
|
149
|
+
- Prefer several small runs over one large budget. The hook re-feeds the same session, so context accumulates across iterations within a run; the state files persist between runs and convergence is sticky, so two runs of 5 beat one run of 10. The clean context is the whole point, and it only arrives with a new session: relaunching /jeffy in the session that just finished a run keeps every accumulated token and forfeits the benefit entirely. Close the session and start a new one in the same directory; the state files on disk carry the run forward, nothing is lost.
|
|
150
|
+
- Edit PLAN.md or BACKLOG.md between iterations, not while one is running: a mid-iteration edit can collide with the loop's own in-flight edit. The Proposed section of BACKLOG.md is the designed channel for decisions.
|
|
151
|
+
- Checkpoints: every iteration ends in a local commit prefixed jeffy:. Review a run with git log --oneline, revert a bad iteration by reverting its checkpoint, and squash the run into one commit if you want tidy history. Nothing is ever pushed.
|
|
152
|
+
- One Jeffy loop per project at a time. The state file is transient; a crashed or closed session can leave it behind, which pre-flight check 4 handles. Orphans can also hide in subdirectories if a session was ever started there; they are inert for the hook, which anchors at the project root, but confuse relative-path checks, so always inspect state files with absolute paths.
|
|
153
|
+
- Git hygiene: `.claude/jeffy-loop.local.md` is transient, session-scoped state that must never be committed. Bootstrap appends it to the target project's `.gitignore` automatically, because the checkpoint's git add -A would otherwise sweep it in. The three state files (PLAN.md, BACKLOG.md, JOURNAL.md) are meant to persist between runs and are committed by the checkpoints; that is intentional, they are the loop's memory.
|
|
154
|
+
- If a loop ever dies silently mid-run (turn ends, no re-feed, state file frozen at its last iteration), the likely causes are: the hook was installed or registered after this session started (start a fresh session and relaunch); or the jeffy skills folder was moved or removed, so the state file's prompt_path went stale - the hook then ends the loop with a message to stderr, and re-running /jeffy relaunches with the new path.
|
|
155
|
+
- When a run ends, the loop closes with a run report; JOURNAL.md and the checkpoint commits in git log hold the full record.
|