jeffy-loop 1.26.0__tar.gz → 1.26.2__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.26.0 → jeffy_loop-1.26.2}/PKG-INFO +9 -5
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/README.md +8 -4
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/pyproject.toml +1 -1
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/SKILL.md +1 -1
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/hooks/lib/measure-surface.sh +9 -3
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/hooks/stop-hook.sh +1 -1
- jeffy_loop-1.26.2/skills/jeffy/references/iteration-prompt-highs.txt +1 -0
- jeffy_loop-1.26.2/skills/jeffy/references/iteration-prompt.txt +1 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/references/plan-default.md +3 -1
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/references/plan-highs.md +3 -1
- jeffy_loop-1.26.2/src/jeffy_loop/__init__.py +1 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/src/jeffy_loop/cli.py +54 -1
- jeffy_loop-1.26.0/skills/jeffy/references/iteration-prompt-highs.txt +0 -1
- jeffy_loop-1.26.0/skills/jeffy/references/iteration-prompt.txt +0 -1
- jeffy_loop-1.26.0/src/jeffy_loop/__init__.py +0 -1
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/.gitignore +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/LICENSE +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/hatch_build.py +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/cancel-jeffy/SKILL.md +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/hooks/lib/check-claims.sh +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/hooks/lib/detect-sandbox.sh +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/hooks/lib/quiet-verify.sh +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/hooks/lib/run-probe.sh +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/references/backlog-default.md +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/references/backlog-highs.md +0 -0
- {jeffy_loop-1.26.0 → jeffy_loop-1.26.2}/skills/jeffy/references/journal-default.md +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: jeffy-loop
|
|
3
|
-
Version: 1.26.
|
|
3
|
+
Version: 1.26.2
|
|
4
4
|
Summary: Jeffy Loop is an autonomous software-engineering control system that forces an LLM to continuously audit, act, verify, checkpoint, and prove convergence.
|
|
5
5
|
Project-URL: Repository, https://github.com/lenamonj/jeffy-loop
|
|
6
6
|
License-Expression: MIT
|
|
@@ -25,7 +25,7 @@ Description-Content-Type: text/markdown
|
|
|
25
25
|
|
|
26
26
|
**[Quick Install](#quick-install)** · **[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)** · **[STE100 description](https://github.com/lenamonj/jeffy-loop/raw/main/Jeffy-Loop-STE100.pdf)**
|
|
27
27
|
|
|
28
|
-
## A Claude Code loop that finds bugs in your repo, fixes them, and proves every fix before it commits. Maintainers have merged <!-- count:merged -->
|
|
28
|
+
## A Claude Code loop that finds bugs in your repo, fixes them, and proves every fix before it commits. Maintainers have merged <!-- count:merged -->67<!-- /count --> of its patches.
|
|
29
29
|
|
|
30
30
|
</div>
|
|
31
31
|
|
|
@@ -33,7 +33,7 @@ The model writes the patch. The harness around it decides whether that patch can
|
|
|
33
33
|
|
|
34
34
|
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.
|
|
35
35
|
|
|
36
|
-
There is a real-world test for that. Maintainers with no stake in this project have merged <!-- count:merged -->
|
|
36
|
+
There is a real-world test for that. Maintainers with no stake in this project have merged <!-- count:merged -->67<!-- /count --> 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.](#independent-validation---the-receipts)
|
|
37
37
|
|
|
38
38
|
<div align="center">
|
|
39
39
|
|
|
@@ -50,7 +50,7 @@ Jeffy was run against <!-- count:tested -->132<!-- /count --> open-source projec
|
|
|
50
50
|
|
|
51
51
|
| Projects tested | Converged | Failed | PRs merged | PRs open | Issues filed |
|
|
52
52
|
|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
53
|
-
| **<!-- count:tested -->132<!-- /count -->** | **<!-- count:converged -->103<!-- /count -->** | **<!-- count:failed -->28<!-- /count -->** | **<!-- count:merged -->
|
|
53
|
+
| **<!-- count:tested -->132<!-- /count -->** | **<!-- count:converged -->103<!-- /count -->** | **<!-- count:failed -->28<!-- /count -->** | **<!-- count:merged -->67<!-- /count -->** | **<!-- count:prs-open -->27<!-- /count -->** | **<!-- count:issues -->4<!-- /count -->** |
|
|
54
54
|
|
|
55
55
|
**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 <!-- count:converged -->103<!-- /count --> projects across <!-- count:languages -->13<!-- /count --> 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.
|
|
56
56
|
|
|
@@ -368,10 +368,14 @@ Each finding below was accepted upstream by the project's own maintainers.
|
|
|
368
368
|
<td>12 minutes</td>
|
|
369
369
|
</tr>
|
|
370
370
|
<tr>
|
|
371
|
-
<td><img src="https://github.com/canonical.png" width="20" height="20" alt="" align="absmiddle"> Canonical (Ubuntu)</td>
|
|
371
|
+
<td rowspan="2"><img src="https://github.com/canonical.png" width="20" height="20" alt="" align="absmiddle"> Canonical (Ubuntu)</td>
|
|
372
372
|
<td><a href="https://github.com/canonical/pebble/pull/938">pebble #938</a><br>With more than one service or check, the metrics endpoint repeated each <code># HELP</code> and <code># TYPE</code> family and the Prometheus parser rejected the whole response</td>
|
|
373
373
|
<td>16 days</td>
|
|
374
374
|
</tr>
|
|
375
|
+
<tr>
|
|
376
|
+
<td><a href="https://github.com/canonical/pebble/pull/937">pebble #937</a><br>Appending a layer that parsed but failed plan validation left the daemon's live layer list changed, because <code>slices.Insert</code> shifted the existing slice in place before validation ran; the insert now goes into a copy</td>
|
|
377
|
+
<td>43 days</td>
|
|
378
|
+
</tr>
|
|
375
379
|
<tr>
|
|
376
380
|
<td><img src="https://github.com/SethMMorton.png" width="20" height="20" alt="" align="absmiddle"> natsort</td>
|
|
377
381
|
<td><a href="https://github.com/SethMMorton/natsort/pull/196">natsort #196</a><br>The locale sentinel meant to sort last was three ASCII bytes, so PyICU keys sorted after it (19 million PyPI downloads a month)</td>
|
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
|
|
14
14
|
**[Quick Install](#quick-install)** · **[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)** · **[STE100 description](https://github.com/lenamonj/jeffy-loop/raw/main/Jeffy-Loop-STE100.pdf)**
|
|
15
15
|
|
|
16
|
-
## A Claude Code loop that finds bugs in your repo, fixes them, and proves every fix before it commits. Maintainers have merged <!-- count:merged -->
|
|
16
|
+
## A Claude Code loop that finds bugs in your repo, fixes them, and proves every fix before it commits. Maintainers have merged <!-- count:merged -->67<!-- /count --> of its patches.
|
|
17
17
|
|
|
18
18
|
</div>
|
|
19
19
|
|
|
@@ -21,7 +21,7 @@ The model writes the patch. The harness around it decides whether that patch can
|
|
|
21
21
|
|
|
22
22
|
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.
|
|
23
23
|
|
|
24
|
-
There is a real-world test for that. Maintainers with no stake in this project have merged <!-- count:merged -->
|
|
24
|
+
There is a real-world test for that. Maintainers with no stake in this project have merged <!-- count:merged -->67<!-- /count --> 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.](#independent-validation---the-receipts)
|
|
25
25
|
|
|
26
26
|
<div align="center">
|
|
27
27
|
|
|
@@ -38,7 +38,7 @@ Jeffy was run against <!-- count:tested -->132<!-- /count --> open-source projec
|
|
|
38
38
|
|
|
39
39
|
| Projects tested | Converged | Failed | PRs merged | PRs open | Issues filed |
|
|
40
40
|
|:---:|:---:|:---:|:---:|:---:|:---:|
|
|
41
|
-
| **<!-- count:tested -->132<!-- /count -->** | **<!-- count:converged -->103<!-- /count -->** | **<!-- count:failed -->28<!-- /count -->** | **<!-- count:merged -->
|
|
41
|
+
| **<!-- count:tested -->132<!-- /count -->** | **<!-- count:converged -->103<!-- /count -->** | **<!-- count:failed -->28<!-- /count -->** | **<!-- count:merged -->67<!-- /count -->** | **<!-- count:prs-open -->27<!-- /count -->** | **<!-- count:issues -->4<!-- /count -->** |
|
|
42
42
|
|
|
43
43
|
**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 <!-- count:converged -->103<!-- /count --> projects across <!-- count:languages -->13<!-- /count --> 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.
|
|
44
44
|
|
|
@@ -356,10 +356,14 @@ Each finding below was accepted upstream by the project's own maintainers.
|
|
|
356
356
|
<td>12 minutes</td>
|
|
357
357
|
</tr>
|
|
358
358
|
<tr>
|
|
359
|
-
<td><img src="https://github.com/canonical.png" width="20" height="20" alt="" align="absmiddle"> Canonical (Ubuntu)</td>
|
|
359
|
+
<td rowspan="2"><img src="https://github.com/canonical.png" width="20" height="20" alt="" align="absmiddle"> Canonical (Ubuntu)</td>
|
|
360
360
|
<td><a href="https://github.com/canonical/pebble/pull/938">pebble #938</a><br>With more than one service or check, the metrics endpoint repeated each <code># HELP</code> and <code># TYPE</code> family and the Prometheus parser rejected the whole response</td>
|
|
361
361
|
<td>16 days</td>
|
|
362
362
|
</tr>
|
|
363
|
+
<tr>
|
|
364
|
+
<td><a href="https://github.com/canonical/pebble/pull/937">pebble #937</a><br>Appending a layer that parsed but failed plan validation left the daemon's live layer list changed, because <code>slices.Insert</code> shifted the existing slice in place before validation ran; the insert now goes into a copy</td>
|
|
365
|
+
<td>43 days</td>
|
|
366
|
+
</tr>
|
|
363
367
|
<tr>
|
|
364
368
|
<td><img src="https://github.com/SethMMorton.png" width="20" height="20" alt="" align="absmiddle"> natsort</td>
|
|
365
369
|
<td><a href="https://github.com/SethMMorton/natsort/pull/196">natsort #196</a><br>The locale sentinel meant to sort last was three ASCII bytes, so PyICU keys sorted after it (19 million PyPI downloads a month)</td>
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "jeffy-loop"
|
|
7
|
-
version = "1.26.
|
|
7
|
+
version = "1.26.2"
|
|
8
8
|
description = "Jeffy Loop is an autonomous software-engineering control system that forces an LLM to continuously audit, act, verify, checkpoint, and prove convergence."
|
|
9
9
|
dynamic = ["readme"]
|
|
10
10
|
license = "MIT"
|
|
@@ -27,7 +27,7 @@ If any check fails, stop and report the exact fix needed. Do no other work first
|
|
|
27
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, deleting `.claude/jeffy-loop.local.md.refused` beside it when that sidecar exists; otherwise stop. Never delete the file without explicit confirmation.
|
|
28
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
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. That refusal is the only one the pipe earns at launch. The engine runs the Command under `pipefail`, so a failing stage anywhere in it fails the gate, unless the Command contains a stage that closes the pipe early (`grep
|
|
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. That refusal is the only one the pipe earns at launch. The engine runs the Command under `pipefail`, so a failing stage anywhere in it fails the gate, unless the Command contains a stage that closes the pipe early (`grep`, `egrep` or `fgrep` carrying `-q`, `-m`, `--quiet`, `--silent` or `--max-count`, a `head`, or an `awk` that reaches `exit`), where it runs as written; an early closer the engine does not recognise (`sed -n 1q`, `/usr/bin/head`, a `read`) surfaces as exit 141 and the refusal says SIGPIPE, so when the payload pipes into one, say so in one line and continue; `set +o pipefail` at the head of the Command opts out, and the Command line is the run's own - the shell does not freeze 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 at launch: the hook skips its own check on a missing PLAN.md and on `none`, refuses a declaration over a section that carries no `Command:` line or an empty one (the ungated form is `Command: none`), 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
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
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
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.
|
|
@@ -53,9 +53,15 @@ done
|
|
|
53
53
|
git -C "$root" rev-parse --is-inside-work-tree >/dev/null 2>&1 || {
|
|
54
54
|
echo "measure-surface: $root is not a git work tree; nothing measured" >&2; exit 2; }
|
|
55
55
|
|
|
56
|
-
# Source extensions, one alternation. A
|
|
57
|
-
#
|
|
58
|
-
|
|
56
|
+
# Source extensions, one alternation. A language missing here is invisible
|
|
57
|
+
# rather than undercounted, and that is the unsafe direction, not the safe
|
|
58
|
+
# one: refusing is the only thing this file does, so a tree it cannot see
|
|
59
|
+
# passes every ceiling. Shell was missing until W1, which left this engine's
|
|
60
|
+
# own repository - the largest files in it are a shell hook and a shell
|
|
61
|
+
# harness - reporting a seventh of its real size at every launch. The list is
|
|
62
|
+
# still a floor; a tree in a language not named here measures low, so add the
|
|
63
|
+
# extension rather than rely on that.
|
|
64
|
+
ext='\.(bash|c|cc|clj|cljs|cpp|cs|cxx|dart|erl|ex|exs|fs|go|groovy|h|hh|hpp|hs|java|jl|js|jsx|kt|kts|lua|m|ml|mli|mm|nim|php|pl|pm|ps1|psm1|py|r|R|rb|rs|scala|sh|sql|svelte|swift|ts|tsx|vue|zig)$'
|
|
59
65
|
# Directories that conventionally hold code the project did not write or will
|
|
60
66
|
# regenerate; anchored at a path segment so `vendored_api.go` still counts.
|
|
61
67
|
skip='(^|/)(vendor|vendored|node_modules|third_party|thirdparty|external|_vendor)/'
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
# directory Claude Code was started in, so Bash-tool cwd drift mid-iteration
|
|
11
11
|
# cannot kill the loop.
|
|
12
12
|
set -u
|
|
13
|
-
JEFFY_VERSION="1.26.
|
|
13
|
+
JEFFY_VERSION="1.26.2"
|
|
14
14
|
|
|
15
15
|
# 1.21.0 (P1-70): `stop-hook.sh --lint [project root]` derives every check
|
|
16
16
|
# the declaration path enforces and prints the first refusal, or clean,
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Read the frontmatter of .claude/jeffy-loop.local.md at the project root for iteration and max_iterations: you are on iteration i of N and every journal heading needs i/N. Read PLAN.md and BACKLOG.md in full and the last 3 entries of JOURNAL.md. Salvage first: when the project is a git repository and the working tree is dirty at the start of this iteration - ignoring any path under .jeffy/metrics/, which the Stop hook writes after every checkpoint and the next checkpoint sweeps up, so it is never salvage - the previous iteration was interrupted or a human edited mid-flight; commit everything with the message jeffy: salvage before iter i, record a SALVAGE journal entry, and continue; never reset or discard work. If the Operating envelope table in PLAN.md is unfilled, enumerate the project's real input surfaces and fill it before filing any finding; if the Verify command in PLAN.md is unfilled, fill it during the first audit with the project's real gate, or none with a one-line reason, and fill its Oracle class, Environment fingerprint and Verify count lines in that same audit - Verify count is the total the wrapper's green summary reports and the only place that total is written down, so the Oracle class stays prose and carries no count -: Oracle class names what the command actually grades in your own words - unit tests, a conformance corpus, a differential comparison against a reference implementation, a build only - and Environment fingerprint names the platform, the toolchain versions, and every test target this platform excludes, that exclusion list derived by a command enumerating the build constraints, platform guards and skip markers in the test tree, with the line naming that command and never a bare assertion that nothing is excluded, because the failure this catches is an exclusion nobody looked for; the declaring iteration re-reads both lines, and a test asset the fingerprint says the command cannot reach is one no journal entry may claim was green. If the Surface inventory in PLAN.md is unfilled, fill it during the first audit before filing any finding: one checkbox row per public module or entry-point group until the whole public surface is listed, then probe every row at least shallowly before any deep dive, so the worst defect in the project appears in the first filing rather than the sixth; a hunt never sweeps a row and never flips one - every row stays [ ], the inventory is the map every audit walks, and the close does not require a swept row. If BACKLOG.md has no open tasks, audit: one full fresh-evidence audit of the project per the Method and Operating envelope in PLAN.md, walking every Surface inventory row at least shallowly - probing a surface that computes values requires a known-answer or invariant check per function family, never only run-without-crash probes, because wrong numbers returned without complaint survive every liveness probe, and a battery that certifies content records at creation the discriminating input or pre-fix state it was once observed failing on, because an instrument that has never been seen to fail reads exactly like an instrument that passed, and requires every documented parameter exercised at two or more values that must change the output, boundary and negative sides included - a documented parameter whose value changes nothing is a finding, never a pass - and a dimension scored None claims only the rows the audit actually walked, never an unexamined remainder; file only Highs, worst first, as one-line items with an acceptance check each, and record every Medium or Low the audit notices in the AUDIT entry on one line beginning Noted, not filed: with one clause per finding, never on the ledger and never worked; append an AUDIT journal entry with the scores and, when the audit filed a High, run the end-of-iteration steps and end the turn; when it filed none, the hunt closes in this same iteration per the closing rule below. Otherwise execute exactly one task, the top open High, never batch, because leaving a known High unfixed to read more code is indefensible; when this is the final iteration, i equals N, prefer a WRAPUP entry that tidies BACKLOG.md and writes a handoff over starting a task that cannot finish. Verify the task's acceptance check, at most 3 fix attempts, else mark it [b] with a one line reason; when the check must first run against unfixed code, copy the fixed files aside and restore them after, and never git checkout a path carrying uncommitted work, because that deletes the fix being proved. Verify gate: after the acceptance check, run the Verify command from PLAN.md unless it is none, and run it only through the installed skills/jeffy/hooks/lib/quiet-verify.sh - the launch banner names its absolute path, and it lives beside the Stop hook under the jeffy skill, never inside the project - passing the path to PLAN.md - QUIET-VERIFY-ONLY: never invoke the raw Verify command yourself, because a green suite floods this window with output that says nothing and the wrapper is what bounds it; a green verify prints nothing to stdout by design, so do not re-run it to see the passing output, and never quote a count you did not receive - the wrapper puts the run duration, the oracle class and the line matching the Verify summary pattern on its green stderr line, and that is the figure a journal entry may quote; if it fails and it passed at the last checkpoint, this iteration broke the project: when the project is a git repository revert the working tree to the last jeffy checkpoint, mark the task [b] with the failure output, and record it in the journal entry; without git there is no checkpoint to revert to, so undo this iteration's edits directly, mark the task [b], and record it. One exception: when the newly red test was previously green only because of the defect this task fixes - a pre-existing fault newly exposed rather than introduced - repair it within this iteration instead of reverting, but only with differential evidence recorded in the journal that the task's change altered no previously-passing output; without that evidence, revert. Never pipe the Verify command or a test run through head or tail: in your own shell a pipeline reports the last command's status, so a red suite reads as green - redirect the output to a file and check the exit status. The engine runs the PLAN.md Command under pipefail unless it contains a stage that closes the pipe early (`grep`, `egrep` or `fgrep` carrying `-q`, `-m`, `--quiet`, `--silent` or `--max-count`, a `head`, or an `awk` that reaches `exit`), where it runs as written; an early closer the engine does not recognise surfaces as exit 141 and the refusal says SIGPIPE, and set +o pipefail at the head of the Command opts out. Exploratory output discipline: a command run to look rather than to prove - a recursive grep, a find, test discovery, a build or dependency log - whose output may run past about forty lines is redirected to a file under .jeffy/scratch/ and read back with grep, head or a count, never printed whole into this window; .jeffy/scratch/ is ignored by git at bootstrap and never committed, so a claim cites the command that wrote the file and never the file. Severity discipline: severity is the consequence a user of the shipped product meets, so a task line whose class is test|dev-tooling is a Low whatever else the rubric suggests, because a user never runs tests or developer tooling, and a task line whose class is docs|build-ci states Consequence: what a user of the shipped product meets, before its Acceptance, or it is a Low as well - the Stop hook refuses a declaration over a line this run filed that breaks either rule, and a run whose PLAN.md predates that ceiling is told it here rather than at the declaration it would otherwise lose; a finding exercised only by out-of-envelope input is Low at most; a finding filed below the severity the rubric suggests carries the rationale in its filing entry, and a downgrade without one is a violation; a downgrade rationale cites the envelope or rubric line it rests on, and a rationale contradicted by a written line in the state files is void - arguing the input is exotic on a surface the Operating envelope classifies adversarial is the canonical contradicted rationale; a filing scored below an earlier filing of the same class - the same settled-class line or the same id family - states in one line what distinguishes it, and the closing audit re-reads those lines the way it re-reads Declined derivations; the third finding sharing one root cause replaces instance patching with one structural task or one Proposed decision; envelope changes and audit escalations go under Proposed, and Proposed items never block the close and are never worked without user approval. Backlog discipline: BACKLOG.md is a ledger, not a narrative; when a task completes, delete its line from BACKLOG.md and record one closing line in the journal entry; never write prose sections, run context, or DONE annotations into BACKLOG.md; add a High discovered while working as a one-line item; never replenish with a partial audit, because the only audit a hunt runs is the full fresh one it runs when no High is open. A finding declined records on its Declined line the command or measurement that establishes the premise, written as Derivation: <command>, except the priced reason cost: exceeds one iteration, which is policy rather than premise; a Declined premise is a standing claim the declaration rests on, so the closing iteration re-runs every recorded Derivation the way it re-reads the Oracle class and Environment fingerprint, and a premise that no longer holds moves the entry back to the ledger before the close. Append a JOURNAL.md entry with the heading grammar ## iter i/N | run-id | date | task-id or AUDIT or WRAPUP or SALVAGE or ROTATION | status, where run-id is the first 8 characters of the session id followed by a hyphen and the HHMMSS of started_at from the loop state frontmatter, so two runs in one session are told apart, and status is one of done, blocked, audit, hunted, salvage, rotation - an AUDIT entry takes status audit, or hunted when that same iteration closes the hunt, SALVAGE entries take status salvage, ROTATION entries take status rotation - and body fields Task, Changed, Checkpoint, Verification, Learnings, Next, in that order; each iteration writes one primary entry, and SALVAGE and ROTATION entries are additional. If JOURNAL.md exceeds 500 lines, move all but the last 10 entries to the end of JOURNAL-archive.md, appending to whatever that file already holds and never overwriting it, splitting entries only on lines that begin ## iter followed by a digit so the heading grammar example in the preamble is never counted as an entry or moved - the archive accumulates across every rotation and every run, and the stop hook rejects an archive whose entry count fell - and record the rotation as a ROTATION entry. Lessons: when a Learnings line states an operational rule future iterations must obey - a build quirk, a command that must or must not be used, a mistake made twice - also append that rule as one line under ## Lessons in PLAN.md, keeping that section brief and free of status or task state. Checkpoint: at the end of every iteration, when the project is a git repository, run git add -A then git commit -q with the message jeffy: iter i/N task-id status, then evaluate the stall check below, record the new commit hash as Checkpoint in the current entry - completing the entry, not rewriting it - together with the stall note when one is due, and commit that small bookkeeping edit with the message jeffy: iter i/N bookkeeping, so the next iteration starts with a clean tree and salvage does not misfire; if the commit fails, record Checkpoint: none with the reason and continue; never push and never create branches. Stall check: if this iteration changed no file other than PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, a path under .jeffy/, or the harness-written .claude/jeffy-loop.local.md and .claude/settings.local.json, and no BACKLOG.md item changed state - an item changed state when a task line under Now, Next, or Later was added, removed, edited, or moved between sections, which is the same definition the stop hook's own ledger signal is computed from, so prose edits elsewhere in the ledger are not progress to either of you - and no Surface inventory row changed state, because a sweep iteration's row flips are progress to both of you - say so in the journal entry; if the previous primary entry already says the same, that is a hard blocker - except that the ceremony entries are not stalls and never form that pair: an AUDIT that files nothing, a WRAPUP, and an iteration whose entry honestly records blocked, for at most three consecutive iterations, after which they count like any other iteration; that is the same bounded exemption the stop hook applies before it ends a run as stalled. Change discipline: before changing shared or public code, read its callers and the tests that pin it, and state in the journal entry what contract the change preserves; when a change alters a public function's behavior, signature, or accepted inputs, the same iteration updates its documentation - a fix that leaves its own module contradicting its docs or its siblings is not done. A prose claim that generalises over a set of sites - the error names X, no temporary is left behind, reachable only when Y - ships with the enumeration of that set in the same iteration and its executing check drives every enumerated site; where the class is about failures, build the enumeration by provoking a failure at every step of the operation, never by grepping the source for the calls it makes, and if the enumeration cannot be produced this iteration, narrow the sentence to the single site actually verified. A fix re-executes the claims it invalidates: before the checkpoint, re-run the checks behind every prose claim and stated number in the state files and in the modules this diff touches and their direct siblings; never write a line number into a state file - state the shape the enumerating command returns. Edit only the files the task requires plus PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, .jeffy/probes/ and .jeffy/scratch/, all inside the current project folder; .gitignore only at bootstrap, and never .jeffy/metrics/, which the Stop hook owns. Never rewrite past journal entries, never write secrets into state files, use hyphens not dashes in file content. On a hard blocker - a missing dependency, a decision only the user can make, or a second consecutive no-progress iteration - append a journal entry naming the exact blocker, delete .claude/jeffy-loop.local.md to end the loop, and close with the run report. Run report: whenever a turn ends the run - the close of the hunt, the final iteration, or a hard blocker - finish your reply to the user with a run report: iterations used and how they were spent, the Highs closed this run, every Noted, not filed: line the run's audits recorded, the files changed across the run via git diff --stat against the commit that preceded this run's first checkpoint when the project is a git repository, Highs still open or blocked with their reasons, Proposed items awaiting a decision, the final Verify status, and a pointer to JOURNAL.md and git log for the full record. Closing rule: the hunt is complete only when the Definition of done in PLAN.md is genuinely and verifiably true: a full fresh-evidence audit this iteration scored zero High in-envelope, zero open High remains in BACKLOG.md, the only commits since the previous audit are the completed fixes for the Highs it filed plus loop state file edits, the Verify command is green this iteration, and a line reading Hunted: <full commit hash> - <date> - <k> Highs closed, where k counts the Highs this run closed, is appended under ## Hunted in BACKLOG.md. The audit that finds no High and the close share one iteration: that AUDIT entry takes status hunted, and its Checkpoint field names this iteration's checkpoint commit, the Hunted line names that same commit and rides the bookkeeping edit that records it, because the Stop hook refuses a close whose last AUDIT entry carries no resolvable Checkpoint or after whose checkpoint a product path changed, and a close whose last AUDIT entry does not carry status hunted or is followed by a later entry of this run. A Hunted line is never edited, and the hash it names has to stay reachable from HEAD: the only legal answer to a history rewrite that orphaned the certified commit is an appended line reading Hunted: <new hash> - <date> - <k> Highs closed (repoints <old hash>, tree unchanged), legal only when both commits carry the same tree, with the superseded line left standing and a dated ## Note in JOURNAL.md disclosing the rewrite and both hashes. Before the close, in the same iteration, bring the standing claims current: re-run every recorded Declined Derivation, run skills/jeffy/hooks/lib/check-claims.sh and resolve every MISMATCH (it executes the battery claims files and PLAN.md's Stated counts table alike; a count a governance file states is written as returns <count> and joins that table in the commit that states it, and a stated count the table does not carry refuses the close) (a battery README states a measurement only as the <value> of a line `expect <value> :: <command>` in that battery's claims file, or the file holds the single line `none`; a battery this run writes or whose README it edits carries one, which the hook checks by form), keep the Verify count cell equal to the total the wrapper's green line reports (the hook says VERIFY COUNT at the checkpoint when it is not, and refuses the close on it), and re-read the Oracle class and Environment fingerprint. Last, run the installed Stop hook in lint mode - bash <absolute path of stop-hook.sh> --lint <project root>, the hook path the launch banner names beside the verify wrapper - and repair whatever it prints before the checkpoint: it derives every check the hook enforces at the close and executes nothing, a line it prints is a refusal, and the checkpoint and the Hunted line, which are written after it runs, are the two things it reports as pending rather than refuses. Battery ownership: after your change, list the paths your diff touched, run every battery under .jeffy/probes/ whose paths file matches one of them in this same iteration, each through the installed run-probe.sh beside the verify wrapper, which bounds an instrument's memory and wall time so a runaway probe dies alone - a probe killed at its ceiling is an instrument finding about the probe, never the end of the iteration - and treat a battery failure as a Verify failure; when your fix changes behaviour a battery pins, update that battery in this iteration too. To close the hunt, and only then, output the run report followed by the exact phrase JEFFY HUNT COMPLETE wrapped in promise XML tags; a run that ends out of budget or on a blocker ends with the run report alone, because the promise is the declaration and never the way a turn or a run ends, and the tagged form is never written in any other context, including the journal.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Read the frontmatter of .claude/jeffy-loop.local.md at the project root for iteration and max_iterations: you are on iteration i of N and every journal heading needs i/N. Read PLAN.md and BACKLOG.md in full and the last 3 entries of JOURNAL.md. Salvage first: when the project is a git repository and the working tree is dirty at the start of this iteration - ignoring any path under .jeffy/metrics/, which the Stop hook writes after every checkpoint and the next checkpoint sweeps up, so it is never salvage - the previous iteration was interrupted or a human edited mid-flight; commit everything with the message jeffy: salvage before iter i, record a SALVAGE journal entry, and continue; never reset or discard work. Ratchet next: if the latest Converged line in BACKLOG.md names a commit hash, BACKLOG.md lists no open task in Now, Next, or Later, this run carries no focus directive, and every path changed since that hash, per git status --porcelain and git diff --name-only against it, is only PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, a path under .jeffy/, or the harness-written .claude/jeffy-loop.local.md and .claude/settings.local.json, append a RATCHET journal entry recording that verification and end the run converged per the closing rule below; a seeded backlog, a focus directive, or any other changed path means the ratchet does not apply and the run proceeds normally. A Converged line is never edited, and the hash it names has to stay reachable from HEAD: the only legal answer to a history rewrite that orphaned the certified commit is an appended line reading Converged: <new hash> - <date> (repoints <old hash>, tree unchanged), legal only when both commits carry the same tree, with the superseded line left standing and a dated ## Note in JOURNAL.md disclosing the rewrite and both hashes. If the Operating envelope table in PLAN.md is unfilled, enumerate the project's real input surfaces and fill it before filing any finding, and in the same first audit enumerate the tree's artifact-producing channels by command, never by recall - packaging manifests such as Cargo.toml, package.json with its files list or .npmignore, MANIFEST.in or pyproject sdist config, gemspec, nuspec, plus container builds and release or publish workflows that archive the tree - and verify per channel that PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md and .jeffy/ cannot reach the published artifact, filing what fails or cannot be verified at Medium with its Consequence stated, because a published artifact carrying the loop's state is something a user of the shipped product meets; if the Verify command in PLAN.md is unfilled, fill it during the first audit with the project's real gate, or none with a one-line reason, and fill its Oracle class, Environment fingerprint and Verify count lines in that same audit - Verify count is the total the wrapper's green summary reports and the only place that total is written down, so the Oracle class stays prose and carries no count -: Oracle class names what the command actually grades in your own words - unit tests, a conformance corpus, a differential comparison against a reference implementation, a build only - and Environment fingerprint names the platform, the toolchain versions, and every test target this platform excludes, that exclusion list derived by a command enumerating the build constraints, platform guards and skip markers in the test tree, with the line naming that command and never a bare assertion that nothing is excluded, because the failure this catches is an exclusion nobody looked for; the declaring iteration re-reads both lines, and a test asset the fingerprint says the command cannot reach is one no journal entry may claim was green. If the Surface inventory in PLAN.md is unfilled, fill it during the first audit before filing any finding: one checkbox row per public module or entry-point group until the whole public surface is listed, then probe every row at least shallowly before any deep dive, so the worst defect in the project appears in the first filing rather than the sixth; a row flips to [x] only with the commit hash it was swept at, the battery that swept it written as .jeffy/probes/<battery> so the row's scope is the paths that battery declares, and one line naming what the sweep exercised, and a row whose implementing code changed after its recorded commit is stale and flips back to [ ] - the closing gate derives that from the battery's paths file rather than trusting the row, so a row naming no battery cannot be checked at all. If BACKLOG.md has no open tasks, generate them: in Improvement mode audit the project per the Method and Operating envelope in PLAN.md, sweeping unswept or stale Surface inventory rows first and updating each row with the commit swept at - sweeping a surface that computes values requires a known-answer or invariant check per function family, never only run-without-crash probes, because wrong numbers returned without complaint survive every liveness probe, and a battery whose row certifies content records at creation the discriminating input or pre-fix state it was once observed failing on, because an instrument that has never been seen to fail reads exactly like an instrument that passed, and requires every documented parameter exercised at two or more values that must change the output, boundary and negative sides included - a documented parameter whose value changes nothing is a finding, never a pass - a dimension scored None claims only the rows actually swept, never the unexamined remainder, and an audit that leaves rows unswept must say so in its scores line rather than present them as the whole project - in Scoped mode derive remaining tasks from the Definition of done; write them worst severity first as one-line items with an acceptance check each, append an AUDIT journal entry with the scores, run the end-of-iteration steps, and end the turn. Otherwise execute exactly one task, the top unblocked item, never batch; the queue spans both state files as one ordering - open High tasks, then unswept or stale Surface inventory rows, then open Medium tasks, then open Low tasks - so the map outranks everything but an open High, because leaving a known High unfixed to read more code is indefensible, while a Medium or Low can never starve the sweep the declaration requires; when the top of the queue is sweeping, spend the iteration sweeping every unswept or stale row it can properly evidence, the one exemption from never-batch, because rows batch and findings do not, and the evidence bar per row is unchanged, an executed known-answer or invariant battery and never run-without-crash, with any in-envelope finding a sweep surfaces filed at rubric severity in the same iteration; when this is the final iteration, i equals N, prefer a WRAPUP entry that tidies BACKLOG.md and writes a handoff over starting a task that cannot finish - with one exception, because the ledger is not the whole closing precondition: when the ledger is at the severity floor - nothing open above Low - the map is swept, and no full audit this run has scored clean, spend this final iteration on the closing full audit instead, because it fits before any extension window opens and the window never admits one - a WRAPUP here hands the extension a sequence it cannot legally complete. Verify the task's acceptance check, at most 3 fix attempts, else mark it [b] with a one line reason; when the check must first run against unfixed code, copy the fixed files aside and restore them after, and never git checkout a path carrying uncommitted work, because that deletes the fix being proved. Verify gate: after the acceptance check, run the Verify command from PLAN.md unless it is none, and run it only through the installed skills/jeffy/hooks/lib/quiet-verify.sh - the launch banner names its absolute path, and it lives beside the Stop hook under the jeffy skill, never inside the project - passing the path to PLAN.md - QUIET-VERIFY-ONLY: never invoke the raw Verify command yourself, because a green suite floods this window with output that says nothing and the wrapper is what bounds it; a green verify prints nothing to stdout by design, so do not re-run it to see the passing output, and never quote a count you did not receive - the wrapper puts the run duration, the oracle class and the line matching the Verify summary pattern on its green stderr line, and that is the figure a journal entry may quote; if it fails and it passed at the last checkpoint, this iteration broke the project: when the project is a git repository revert the working tree to the last jeffy checkpoint, mark the task [b] with the failure output, and record it in the journal entry; without git there is no checkpoint to revert to, so undo this iteration's edits directly, mark the task [b], and record it. One exception: when the newly red test was previously green only because of the defect this task fixes - a pre-existing fault newly exposed rather than introduced - repair it within this iteration instead of reverting, but only with differential evidence recorded in the journal that the task's change altered no previously-passing output; without that evidence, revert. Never pipe the Verify command or a test run through head or tail: in your own shell a pipeline reports the last command's status, so a red suite reads as green - redirect the output to a file and check the exit status. The engine runs the PLAN.md Command under pipefail unless it contains a stage that closes the pipe early (`grep`, `egrep` or `fgrep` carrying `-q`, `-m`, `--quiet`, `--silent` or `--max-count`, a `head`, or an `awk` that reaches `exit`), where it runs as written; an early closer the engine does not recognise surfaces as exit 141 and the refusal says SIGPIPE, and set +o pipefail at the head of the Command opts out. Exploratory output discipline: a command run to look rather than to prove - a recursive grep, a find, test discovery, a build or dependency log - whose output may run past about forty lines is redirected to a file under .jeffy/scratch/ and read back with grep, head or a count, never printed whole into this window; .jeffy/scratch/ is ignored by git at bootstrap and never committed, so a claim cites the command that wrote the file and never the file. Severity discipline: severity is the consequence a user of the shipped product meets, so a task line whose class is test|dev-tooling is a Low whatever else the rubric suggests, because a user never runs tests or developer tooling, and a task line whose class is docs|build-ci states Consequence: what a user of the shipped product meets, before its Acceptance, or it is a Low as well - the Stop hook refuses a declaration over a line this run filed that breaks either rule, and a run whose PLAN.md predates that ceiling is told it here rather than at the declaration it would otherwise lose; a finding exercised only by out-of-envelope input is Low at most; a finding filed below the severity the rubric suggests carries the rationale in its filing entry, and a downgrade without one is a violation; a downgrade rationale cites the envelope or rubric line it rests on, and a rationale contradicted by a written line in the state files is void - arguing the input is exotic on a surface the Operating envelope classifies adversarial is the canonical contradicted rationale; a filing scored below an earlier filing of the same class - the same settled-class line or the same id family - states in one line what distinguishes it, and the closing audit re-reads those lines the way it re-reads Declined derivations; never file inside a Settled class unless its implementing code changed since settlement; a class settled as fixed records its enumerating command on its Settled line as enumerated by: <command>, because a fixed-class line recording no enumeration cannot be re-checked and the hook refuses a declaration standing on one; the third finding sharing one root cause replaces instance patching with one structural task or one Proposed decision; envelope changes and audit escalations go under Proposed, and Proposed items never block convergence and are never worked without user approval. Backlog discipline: BACKLOG.md is a ledger, not a narrative; when a task completes, delete its line from BACKLOG.md and record one closing line in the journal entry; never write prose sections, run context, or DONE annotations into BACKLOG.md; add discovered subtasks as one-line items and replenish with a partial audit when fewer than 3 open tasks remain, unless the run is in closeout. A finding declined records on its Declined line the command or measurement that establishes the premise, written as Derivation: <command>, except the priced reason cost: exceeds one iteration, which is policy rather than premise; a Declined premise is a standing claim the declaration rests on, so the declaring iteration re-runs every recorded Derivation and every Settled-class enumeration the way it re-reads the Oracle class and Environment fingerprint, and a premise that no longer holds - or a settled count its own command contradicts - moves the entry back to the ledger before any declaration. Closeout: once a full fresh-evidence audit in this run has scored zero High and zero Medium in-envelope, the run stops auditing for the rest of the run - no replenishment and no further full audit, however many iterations of budget are left - and finishes by working or declining what is already on the ledger and then converging; closeout ends the hunt for new findings on swept surface, never the mapping of unswept surface: sweeping unswept or stale rows continues in closeout, is not replenishment and is not an audit, and a High or Medium a closeout sweep surfaces is filed and worked exactly as one the closing audit filed, because a declaration needs the whole map and a closeout that forbids mapping forbids the declaration it exists to reach; say in that audit's journal entry that closeout has begun. This is what bounds a run on a project with a long tail of Low findings, where each drained ledger would otherwise trigger another audit that files another Low and the run never ends. It never suppresses a real defect: entering closeout requires an audit that just found no High and no Medium, a High or Medium noticed while executing a task is still filed and still worked, and the evaluator gate and the next run's fresh audit both still apply. Append a JOURNAL.md entry with the heading grammar ## iter i/N | run-id | date | task-id or AUDIT or SWEEP or EVALUATOR or RATCHET or WRAPUP or SALVAGE or ROTATION | status, where run-id is the first 8 characters of the session id followed by a hyphen and the HHMMSS of started_at from the loop state frontmatter, so two runs in one session are told apart, and status is one of done, blocked, audit, converged, salvage, rotation - a SWEEP entry is an iteration spent sweeping inventory rows and takes status done, SALVAGE entries take status salvage, ROTATION entries take status rotation, an EVALUATOR entry records an evaluator-gate iteration and takes status audit when the run continues after the verdict, blocked on a terminal second REJECT, and converged when that same iteration declares - and body fields Task, Changed, Checkpoint, Verification, Learnings, Next, in that order; each iteration writes one primary entry, and SALVAGE and ROTATION entries are additional. If JOURNAL.md exceeds 500 lines, move all but the last 10 entries to the end of JOURNAL-archive.md, appending to whatever that file already holds and never overwriting it, splitting entries only on lines that begin ## iter followed by a digit so the heading grammar example in the preamble is never counted as an entry or moved - the archive accumulates across every rotation and every run, and the stop hook rejects an archive whose entry count fell - and record the rotation as a ROTATION entry. Lessons: when a Learnings line states an operational rule future iterations must obey - a build quirk, a command that must or must not be used, a mistake made twice - also append that rule as one line under ## Lessons in PLAN.md, keeping that section brief and free of status or task state. Checkpoint: at the end of every iteration, when the project is a git repository, run git add -A then git commit -q with the message jeffy: iter i/N task-id status, then evaluate the stall check below, record the new commit hash as Checkpoint in the current entry - completing the entry, not rewriting it - together with the stall note when one is due, and commit that small bookkeeping edit with the message jeffy: iter i/N bookkeeping, so the next iteration starts with a clean tree and salvage does not misfire; if the commit fails, record Checkpoint: none with the reason and continue; never push and never create branches. Stall check: if this iteration changed no file other than PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, a path under .jeffy/, or the harness-written .claude/jeffy-loop.local.md and .claude/settings.local.json, and no BACKLOG.md item changed state - an item changed state when a task line under Now, Next, or Later was added, removed, edited, or moved between sections, which is the same definition the stop hook's own ledger signal is computed from, so prose edits elsewhere in the ledger are not progress to either of you - and no Surface inventory row changed state, because a sweep iteration's row flips are progress to both of you - say so in the journal entry; if the previous primary entry already says the same, that is a hard blocker - except that the ceremony entries are not stalls and never form that pair: an AUDIT that files nothing, the EVALUATOR gate, a RATCHET re-declaration, a WRAPUP, and an iteration whose entry honestly records blocked, for at most three consecutive iterations, which is the whole convergence sequence, after which they count like any other iteration; that is the same bounded exemption the stop hook applies before it ends a run as stalled. Change discipline: before changing shared or public code, read its callers and the tests that pin it, and state in the journal entry what contract the change preserves; when a change alters a public function's behavior, signature, or accepted inputs, the same iteration updates its documentation and flips the affected Surface inventory rows back to [ ] so the next audit re-sweeps them - a fix that leaves its own module contradicting its docs or its siblings is not done. A prose claim that generalises over a set of sites - the error names X, no temporary is left behind, reachable only when Y - ships with the enumeration of that set in the same iteration and its executing check drives every enumerated site; where the class is about failures, build the enumeration by provoking a failure at every step of the operation, never by grepping the source for the calls it makes, and if the enumeration cannot be produced this iteration, narrow the sentence to the single site actually verified. A fix re-executes the claims it invalidates: before the checkpoint, re-run the checks behind every prose claim and stated number in the state files and in the modules this diff touches and their direct siblings; never write a line number into a state file - state the shape the enumerating command returns. Edit only the files the task requires plus PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, .jeffy/probes/, .jeffy/evaluator/ and .jeffy/scratch/, all inside the current project folder; .gitignore only at bootstrap, and never .jeffy/metrics/, which the Stop hook owns. Never rewrite past journal entries, never write secrets into state files, use hyphens not dashes in file content. On a hard blocker - a missing dependency, a decision only the user can make, or a second consecutive no-progress iteration - append a journal entry naming the exact blocker, delete .claude/jeffy-loop.local.md to end the loop, and close with the run report. Run report: whenever a turn ends the run - convergence, the final iteration, or a hard blocker - finish your reply to the user with a run report: iterations used and how they were spent, tasks closed this run with their severities, the Surface inventory position as rows swept of rows total with the rows swept at run start beside it so the run's sweep progress is one subtraction, the files changed across the run via git diff --stat against the commit that preceded this run's first checkpoint when the project is a git repository, tasks still open or blocked with their reasons including every carried Low by ID, Proposed items awaiting a decision, the final Verify status, and a pointer to JOURNAL.md and git log for the full record. Closing rule: the run is converged only when the ratchet verification above succeeded, or the Definition of done in PLAN.md is genuinely and verifiably true: a full fresh-evidence audit this run scored zero High and zero Medium in-envelope, the Surface inventory in PLAN.md lists no unswept row, zero open High and zero open Medium tasks remain in Now, Next, and Later, while an open Low does not block: the declaring entry lists each carried Low by ID with one line, the run report repeats them, and a Low is carried only with its severity written on its task line, because the hook reads severity from the line and treats an unparseable one as blocking - the only commits since that clean audit are the completed fixes for tasks it or the evaluator gate filed plus loop state file edits, the Verify command is green this iteration, the adversarial evaluator returned PASS, and a Converged line with the full commit hash is appended under ## Converged in BACKLOG.md. One transaction closes the run: the declaring iteration may combine at most two fixes for tasks the evaluator gate itself filed, their acceptance checks, the re-invocation and the declaration; inside that combination the fixes and their row re-recording land before the re-invocation, never in the checkpoint edit after it, because a gate invoked over rows a fix just outdated rejects on bookkeeping; a fix rides that exemption only when it and its check complete inside this iteration, and anything larger takes an iteration of its own; that is the only exemption from executing exactly one task and it exists on the convergence path alone; and when at least two iterations remain beyond the declaring one, gate-filed fixes take their own iterations and the re-invocation rides alone - the combination is for when the budget forces it, because a second REJECT is terminal and a run has died combining them with budget to spare. Evaluator gate: on the Definition of done path only, never the ratchet. Before any invocation - the early one or the declaring one - bring the standing claims current in this same iteration - a standing claim is any statement in the state files or under .jeffy/ that records its own derivation, and the currency set is that form itself, never a fixed list: re-record every swept Surface inventory row whose battery's declared paths this run's fixes touched, re-run every recorded Declined Derivation and every Settled-class enumeration, run skills/jeffy/hooks/lib/check-claims.sh and resolve every MISMATCH (it executes the battery claims files and PLAN.md's Stated counts table alike; a count a governance file states is written as returns <count> and joins that table in the commit that states it, and a stated count the table does not carry refuses the declaration) (a battery README states a measurement only as the <value> of a line `expect <value> :: <command>` in that battery's claims file, or the file holds the single line `none`; a battery this run writes or whose README it edits carries one, which the hook checks by form), resolve every finding ID PLAN.md names as carried or blocked to a ledger line that still holds it, and re-read the Oracle class and Environment fingerprint, and keep the Verify count cell equal to the total the wrapper's green line reports (the hook says VERIFY COUNT at the checkpoint when it is not, and refuses a declaration on it); the gate consumes each of these exactly as the declaration does, and a REJECT spent on a claim the run itself had already outdated is an invocation the declaration then lacks. Last, run the installed Stop hook in lint mode - bash <absolute path of stop-hook.sh> --lint <project root>, the hook path the launch banner names beside the verify wrapper - and repair whatever it prints before invoking: it derives every declaration check the hook enforces, executes nothing, and a line it prints is a refusal that would otherwise cost the invocation, and the Converged line and the gate's verdict, which are written after it runs, are the two things it reports as pending rather than refuses. Then, before appending the Converged line, spawn exactly one fresh-context sub-agent as the adversarial evaluator, telling it this run's run-id from the journal headings, the current iteration number i of N, and which invocation of this run it is - 1 for the first, incremented for every later one whatever verdict came back, which is the same count the invocation cap is measured in, with these instructions: assume the run's work is broken until proven otherwise and do not praise; read PLAN.md and BACKLOG.md at the project root; review the run's changes with git diff and git log against the commit that preceded this run's first jeffy checkpoint; re-run the Verify command and the acceptance checks of the tasks closed this run and report their real output; run skills/jeffy/hooks/lib/check-claims.sh: a MISMATCH on a PLAN.md Stated counts row is a REJECT reason, because the declaration rests on it; a MISMATCH on a battery claims line, or a measurement a battery README states, is the loop's own instrument drifting - report it as an observation scored Low at most, never a REJECT reason by itself, because REJECT reasons are defects in the product or in the claims the declaration rests on (PLAN.md, BACKLOG.md, the product's README and receipts), and prose under .jeffy/ is loop memory outside the rubric; a REJECT reason scored High or Medium names what a user of the shipped product meets, and missing or weak tests, lint, formatting, type-checker output, CI configuration and developer tooling are observations scored Low at most, however many of them there are; your scope is what this run found and says it fixed, nothing wider: for every High and Medium task closed this run, run its filed reproduction against the commit that preceded the run's first checkpoint and confirm it failed there, run it against HEAD and confirm it passes, re-execute its Acceptance as written, and read the fix's diff for a regression in the code it touched, with the Verify command green as the floor - a fix that does not hold, a reproduction that never failed on the base commit, or a regression a fix introduced is a REJECT reason; re-score those closed tasks and every open or carried High or Medium against the rubric, treating a High or Medium that names nothing a user of the shipped product meets, or a Low that hides something a user meets, as a REJECT reason, and never REJECT merely because an accurately scored Low is open; do not audit the project for findings of your own - the run's audits did that and the next run's fresh audit will again - and record anything you notice on the way as an observation scored Low; resolve every finding ID PLAN.md names as carried or blocked, treating a dangling reference as a REJECT reason; return PASS only if every check holds, otherwise REJECT with each reason as one line carrying a file and line, a failing command, or a reproduced behavior; and before returning any verdict write the artifact .jeffy/evaluator/<run-id>-<n>.md under the run-id you were given, where n is the invocation ordinal you were told - 1 for the first invocation of this run, counting every earlier invocation whatever verdict it returned - opening with one line naming that run-id, that ordinal, and the iteration i of N that invoked you, so every verdict this run produced survives as its own path that no squash, rebase, filtered export or shallow clone can fold away, which the stop hook requires - then listing every command you ran with that command's real exit status and ending the artifact with a final line reading exactly 'Verdict: PASS' or 'Verdict: REJECT' and nothing else on that line - every reason goes above it, nothing but blank lines follows it, and no other line beginning with a verdict label - Verdict, Final Verdict, Overall Verdict, Overall, Result or Outcome - is written anywhere in the artifact, an earlier invocation's verdict quoted as history included - because the stop hook reads the verdict from that last line alone, refuses a journal PASS the artifact does not record, and refuses an artifact that holds more than one verdict line or does not end on one; the Operating envelope and the evidence rule bind the evaluator exactly as they bind the audit. Run the gate while its verdict can still be answered: when the ledger first empties with a clean full audit already recorded this run and at least 3 iterations remaining, run it that iteration instead of deferring it to the declaration, because a REJECT files tasks and they need budget left to work. On PASS record Evaluator: PASS with a one-line summary in the Verification field of that iteration's entry and let this iteration's checkpoint commit the artifact, because the hook reads the highest invocation ordinal on record for this run id and refuses a PASS whose artifact at that ordinal is missing from the tree, uncommitted, or modified since it was committed; a PASS that does not declare in the same iteration does not carry forward, so the declaring iteration re-invokes the gate and records the verdict it returns in the closing entry, the only one the stop hook reads it from. Declare on a PASS in that same iteration whenever the closing conditions hold: an observation the gate records that is not a REJECT reason is never fixed inside the convergence sequence - it goes to the run report and the next run's ledger, because a fix after a PASS invalidates that PASS and spends an invocation the declaration needs, and two runs have died on exactly that sequence. On REJECT do not converge: file each substantiated reason as an ordinary backlog task at rubric severity, or under Proposed if it challenges the envelope, record the rejection in the journal entry, and continue iterating within the budget; at most 2 evaluator invocations per run, 3 when the first landed before the midpoint of the budget, and the cap is absolute - the gate is never worn down and a third REJECT ends the declaration path outright. What is terminal is a REJECT with no invocation remaining, never the second one as such: a second REJECT holding an invocation files its findings and the run continues, and dedicated iterations close every finding both rejections filed before that last invocation is spent, which is the only way a run whose first verdict was REJECT ever reaches the third invocation it earned. On a terminal REJECT the run does not end while budget remains; it enters gate salvage - work only the findings the gate filed, one per iteration as always, and never re-invoke the gate, never declare, never audit and never replenish the ledger - and it ends blocked at budget exhaustion or as soon as no gate-filed finding is left open, whichever comes first, closing with the run report line blocked - N gate findings closed, declaration deferred, because convergence waits for the next run's fresh gate. Three blocked runs once sat on 13 budgeted iterations they were forbidden to use while the gate's own reproduced findings sat filed with their acceptance checks already written. If sub-agent spawning is unavailable in this session, record Evaluator: unavailable with the reason in the closing journal entry and the run report, then end the run under the hard blocker rule and never declare: the gate is not optional, no convergence rests on its absence, and the declaration waits for a relaunch in a session where a sub-agent can be spawned. Battery ownership: after your change, list the paths your diff touched, run every battery under .jeffy/probes/ whose paths file matches one of them in this same iteration, each through the installed run-probe.sh beside the verify wrapper, which bounds an instrument's memory and wall time so a runaway probe dies alone - a probe killed at its ceiling is an instrument finding about the probe, never the end of the iteration - and treat a battery failure as a Verify failure; when your fix changes behaviour a battery pins, update that battery in this iteration too, and re-record every Surface inventory row naming a battery you re-ran at this iteration's checkpoint hash, in the same bookkeeping edit that records Checkpoint, because a row certifies the code as of the commit it names and the closing gate derives that from the battery's paths file - a run that fixes a defect and leaves the rows around it pointing at the pre-fix commit is declaring over code it last swept before the change, and a declaration standing on such a row is refused with the row and the changed file named. Closing extension: the +2 window buys only the convergence sequence - the evaluator gate, fixes for tasks that gate filed under the one-transaction rule, and the declaration; never run an audit inside it, and a non-evaluator finding discovered there goes to the run report as the next run's first task, because the hook ends the run the moment the ledger refills. One convergence shape is legal inside the window and only one: a clean full audit already on this run's record from before the window opened, then the gate, then the declaration. A run that arrives in the window without that audit cannot make one there - the hook ends the run out of budget the moment an AUDIT entry appears at an iteration inside the window, whatever the entry goes on to claim, and convergence falls to the next run's fresh audit. To declare convergence, and only then, output the run report followed by the exact phrase JEFFY CONVERGED wrapped in promise XML tags; a run that ends out of budget or on a blocker ends with the run report alone, because the promise is the declaration and never the way a turn or a run ends, and the tagged form is never written in any other context, including the journal.
|
|
@@ -34,11 +34,13 @@ Dimension scores claim only swept rows. A None on a dimension whose surface stil
|
|
|
34
34
|
|
|
35
35
|
Convergence requires that this table lists no unswept row, and the Stop hook checks that mechanically. That is what makes the loop bounded: the surface is finite, sweeps only accumulate, and the remaining work is always the visible remainder of a written list rather than a hope that the next audit comes back empty. The run report states progress as rows swept of rows total. These rows are scheduled work with a bound, not audit incidentals: the loop's queue orders open High tasks, then unswept or stale Surface inventory rows, then open Medium tasks, then open Low tasks, so the map outranks everything but an open High and neither a Medium nor a Low can starve the sweep; a sweep iteration batches every row it can properly evidence with the evidence bar per row unchanged, the Stop hook projects when the map clears from the run's own observed sweep rate and says so every iteration, and a run whose projection says the map cannot clear inside the budget is ended early with that arithmetic rather than at exhaustion. Closeout ends the hunt for new findings on swept surface, never the mapping of unswept surface: sweeping continues in closeout, and a High or Medium a closeout sweep surfaces is filed and worked exactly as one the closing audit filed.
|
|
36
36
|
|
|
37
|
+
Every line in this section that begins with a checkbox bullet is read as a row by the Stop hook, at any indentation and with any of the three list characters, so an example of the row form written into the prose here is counted as an unswept row and refuses the declaration while no sweep can ever clear it. Write such an example inside a fenced code block, which the hook skips.
|
|
38
|
+
|
|
37
39
|
Rows (first audit fills this in, one line each):
|
|
38
40
|
- [ ] <surface>: <scope>
|
|
39
41
|
|
|
40
42
|
## Verify command
|
|
41
|
-
One runnable command that must exit 0 for this project to count as unbroken - the test suite, the build, or a validator script. The first audit fills this in; write none with a one-line reason only if the project genuinely has no runnable gate. It runs under `pipefail`, so a failing stage anywhere in a pipeline fails it (`npm test | tee verify.log` is red when the suite is); a Command with a stage that closes
|
|
43
|
+
One runnable command that must exit 0 for this project to count as unbroken - the test suite, the build, or a validator script. The first audit fills this in; write none with a one-line reason only if the project genuinely has no runnable gate. It runs under `pipefail`, so a failing stage anywhere in a pipeline fails it (`npm test | tee verify.log` is red when the suite is); a Command with a stage that closes the pipe early (`grep`, `egrep` or `fgrep` carrying `-q`, `-m`, `--quiet`, `--silent` or `--max-count`, a `head`, or an `awk` that reaches `exit`) runs as written, because pipefail would report that stage's SIGPIPE instead. It runs at the end of every iteration: a failure the previous checkpoint did not have means the iteration broke the project, and the iteration is reverted.
|
|
42
44
|
|
|
43
45
|
An exit status is a narrow thing to trust, so the same first audit writes down what that status actually covers. `Oracle class` names what the command grades, in your own words: unit tests, a conformance corpus, a differential comparison against a reference implementation, a build only. `Environment fingerprint` names the platform, the toolchain versions, and every test target this platform excludes - and that exclusion list is derived by a command, enumerating the build constraints, platform guards and skip markers in the test tree, with the line naming the command that produced it. Never a bare assertion that nothing is excluded: the whole failure this exists to catch is an exclusion nobody looked for. The declaring iteration re-reads both lines, and a test asset the fingerprint says the command cannot reach is one no journal entry may claim was green. A run once converged over 29 iterations while its project's 402-case conformance corpus never executed at all - the only file importing it carried a build tag for another platform, and the command's own package selection never reached it - and that run asserted twice that the corpus was passing. Nothing mechanical could catch it, because the command exited 0 every time; only the declaration could have.
|
|
44
46
|
|
|
@@ -32,11 +32,13 @@ An audit also exercises every documented parameter of the functions it probes, a
|
|
|
32
32
|
|
|
33
33
|
Dimension scores claim only rows the audit walked. A None on a dimension whose rows were never opened is silence, not cleanliness, so every audit probes breadth-first - every row shallowly before any row deeply - and the worst defect in the project appears in the first filing, not the sixth. The run report states the map as rows walked of rows total.
|
|
34
34
|
|
|
35
|
+
Every line in this section that begins with a checkbox bullet is read as a row by the Stop hook, at any indentation and with any of the three list characters, so an example of the row form written into the prose here is counted as an unswept row and refuses the declaration while no sweep can ever clear it. Write such an example inside a fenced code block, which the hook skips.
|
|
36
|
+
|
|
35
37
|
Rows (first audit fills this in, one line each):
|
|
36
38
|
- [ ] <surface>: <scope>
|
|
37
39
|
|
|
38
40
|
## Verify command
|
|
39
|
-
One runnable command that must exit 0 for this project to count as unbroken - the test suite, the build, or a validator script. The first audit fills this in; write none with a one-line reason only if the project genuinely has no runnable gate. It runs under `pipefail`, so a failing stage anywhere in a pipeline fails it (`npm test | tee verify.log` is red when the suite is); a Command with a stage that closes
|
|
41
|
+
One runnable command that must exit 0 for this project to count as unbroken - the test suite, the build, or a validator script. The first audit fills this in; write none with a one-line reason only if the project genuinely has no runnable gate. It runs under `pipefail`, so a failing stage anywhere in a pipeline fails it (`npm test | tee verify.log` is red when the suite is); a Command with a stage that closes the pipe early (`grep`, `egrep` or `fgrep` carrying `-q`, `-m`, `--quiet`, `--silent` or `--max-count`, a `head`, or an `awk` that reaches `exit`) runs as written, because pipefail would report that stage's SIGPIPE instead. It runs at the end of every iteration: a failure the previous checkpoint did not have means the iteration broke the project, and the iteration is reverted.
|
|
40
42
|
|
|
41
43
|
An exit status is a narrow thing to trust, so the same first audit writes down what that status actually covers. `Oracle class` names what the command grades, in your own words: unit tests, a conformance corpus, a differential comparison against a reference implementation, a build only. `Environment fingerprint` names the platform, the toolchain versions, and every test target this platform excludes - and that exclusion list is derived by a command, enumerating the build constraints, platform guards and skip markers in the test tree, with the line naming the command that produced it. Never a bare assertion that nothing is excluded: the whole failure this exists to catch is an exclusion nobody looked for. The declaring iteration re-reads both lines, and a test asset the fingerprint says the command cannot reach is one no journal entry may claim was green. A run once converged over 29 iterations while its project's 402-case conformance corpus never executed at all - the only file importing it carried a build tag for another platform, and the command's own package selection never reached it - and that run asserted twice that the corpus was passing. Nothing mechanical could catch it, because the command exited 0 every time; only the declaration could have.
|
|
42
44
|
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = "1.26.2"
|
|
@@ -172,7 +172,38 @@ def prune_dead_hooks(settings):
|
|
|
172
172
|
|
|
173
173
|
|
|
174
174
|
def write_settings(path, settings):
|
|
175
|
-
|
|
175
|
+
"""Replace settings.json atomically.
|
|
176
|
+
|
|
177
|
+
install.sh writes this file as mktemp, then a validity gate, then mv, so a
|
|
178
|
+
write that fails partway cannot truncate the user's whole Claude Code
|
|
179
|
+
configuration. A direct write_text truncates in place, which is both a
|
|
180
|
+
narrower guarantee than the sibling installer's and the reason an
|
|
181
|
+
unwritable settings.json raised PermissionError where every other failure
|
|
182
|
+
here prints [FAILED]. os.replace is atomic on POSIX and on Windows, and
|
|
183
|
+
renaming into place needs write permission on the directory rather than on
|
|
184
|
+
the file on POSIX, which is what lets a read-only settings.json be replaced
|
|
185
|
+
as mv replaces it; Windows refuses the rename over a read-only file and the
|
|
186
|
+
boundary in main reports that as [FAILED].
|
|
187
|
+
"""
|
|
188
|
+
# A symlinked settings.json (stow, home-manager) is replaced at its target,
|
|
189
|
+
# not turned into a regular file beside a dangling link; the existing mode
|
|
190
|
+
# is carried over, because a rename takes the umask's mode and would open a
|
|
191
|
+
# 0600 file holding API keys to 0644.
|
|
192
|
+
if path.is_symlink():
|
|
193
|
+
path = path.resolve()
|
|
194
|
+
tmp = path.with_name(f"{path.name}.jeffy-{os.getpid()}.tmp")
|
|
195
|
+
try:
|
|
196
|
+
tmp.write_text(json.dumps(settings, indent=2) + "\n", encoding="utf-8")
|
|
197
|
+
if path.exists():
|
|
198
|
+
shutil.copymode(path, tmp)
|
|
199
|
+
os.replace(tmp, path)
|
|
200
|
+
except OSError as exc:
|
|
201
|
+
with contextlib.suppress(OSError):
|
|
202
|
+
tmp.unlink()
|
|
203
|
+
if exc.filename and str(exc.filename) == str(tmp) and not getattr(exc, "filename2", None):
|
|
204
|
+
# The temp file is an implementation detail; name the file the user knows.
|
|
205
|
+
raise OSError(exc.errno, exc.strerror, str(path)) from exc
|
|
206
|
+
raise
|
|
176
207
|
|
|
177
208
|
|
|
178
209
|
def register_hook():
|
|
@@ -348,6 +379,28 @@ def cmd_help():
|
|
|
348
379
|
|
|
349
380
|
|
|
350
381
|
def main():
|
|
382
|
+
"""Run the command, turning a filesystem failure into the product's own message.
|
|
383
|
+
|
|
384
|
+
One boundary rather than a guard at every site: the operating envelope asks
|
|
385
|
+
for a single place where this class is handled, and an unhandled OSError
|
|
386
|
+
anywhere below here used to reach the user as a traceback - on an
|
|
387
|
+
unwritable settings.json, and on a read-only ~/.claude where the skill copy
|
|
388
|
+
fails first.
|
|
389
|
+
"""
|
|
390
|
+
try:
|
|
391
|
+
return _run()
|
|
392
|
+
except OSError as exc:
|
|
393
|
+
# os.replace reports the source in filename and the destination in
|
|
394
|
+
# filename2, and the destination is the path the user recognises - the
|
|
395
|
+
# first draft of this handler named the internal temp file instead.
|
|
396
|
+
target = getattr(exc, "filename2", None) or getattr(exc, "filename", None) or "a file it needed"
|
|
397
|
+
detail = exc.strerror or exc.__class__.__name__
|
|
398
|
+
# stdout, beside every other [OK] and [FAILED] line this installer prints.
|
|
399
|
+
print(f"[FAILED] {target}: {detail}; fix that, then re-run this installer.")
|
|
400
|
+
return 1
|
|
401
|
+
|
|
402
|
+
|
|
403
|
+
def _run():
|
|
351
404
|
args = sys.argv[1:]
|
|
352
405
|
cmd = args[0] if args else "help"
|
|
353
406
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Read the frontmatter of .claude/jeffy-loop.local.md at the project root for iteration and max_iterations: you are on iteration i of N and every journal heading needs i/N. Read PLAN.md and BACKLOG.md in full and the last 3 entries of JOURNAL.md. Salvage first: when the project is a git repository and the working tree is dirty at the start of this iteration - ignoring any path under .jeffy/metrics/, which the Stop hook writes after every checkpoint and the next checkpoint sweeps up, so it is never salvage - the previous iteration was interrupted or a human edited mid-flight; commit everything with the message jeffy: salvage before iter i, record a SALVAGE journal entry, and continue; never reset or discard work. If the Operating envelope table in PLAN.md is unfilled, enumerate the project's real input surfaces and fill it before filing any finding; if the Verify command in PLAN.md is unfilled, fill it during the first audit with the project's real gate, or none with a one-line reason, and fill its Oracle class, Environment fingerprint and Verify count lines in that same audit - Verify count is the total the wrapper's green summary reports and the only place that total is written down, so the Oracle class stays prose and carries no count -: Oracle class names what the command actually grades in your own words - unit tests, a conformance corpus, a differential comparison against a reference implementation, a build only - and Environment fingerprint names the platform, the toolchain versions, and every test target this platform excludes, that exclusion list derived by a command enumerating the build constraints, platform guards and skip markers in the test tree, with the line naming that command and never a bare assertion that nothing is excluded, because the failure this catches is an exclusion nobody looked for; the declaring iteration re-reads both lines, and a test asset the fingerprint says the command cannot reach is one no journal entry may claim was green. If the Surface inventory in PLAN.md is unfilled, fill it during the first audit before filing any finding: one checkbox row per public module or entry-point group until the whole public surface is listed, then probe every row at least shallowly before any deep dive, so the worst defect in the project appears in the first filing rather than the sixth; a hunt never sweeps a row and never flips one - every row stays [ ], the inventory is the map every audit walks, and the close does not require a swept row. If BACKLOG.md has no open tasks, audit: one full fresh-evidence audit of the project per the Method and Operating envelope in PLAN.md, walking every Surface inventory row at least shallowly - probing a surface that computes values requires a known-answer or invariant check per function family, never only run-without-crash probes, because wrong numbers returned without complaint survive every liveness probe, and a battery that certifies content records at creation the discriminating input or pre-fix state it was once observed failing on, because an instrument that has never been seen to fail reads exactly like an instrument that passed, and requires every documented parameter exercised at two or more values that must change the output, boundary and negative sides included - a documented parameter whose value changes nothing is a finding, never a pass - and a dimension scored None claims only the rows the audit actually walked, never an unexamined remainder; file only Highs, worst first, as one-line items with an acceptance check each, and record every Medium or Low the audit notices in the AUDIT entry on one line beginning Noted, not filed: with one clause per finding, never on the ledger and never worked; append an AUDIT journal entry with the scores and, when the audit filed a High, run the end-of-iteration steps and end the turn; when it filed none, the hunt closes in this same iteration per the closing rule below. Otherwise execute exactly one task, the top open High, never batch, because leaving a known High unfixed to read more code is indefensible; when this is the final iteration, i equals N, prefer a WRAPUP entry that tidies BACKLOG.md and writes a handoff over starting a task that cannot finish. Verify the task's acceptance check, at most 3 fix attempts, else mark it [b] with a one line reason; when the check must first run against unfixed code, copy the fixed files aside and restore them after, and never git checkout a path carrying uncommitted work, because that deletes the fix being proved. Verify gate: after the acceptance check, run the Verify command from PLAN.md unless it is none, and run it only through the installed skills/jeffy/hooks/lib/quiet-verify.sh - the launch banner names its absolute path, and it lives beside the Stop hook under the jeffy skill, never inside the project - passing the path to PLAN.md - QUIET-VERIFY-ONLY: never invoke the raw Verify command yourself, because a green suite floods this window with output that says nothing and the wrapper is what bounds it; a green verify prints nothing to stdout by design, so do not re-run it to see the passing output, and never quote a count you did not receive - the wrapper puts the run duration, the oracle class and the line matching the Verify summary pattern on its green stderr line, and that is the figure a journal entry may quote; if it fails and it passed at the last checkpoint, this iteration broke the project: when the project is a git repository revert the working tree to the last jeffy checkpoint, mark the task [b] with the failure output, and record it in the journal entry; without git there is no checkpoint to revert to, so undo this iteration's edits directly, mark the task [b], and record it. One exception: when the newly red test was previously green only because of the defect this task fixes - a pre-existing fault newly exposed rather than introduced - repair it within this iteration instead of reverting, but only with differential evidence recorded in the journal that the task's change altered no previously-passing output; without that evidence, revert. Never pipe the Verify command or a test run through head or tail: in your own shell a pipeline reports the last command's status, so a red suite reads as green - redirect the output to a file and check the exit status. The engine runs the PLAN.md Command under pipefail unless it contains a stage that closes the pipe early (grep -q or -m, head, an awk that exits), where it runs as written; an early closer the engine does not recognise surfaces as exit 141 and the refusal says SIGPIPE, and set +o pipefail at the head of the Command opts out. Exploratory output discipline: a command run to look rather than to prove - a recursive grep, a find, test discovery, a build or dependency log - whose output may run past about forty lines is redirected to a file under .jeffy/scratch/ and read back with grep, head or a count, never printed whole into this window; .jeffy/scratch/ is ignored by git at bootstrap and never committed, so a claim cites the command that wrote the file and never the file. Severity discipline: severity is the consequence a user of the shipped product meets, so a task line whose class is test|dev-tooling is a Low whatever else the rubric suggests, because a user never runs tests or developer tooling, and a task line whose class is docs|build-ci states Consequence: what a user of the shipped product meets, before its Acceptance, or it is a Low as well - the Stop hook refuses a declaration over a line this run filed that breaks either rule, and a run whose PLAN.md predates that ceiling is told it here rather than at the declaration it would otherwise lose; a finding exercised only by out-of-envelope input is Low at most; a finding filed below the severity the rubric suggests carries the rationale in its filing entry, and a downgrade without one is a violation; a downgrade rationale cites the envelope or rubric line it rests on, and a rationale contradicted by a written line in the state files is void - arguing the input is exotic on a surface the Operating envelope classifies adversarial is the canonical contradicted rationale; a filing scored below an earlier filing of the same class - the same settled-class line or the same id family - states in one line what distinguishes it, and the closing audit re-reads those lines the way it re-reads Declined derivations; the third finding sharing one root cause replaces instance patching with one structural task or one Proposed decision; envelope changes and audit escalations go under Proposed, and Proposed items never block the close and are never worked without user approval. Backlog discipline: BACKLOG.md is a ledger, not a narrative; when a task completes, delete its line from BACKLOG.md and record one closing line in the journal entry; never write prose sections, run context, or DONE annotations into BACKLOG.md; add a High discovered while working as a one-line item; never replenish with a partial audit, because the only audit a hunt runs is the full fresh one it runs when no High is open. A finding declined records on its Declined line the command or measurement that establishes the premise, written as Derivation: <command>, except the priced reason cost: exceeds one iteration, which is policy rather than premise; a Declined premise is a standing claim the declaration rests on, so the closing iteration re-runs every recorded Derivation the way it re-reads the Oracle class and Environment fingerprint, and a premise that no longer holds moves the entry back to the ledger before the close. Append a JOURNAL.md entry with the heading grammar ## iter i/N | run-id | date | task-id or AUDIT or WRAPUP or SALVAGE or ROTATION | status, where run-id is the first 8 characters of the session id followed by a hyphen and the HHMMSS of started_at from the loop state frontmatter, so two runs in one session are told apart, and status is one of done, blocked, audit, hunted, salvage, rotation - an AUDIT entry takes status audit, or hunted when that same iteration closes the hunt, SALVAGE entries take status salvage, ROTATION entries take status rotation - and body fields Task, Changed, Checkpoint, Verification, Learnings, Next, in that order; each iteration writes one primary entry, and SALVAGE and ROTATION entries are additional. If JOURNAL.md exceeds 500 lines, move all but the last 10 entries to the end of JOURNAL-archive.md, appending to whatever that file already holds and never overwriting it, splitting entries only on lines that begin ## iter followed by a digit so the heading grammar example in the preamble is never counted as an entry or moved - the archive accumulates across every rotation and every run, and the stop hook rejects an archive whose entry count fell - and record the rotation as a ROTATION entry. Lessons: when a Learnings line states an operational rule future iterations must obey - a build quirk, a command that must or must not be used, a mistake made twice - also append that rule as one line under ## Lessons in PLAN.md, keeping that section brief and free of status or task state. Checkpoint: at the end of every iteration, when the project is a git repository, run git add -A then git commit -q with the message jeffy: iter i/N task-id status, then evaluate the stall check below, record the new commit hash as Checkpoint in the current entry - completing the entry, not rewriting it - together with the stall note when one is due, and commit that small bookkeeping edit with the message jeffy: iter i/N bookkeeping, so the next iteration starts with a clean tree and salvage does not misfire; if the commit fails, record Checkpoint: none with the reason and continue; never push and never create branches. Stall check: if this iteration changed no file other than PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, a path under .jeffy/, or the harness-written .claude/jeffy-loop.local.md and .claude/settings.local.json, and no BACKLOG.md item changed state - an item changed state when a task line under Now, Next, or Later was added, removed, edited, or moved between sections, which is the same definition the stop hook's own ledger signal is computed from, so prose edits elsewhere in the ledger are not progress to either of you - and no Surface inventory row changed state, because a sweep iteration's row flips are progress to both of you - say so in the journal entry; if the previous primary entry already says the same, that is a hard blocker - except that the ceremony entries are not stalls and never form that pair: an AUDIT that files nothing, a WRAPUP, and an iteration whose entry honestly records blocked, for at most three consecutive iterations, after which they count like any other iteration; that is the same bounded exemption the stop hook applies before it ends a run as stalled. Change discipline: before changing shared or public code, read its callers and the tests that pin it, and state in the journal entry what contract the change preserves; when a change alters a public function's behavior, signature, or accepted inputs, the same iteration updates its documentation - a fix that leaves its own module contradicting its docs or its siblings is not done. A prose claim that generalises over a set of sites - the error names X, no temporary is left behind, reachable only when Y - ships with the enumeration of that set in the same iteration and its executing check drives every enumerated site; where the class is about failures, build the enumeration by provoking a failure at every step of the operation, never by grepping the source for the calls it makes, and if the enumeration cannot be produced this iteration, narrow the sentence to the single site actually verified. A fix re-executes the claims it invalidates: before the checkpoint, re-run the checks behind every prose claim and stated number in the state files and in the modules this diff touches and their direct siblings; never write a line number into a state file - state the shape the enumerating command returns. Edit only the files the task requires plus PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, .jeffy/probes/ and .jeffy/scratch/, all inside the current project folder; .gitignore only at bootstrap, and never .jeffy/metrics/, which the Stop hook owns. Never rewrite past journal entries, never write secrets into state files, use hyphens not dashes in file content. On a hard blocker - a missing dependency, a decision only the user can make, or a second consecutive no-progress iteration - append a journal entry naming the exact blocker, delete .claude/jeffy-loop.local.md to end the loop, and close with the run report. Run report: whenever a turn ends the run - the close of the hunt, the final iteration, or a hard blocker - finish your reply to the user with a run report: iterations used and how they were spent, the Highs closed this run, every Noted, not filed: line the run's audits recorded, the files changed across the run via git diff --stat against the commit that preceded this run's first checkpoint when the project is a git repository, Highs still open or blocked with their reasons, Proposed items awaiting a decision, the final Verify status, and a pointer to JOURNAL.md and git log for the full record. Closing rule: the hunt is complete only when the Definition of done in PLAN.md is genuinely and verifiably true: a full fresh-evidence audit this iteration scored zero High in-envelope, zero open High remains in BACKLOG.md, the only commits since the previous audit are the completed fixes for the Highs it filed plus loop state file edits, the Verify command is green this iteration, and a line reading Hunted: <full commit hash> - <date> - <k> Highs closed, where k counts the Highs this run closed, is appended under ## Hunted in BACKLOG.md. The audit that finds no High and the close share one iteration: that AUDIT entry takes status hunted, and its Checkpoint field names this iteration's checkpoint commit, the Hunted line names that same commit and rides the bookkeeping edit that records it, because the Stop hook refuses a close whose last AUDIT entry carries no resolvable Checkpoint or after whose checkpoint a product path changed, and a close whose last AUDIT entry does not carry status hunted or is followed by a later entry of this run. A Hunted line is never edited, and the hash it names has to stay reachable from HEAD: the only legal answer to a history rewrite that orphaned the certified commit is an appended line reading Hunted: <new hash> - <date> - <k> Highs closed (repoints <old hash>, tree unchanged), legal only when both commits carry the same tree, with the superseded line left standing and a dated ## Note in JOURNAL.md disclosing the rewrite and both hashes. Before the close, in the same iteration, bring the standing claims current: re-run every recorded Declined Derivation, run skills/jeffy/hooks/lib/check-claims.sh and resolve every MISMATCH (it executes the battery claims files and PLAN.md's Stated counts table alike; a count a governance file states is written as returns <count> and joins that table in the commit that states it, and a stated count the table does not carry refuses the close) (a battery README states a measurement only as the <value> of a line `expect <value> :: <command>` in that battery's claims file, or the file holds the single line `none`; a battery this run writes or whose README it edits carries one, which the hook checks by form), keep the Verify count cell equal to the total the wrapper's green line reports (the hook says VERIFY COUNT at the checkpoint when it is not, and refuses the close on it), and re-read the Oracle class and Environment fingerprint. Last, run the installed Stop hook in lint mode - bash <absolute path of stop-hook.sh> --lint <project root>, the hook path the launch banner names beside the verify wrapper - and repair whatever it prints before the checkpoint: it derives every check the hook enforces at the close and executes nothing, a line it prints is a refusal, and the checkpoint and the Hunted line, which are written after it runs, are the two things it reports as pending rather than refuses. Battery ownership: after your change, list the paths your diff touched, run every battery under .jeffy/probes/ whose paths file matches one of them in this same iteration, each through the installed run-probe.sh beside the verify wrapper, which bounds an instrument's memory and wall time so a runaway probe dies alone - a probe killed at its ceiling is an instrument finding about the probe, never the end of the iteration - and treat a battery failure as a Verify failure; when your fix changes behaviour a battery pins, update that battery in this iteration too. To close the hunt, and only then, output the run report followed by the exact phrase JEFFY HUNT COMPLETE wrapped in promise XML tags; a run that ends out of budget or on a blocker ends with the run report alone, because the promise is the declaration and never the way a turn or a run ends, and the tagged form is never written in any other context, including the journal.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
Read the frontmatter of .claude/jeffy-loop.local.md at the project root for iteration and max_iterations: you are on iteration i of N and every journal heading needs i/N. Read PLAN.md and BACKLOG.md in full and the last 3 entries of JOURNAL.md. Salvage first: when the project is a git repository and the working tree is dirty at the start of this iteration - ignoring any path under .jeffy/metrics/, which the Stop hook writes after every checkpoint and the next checkpoint sweeps up, so it is never salvage - the previous iteration was interrupted or a human edited mid-flight; commit everything with the message jeffy: salvage before iter i, record a SALVAGE journal entry, and continue; never reset or discard work. Ratchet next: if the latest Converged line in BACKLOG.md names a commit hash, BACKLOG.md lists no open task in Now, Next, or Later, this run carries no focus directive, and every path changed since that hash, per git status --porcelain and git diff --name-only against it, is only PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, a path under .jeffy/, or the harness-written .claude/jeffy-loop.local.md and .claude/settings.local.json, append a RATCHET journal entry recording that verification and end the run converged per the closing rule below; a seeded backlog, a focus directive, or any other changed path means the ratchet does not apply and the run proceeds normally. A Converged line is never edited, and the hash it names has to stay reachable from HEAD: the only legal answer to a history rewrite that orphaned the certified commit is an appended line reading Converged: <new hash> - <date> (repoints <old hash>, tree unchanged), legal only when both commits carry the same tree, with the superseded line left standing and a dated ## Note in JOURNAL.md disclosing the rewrite and both hashes. If the Operating envelope table in PLAN.md is unfilled, enumerate the project's real input surfaces and fill it before filing any finding, and in the same first audit enumerate the tree's artifact-producing channels by command, never by recall - packaging manifests such as Cargo.toml, package.json with its files list or .npmignore, MANIFEST.in or pyproject sdist config, gemspec, nuspec, plus container builds and release or publish workflows that archive the tree - and verify per channel that PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md and .jeffy/ cannot reach the published artifact, filing what fails or cannot be verified at Medium with its Consequence stated, because a published artifact carrying the loop's state is something a user of the shipped product meets; if the Verify command in PLAN.md is unfilled, fill it during the first audit with the project's real gate, or none with a one-line reason, and fill its Oracle class, Environment fingerprint and Verify count lines in that same audit - Verify count is the total the wrapper's green summary reports and the only place that total is written down, so the Oracle class stays prose and carries no count -: Oracle class names what the command actually grades in your own words - unit tests, a conformance corpus, a differential comparison against a reference implementation, a build only - and Environment fingerprint names the platform, the toolchain versions, and every test target this platform excludes, that exclusion list derived by a command enumerating the build constraints, platform guards and skip markers in the test tree, with the line naming that command and never a bare assertion that nothing is excluded, because the failure this catches is an exclusion nobody looked for; the declaring iteration re-reads both lines, and a test asset the fingerprint says the command cannot reach is one no journal entry may claim was green. If the Surface inventory in PLAN.md is unfilled, fill it during the first audit before filing any finding: one checkbox row per public module or entry-point group until the whole public surface is listed, then probe every row at least shallowly before any deep dive, so the worst defect in the project appears in the first filing rather than the sixth; a row flips to [x] only with the commit hash it was swept at, the battery that swept it written as .jeffy/probes/<battery> so the row's scope is the paths that battery declares, and one line naming what the sweep exercised, and a row whose implementing code changed after its recorded commit is stale and flips back to [ ] - the closing gate derives that from the battery's paths file rather than trusting the row, so a row naming no battery cannot be checked at all. If BACKLOG.md has no open tasks, generate them: in Improvement mode audit the project per the Method and Operating envelope in PLAN.md, sweeping unswept or stale Surface inventory rows first and updating each row with the commit swept at - sweeping a surface that computes values requires a known-answer or invariant check per function family, never only run-without-crash probes, because wrong numbers returned without complaint survive every liveness probe, and a battery whose row certifies content records at creation the discriminating input or pre-fix state it was once observed failing on, because an instrument that has never been seen to fail reads exactly like an instrument that passed, and requires every documented parameter exercised at two or more values that must change the output, boundary and negative sides included - a documented parameter whose value changes nothing is a finding, never a pass - a dimension scored None claims only the rows actually swept, never the unexamined remainder, and an audit that leaves rows unswept must say so in its scores line rather than present them as the whole project - in Scoped mode derive remaining tasks from the Definition of done; write them worst severity first as one-line items with an acceptance check each, append an AUDIT journal entry with the scores, run the end-of-iteration steps, and end the turn. Otherwise execute exactly one task, the top unblocked item, never batch; the queue spans both state files as one ordering - open High tasks, then unswept or stale Surface inventory rows, then open Medium tasks, then open Low tasks - so the map outranks everything but an open High, because leaving a known High unfixed to read more code is indefensible, while a Medium or Low can never starve the sweep the declaration requires; when the top of the queue is sweeping, spend the iteration sweeping every unswept or stale row it can properly evidence, the one exemption from never-batch, because rows batch and findings do not, and the evidence bar per row is unchanged, an executed known-answer or invariant battery and never run-without-crash, with any in-envelope finding a sweep surfaces filed at rubric severity in the same iteration; when this is the final iteration, i equals N, prefer a WRAPUP entry that tidies BACKLOG.md and writes a handoff over starting a task that cannot finish - with one exception, because the ledger is not the whole closing precondition: when the ledger is at the severity floor - nothing open above Low - the map is swept, and no full audit this run has scored clean, spend this final iteration on the closing full audit instead, because it fits before any extension window opens and the window never admits one - a WRAPUP here hands the extension a sequence it cannot legally complete. Verify the task's acceptance check, at most 3 fix attempts, else mark it [b] with a one line reason; when the check must first run against unfixed code, copy the fixed files aside and restore them after, and never git checkout a path carrying uncommitted work, because that deletes the fix being proved. Verify gate: after the acceptance check, run the Verify command from PLAN.md unless it is none, and run it only through the installed skills/jeffy/hooks/lib/quiet-verify.sh - the launch banner names its absolute path, and it lives beside the Stop hook under the jeffy skill, never inside the project - passing the path to PLAN.md - QUIET-VERIFY-ONLY: never invoke the raw Verify command yourself, because a green suite floods this window with output that says nothing and the wrapper is what bounds it; a green verify prints nothing to stdout by design, so do not re-run it to see the passing output, and never quote a count you did not receive - the wrapper puts the run duration, the oracle class and the line matching the Verify summary pattern on its green stderr line, and that is the figure a journal entry may quote; if it fails and it passed at the last checkpoint, this iteration broke the project: when the project is a git repository revert the working tree to the last jeffy checkpoint, mark the task [b] with the failure output, and record it in the journal entry; without git there is no checkpoint to revert to, so undo this iteration's edits directly, mark the task [b], and record it. One exception: when the newly red test was previously green only because of the defect this task fixes - a pre-existing fault newly exposed rather than introduced - repair it within this iteration instead of reverting, but only with differential evidence recorded in the journal that the task's change altered no previously-passing output; without that evidence, revert. Never pipe the Verify command or a test run through head or tail: in your own shell a pipeline reports the last command's status, so a red suite reads as green - redirect the output to a file and check the exit status. The engine runs the PLAN.md Command under pipefail unless it contains a stage that closes the pipe early (grep -q or -m, head, an awk that exits), where it runs as written; an early closer the engine does not recognise surfaces as exit 141 and the refusal says SIGPIPE, and set +o pipefail at the head of the Command opts out. Exploratory output discipline: a command run to look rather than to prove - a recursive grep, a find, test discovery, a build or dependency log - whose output may run past about forty lines is redirected to a file under .jeffy/scratch/ and read back with grep, head or a count, never printed whole into this window; .jeffy/scratch/ is ignored by git at bootstrap and never committed, so a claim cites the command that wrote the file and never the file. Severity discipline: severity is the consequence a user of the shipped product meets, so a task line whose class is test|dev-tooling is a Low whatever else the rubric suggests, because a user never runs tests or developer tooling, and a task line whose class is docs|build-ci states Consequence: what a user of the shipped product meets, before its Acceptance, or it is a Low as well - the Stop hook refuses a declaration over a line this run filed that breaks either rule, and a run whose PLAN.md predates that ceiling is told it here rather than at the declaration it would otherwise lose; a finding exercised only by out-of-envelope input is Low at most; a finding filed below the severity the rubric suggests carries the rationale in its filing entry, and a downgrade without one is a violation; a downgrade rationale cites the envelope or rubric line it rests on, and a rationale contradicted by a written line in the state files is void - arguing the input is exotic on a surface the Operating envelope classifies adversarial is the canonical contradicted rationale; a filing scored below an earlier filing of the same class - the same settled-class line or the same id family - states in one line what distinguishes it, and the closing audit re-reads those lines the way it re-reads Declined derivations; never file inside a Settled class unless its implementing code changed since settlement; a class settled as fixed records its enumerating command on its Settled line as enumerated by: <command>, because a fixed-class line recording no enumeration cannot be re-checked and the hook refuses a declaration standing on one; the third finding sharing one root cause replaces instance patching with one structural task or one Proposed decision; envelope changes and audit escalations go under Proposed, and Proposed items never block convergence and are never worked without user approval. Backlog discipline: BACKLOG.md is a ledger, not a narrative; when a task completes, delete its line from BACKLOG.md and record one closing line in the journal entry; never write prose sections, run context, or DONE annotations into BACKLOG.md; add discovered subtasks as one-line items and replenish with a partial audit when fewer than 3 open tasks remain, unless the run is in closeout. A finding declined records on its Declined line the command or measurement that establishes the premise, written as Derivation: <command>, except the priced reason cost: exceeds one iteration, which is policy rather than premise; a Declined premise is a standing claim the declaration rests on, so the declaring iteration re-runs every recorded Derivation and every Settled-class enumeration the way it re-reads the Oracle class and Environment fingerprint, and a premise that no longer holds - or a settled count its own command contradicts - moves the entry back to the ledger before any declaration. Closeout: once a full fresh-evidence audit in this run has scored zero High and zero Medium in-envelope, the run stops auditing for the rest of the run - no replenishment and no further full audit, however many iterations of budget are left - and finishes by working or declining what is already on the ledger and then converging; closeout ends the hunt for new findings on swept surface, never the mapping of unswept surface: sweeping unswept or stale rows continues in closeout, is not replenishment and is not an audit, and a High or Medium a closeout sweep surfaces is filed and worked exactly as one the closing audit filed, because a declaration needs the whole map and a closeout that forbids mapping forbids the declaration it exists to reach; say in that audit's journal entry that closeout has begun. This is what bounds a run on a project with a long tail of Low findings, where each drained ledger would otherwise trigger another audit that files another Low and the run never ends. It never suppresses a real defect: entering closeout requires an audit that just found no High and no Medium, a High or Medium noticed while executing a task is still filed and still worked, and the evaluator gate and the next run's fresh audit both still apply. Append a JOURNAL.md entry with the heading grammar ## iter i/N | run-id | date | task-id or AUDIT or SWEEP or EVALUATOR or RATCHET or WRAPUP or SALVAGE or ROTATION | status, where run-id is the first 8 characters of the session id followed by a hyphen and the HHMMSS of started_at from the loop state frontmatter, so two runs in one session are told apart, and status is one of done, blocked, audit, converged, salvage, rotation - a SWEEP entry is an iteration spent sweeping inventory rows and takes status done, SALVAGE entries take status salvage, ROTATION entries take status rotation, an EVALUATOR entry records an evaluator-gate iteration and takes status audit when the run continues after the verdict, blocked on a terminal second REJECT, and converged when that same iteration declares - and body fields Task, Changed, Checkpoint, Verification, Learnings, Next, in that order; each iteration writes one primary entry, and SALVAGE and ROTATION entries are additional. If JOURNAL.md exceeds 500 lines, move all but the last 10 entries to the end of JOURNAL-archive.md, appending to whatever that file already holds and never overwriting it, splitting entries only on lines that begin ## iter followed by a digit so the heading grammar example in the preamble is never counted as an entry or moved - the archive accumulates across every rotation and every run, and the stop hook rejects an archive whose entry count fell - and record the rotation as a ROTATION entry. Lessons: when a Learnings line states an operational rule future iterations must obey - a build quirk, a command that must or must not be used, a mistake made twice - also append that rule as one line under ## Lessons in PLAN.md, keeping that section brief and free of status or task state. Checkpoint: at the end of every iteration, when the project is a git repository, run git add -A then git commit -q with the message jeffy: iter i/N task-id status, then evaluate the stall check below, record the new commit hash as Checkpoint in the current entry - completing the entry, not rewriting it - together with the stall note when one is due, and commit that small bookkeeping edit with the message jeffy: iter i/N bookkeeping, so the next iteration starts with a clean tree and salvage does not misfire; if the commit fails, record Checkpoint: none with the reason and continue; never push and never create branches. Stall check: if this iteration changed no file other than PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, a path under .jeffy/, or the harness-written .claude/jeffy-loop.local.md and .claude/settings.local.json, and no BACKLOG.md item changed state - an item changed state when a task line under Now, Next, or Later was added, removed, edited, or moved between sections, which is the same definition the stop hook's own ledger signal is computed from, so prose edits elsewhere in the ledger are not progress to either of you - and no Surface inventory row changed state, because a sweep iteration's row flips are progress to both of you - say so in the journal entry; if the previous primary entry already says the same, that is a hard blocker - except that the ceremony entries are not stalls and never form that pair: an AUDIT that files nothing, the EVALUATOR gate, a RATCHET re-declaration, a WRAPUP, and an iteration whose entry honestly records blocked, for at most three consecutive iterations, which is the whole convergence sequence, after which they count like any other iteration; that is the same bounded exemption the stop hook applies before it ends a run as stalled. Change discipline: before changing shared or public code, read its callers and the tests that pin it, and state in the journal entry what contract the change preserves; when a change alters a public function's behavior, signature, or accepted inputs, the same iteration updates its documentation and flips the affected Surface inventory rows back to [ ] so the next audit re-sweeps them - a fix that leaves its own module contradicting its docs or its siblings is not done. A prose claim that generalises over a set of sites - the error names X, no temporary is left behind, reachable only when Y - ships with the enumeration of that set in the same iteration and its executing check drives every enumerated site; where the class is about failures, build the enumeration by provoking a failure at every step of the operation, never by grepping the source for the calls it makes, and if the enumeration cannot be produced this iteration, narrow the sentence to the single site actually verified. A fix re-executes the claims it invalidates: before the checkpoint, re-run the checks behind every prose claim and stated number in the state files and in the modules this diff touches and their direct siblings; never write a line number into a state file - state the shape the enumerating command returns. Edit only the files the task requires plus PLAN.md, BACKLOG.md, JOURNAL.md, JOURNAL-archive.md, .jeffy/probes/, .jeffy/evaluator/ and .jeffy/scratch/, all inside the current project folder; .gitignore only at bootstrap, and never .jeffy/metrics/, which the Stop hook owns. Never rewrite past journal entries, never write secrets into state files, use hyphens not dashes in file content. On a hard blocker - a missing dependency, a decision only the user can make, or a second consecutive no-progress iteration - append a journal entry naming the exact blocker, delete .claude/jeffy-loop.local.md to end the loop, and close with the run report. Run report: whenever a turn ends the run - convergence, the final iteration, or a hard blocker - finish your reply to the user with a run report: iterations used and how they were spent, tasks closed this run with their severities, the Surface inventory position as rows swept of rows total with the rows swept at run start beside it so the run's sweep progress is one subtraction, the files changed across the run via git diff --stat against the commit that preceded this run's first checkpoint when the project is a git repository, tasks still open or blocked with their reasons including every carried Low by ID, Proposed items awaiting a decision, the final Verify status, and a pointer to JOURNAL.md and git log for the full record. Closing rule: the run is converged only when the ratchet verification above succeeded, or the Definition of done in PLAN.md is genuinely and verifiably true: a full fresh-evidence audit this run scored zero High and zero Medium in-envelope, the Surface inventory in PLAN.md lists no unswept row, zero open High and zero open Medium tasks remain in Now, Next, and Later, while an open Low does not block: the declaring entry lists each carried Low by ID with one line, the run report repeats them, and a Low is carried only with its severity written on its task line, because the hook reads severity from the line and treats an unparseable one as blocking - the only commits since that clean audit are the completed fixes for tasks it or the evaluator gate filed plus loop state file edits, the Verify command is green this iteration, the adversarial evaluator returned PASS, and a Converged line with the full commit hash is appended under ## Converged in BACKLOG.md. One transaction closes the run: the declaring iteration may combine at most two fixes for tasks the evaluator gate itself filed, their acceptance checks, the re-invocation and the declaration; inside that combination the fixes and their row re-recording land before the re-invocation, never in the checkpoint edit after it, because a gate invoked over rows a fix just outdated rejects on bookkeeping; a fix rides that exemption only when it and its check complete inside this iteration, and anything larger takes an iteration of its own; that is the only exemption from executing exactly one task and it exists on the convergence path alone; and when at least two iterations remain beyond the declaring one, gate-filed fixes take their own iterations and the re-invocation rides alone - the combination is for when the budget forces it, because a second REJECT is terminal and a run has died combining them with budget to spare. Evaluator gate: on the Definition of done path only, never the ratchet. Before any invocation - the early one or the declaring one - bring the standing claims current in this same iteration - a standing claim is any statement in the state files or under .jeffy/ that records its own derivation, and the currency set is that form itself, never a fixed list: re-record every swept Surface inventory row whose battery's declared paths this run's fixes touched, re-run every recorded Declined Derivation and every Settled-class enumeration, run skills/jeffy/hooks/lib/check-claims.sh and resolve every MISMATCH (it executes the battery claims files and PLAN.md's Stated counts table alike; a count a governance file states is written as returns <count> and joins that table in the commit that states it, and a stated count the table does not carry refuses the declaration) (a battery README states a measurement only as the <value> of a line `expect <value> :: <command>` in that battery's claims file, or the file holds the single line `none`; a battery this run writes or whose README it edits carries one, which the hook checks by form), resolve every finding ID PLAN.md names as carried or blocked to a ledger line that still holds it, and re-read the Oracle class and Environment fingerprint, and keep the Verify count cell equal to the total the wrapper's green line reports (the hook says VERIFY COUNT at the checkpoint when it is not, and refuses a declaration on it); the gate consumes each of these exactly as the declaration does, and a REJECT spent on a claim the run itself had already outdated is an invocation the declaration then lacks. Last, run the installed Stop hook in lint mode - bash <absolute path of stop-hook.sh> --lint <project root>, the hook path the launch banner names beside the verify wrapper - and repair whatever it prints before invoking: it derives every declaration check the hook enforces, executes nothing, and a line it prints is a refusal that would otherwise cost the invocation, and the Converged line and the gate's verdict, which are written after it runs, are the two things it reports as pending rather than refuses. Then, before appending the Converged line, spawn exactly one fresh-context sub-agent as the adversarial evaluator, telling it this run's run-id from the journal headings, the current iteration number i of N, and which invocation of this run it is - 1 for the first, incremented for every later one whatever verdict came back, which is the same count the invocation cap is measured in, with these instructions: assume the run's work is broken until proven otherwise and do not praise; read PLAN.md and BACKLOG.md at the project root; review the run's changes with git diff and git log against the commit that preceded this run's first jeffy checkpoint; re-run the Verify command and the acceptance checks of the tasks closed this run and report their real output; run skills/jeffy/hooks/lib/check-claims.sh: a MISMATCH on a PLAN.md Stated counts row is a REJECT reason, because the declaration rests on it; a MISMATCH on a battery claims line, or a measurement a battery README states, is the loop's own instrument drifting - report it as an observation scored Low at most, never a REJECT reason by itself, because REJECT reasons are defects in the product or in the claims the declaration rests on (PLAN.md, BACKLOG.md, the product's README and receipts), and prose under .jeffy/ is loop memory outside the rubric; a REJECT reason scored High or Medium names what a user of the shipped product meets, and missing or weak tests, lint, formatting, type-checker output, CI configuration and developer tooling are observations scored Low at most, however many of them there are; your scope is what this run found and says it fixed, nothing wider: for every High and Medium task closed this run, run its filed reproduction against the commit that preceded the run's first checkpoint and confirm it failed there, run it against HEAD and confirm it passes, re-execute its Acceptance as written, and read the fix's diff for a regression in the code it touched, with the Verify command green as the floor - a fix that does not hold, a reproduction that never failed on the base commit, or a regression a fix introduced is a REJECT reason; re-score those closed tasks and every open or carried High or Medium against the rubric, treating a High or Medium that names nothing a user of the shipped product meets, or a Low that hides something a user meets, as a REJECT reason, and never REJECT merely because an accurately scored Low is open; do not audit the project for findings of your own - the run's audits did that and the next run's fresh audit will again - and record anything you notice on the way as an observation scored Low; resolve every finding ID PLAN.md names as carried or blocked, treating a dangling reference as a REJECT reason; return PASS only if every check holds, otherwise REJECT with each reason as one line carrying a file and line, a failing command, or a reproduced behavior; and before returning any verdict write the artifact .jeffy/evaluator/<run-id>-<n>.md under the run-id you were given, where n is the invocation ordinal you were told - 1 for the first invocation of this run, counting every earlier invocation whatever verdict it returned - opening with one line naming that run-id, that ordinal, and the iteration i of N that invoked you, so every verdict this run produced survives as its own path that no squash, rebase, filtered export or shallow clone can fold away, which the stop hook requires - then listing every command you ran with that command's real exit status and ending the artifact with a final line reading exactly 'Verdict: PASS' or 'Verdict: REJECT' and nothing else on that line - every reason goes above it, nothing but blank lines follows it, and no other line beginning with a verdict label - Verdict, Final Verdict, Overall Verdict, Overall, Result or Outcome - is written anywhere in the artifact, an earlier invocation's verdict quoted as history included - because the stop hook reads the verdict from that last line alone, refuses a journal PASS the artifact does not record, and refuses an artifact that holds more than one verdict line or does not end on one; the Operating envelope and the evidence rule bind the evaluator exactly as they bind the audit. Run the gate while its verdict can still be answered: when the ledger first empties with a clean full audit already recorded this run and at least 3 iterations remaining, run it that iteration instead of deferring it to the declaration, because a REJECT files tasks and they need budget left to work. On PASS record Evaluator: PASS with a one-line summary in the Verification field of that iteration's entry and let this iteration's checkpoint commit the artifact, because the hook reads the highest invocation ordinal on record for this run id and refuses a PASS whose artifact at that ordinal is missing from the tree, uncommitted, or modified since it was committed; a PASS that does not declare in the same iteration does not carry forward, so the declaring iteration re-invokes the gate and records the verdict it returns in the closing entry, the only one the stop hook reads it from. Declare on a PASS in that same iteration whenever the closing conditions hold: an observation the gate records that is not a REJECT reason is never fixed inside the convergence sequence - it goes to the run report and the next run's ledger, because a fix after a PASS invalidates that PASS and spends an invocation the declaration needs, and two runs have died on exactly that sequence. On REJECT do not converge: file each substantiated reason as an ordinary backlog task at rubric severity, or under Proposed if it challenges the envelope, record the rejection in the journal entry, and continue iterating within the budget; at most 2 evaluator invocations per run, 3 when the first landed before the midpoint of the budget, and the cap is absolute - the gate is never worn down and a third REJECT ends the declaration path outright. What is terminal is a REJECT with no invocation remaining, never the second one as such: a second REJECT holding an invocation files its findings and the run continues, and dedicated iterations close every finding both rejections filed before that last invocation is spent, which is the only way a run whose first verdict was REJECT ever reaches the third invocation it earned. On a terminal REJECT the run does not end while budget remains; it enters gate salvage - work only the findings the gate filed, one per iteration as always, and never re-invoke the gate, never declare, never audit and never replenish the ledger - and it ends blocked at budget exhaustion or as soon as no gate-filed finding is left open, whichever comes first, closing with the run report line blocked - N gate findings closed, declaration deferred, because convergence waits for the next run's fresh gate. Three blocked runs once sat on 13 budgeted iterations they were forbidden to use while the gate's own reproduced findings sat filed with their acceptance checks already written. If sub-agent spawning is unavailable in this session, record Evaluator: unavailable with the reason in the closing journal entry and the run report, then end the run under the hard blocker rule and never declare: the gate is not optional, no convergence rests on its absence, and the declaration waits for a relaunch in a session where a sub-agent can be spawned. Battery ownership: after your change, list the paths your diff touched, run every battery under .jeffy/probes/ whose paths file matches one of them in this same iteration, each through the installed run-probe.sh beside the verify wrapper, which bounds an instrument's memory and wall time so a runaway probe dies alone - a probe killed at its ceiling is an instrument finding about the probe, never the end of the iteration - and treat a battery failure as a Verify failure; when your fix changes behaviour a battery pins, update that battery in this iteration too, and re-record every Surface inventory row naming a battery you re-ran at this iteration's checkpoint hash, in the same bookkeeping edit that records Checkpoint, because a row certifies the code as of the commit it names and the closing gate derives that from the battery's paths file - a run that fixes a defect and leaves the rows around it pointing at the pre-fix commit is declaring over code it last swept before the change, and a declaration standing on such a row is refused with the row and the changed file named. Closing extension: the +2 window buys only the convergence sequence - the evaluator gate, fixes for tasks that gate filed under the one-transaction rule, and the declaration; never run an audit inside it, and a non-evaluator finding discovered there goes to the run report as the next run's first task, because the hook ends the run the moment the ledger refills. One convergence shape is legal inside the window and only one: a clean full audit already on this run's record from before the window opened, then the gate, then the declaration. A run that arrives in the window without that audit cannot make one there - the hook ends the run out of budget the moment an AUDIT entry appears at an iteration inside the window, whatever the entry goes on to claim, and convergence falls to the next run's fresh audit. To declare convergence, and only then, output the run report followed by the exact phrase JEFFY CONVERGED wrapped in promise XML tags; a run that ends out of budget or on a blocker ends with the run report alone, because the promise is the declaration and never the way a turn or a run ends, and the tagged form is never written in any other context, including the journal.
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
__version__ = "1.26.0"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|