liteagents 2.22.0 → 2.23.0
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.
- package/CHANGELOG.md +170 -1
- package/README.md +7 -5
- package/package.json +1 -1
- package/packages/ampcode/commands/branch-review.md +180 -14
- package/packages/ampcode/commands/docs-builder/docs-builder.cjs +25 -8
- package/packages/ampcode/commands/refactor.md +71 -3
- package/packages/ampcode/commands/release.md +49 -9
- package/packages/ampcode/commands/remember/AGENT_RULES.md +12 -3
- package/packages/ampcode/commands/remember.md +40 -7
- package/packages/ampcode/commands/ship.md +16 -0
- package/packages/claude/commands/branch-review.md +180 -14
- package/packages/claude/commands/docs-builder/docs-builder.cjs +25 -8
- package/packages/claude/commands/refactor.md +71 -3
- package/packages/claude/commands/release.md +49 -9
- package/packages/claude/commands/remember/AGENT_RULES.md +12 -3
- package/packages/claude/commands/remember.md +40 -7
- package/packages/claude/commands/ship.md +16 -0
- package/packages/droid/commands/branch-review.md +180 -14
- package/packages/droid/commands/docs-builder/docs-builder.cjs +25 -8
- package/packages/droid/commands/refactor.md +71 -3
- package/packages/droid/commands/release.md +49 -9
- package/packages/droid/commands/remember/AGENT_RULES.md +12 -3
- package/packages/droid/commands/remember.md +40 -7
- package/packages/droid/commands/ship.md +16 -0
- package/packages/opencode/command/branch-review.md +180 -14
- package/packages/opencode/command/docs-builder/docs-builder.cjs +25 -8
- package/packages/opencode/command/refactor.md +71 -3
- package/packages/opencode/command/release.md +49 -9
- package/packages/opencode/command/remember/AGENT_RULES.md +12 -3
- package/packages/opencode/command/remember.md +40 -7
- package/packages/opencode/command/ship.md +16 -0
package/CHANGELOG.md
CHANGED
|
@@ -7,7 +7,176 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
---
|
|
9
9
|
|
|
10
|
-
## [
|
|
10
|
+
## [2.23.0] - 2026-09-02
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **`/refactor` gains a no-argument ledger mode.** Bare `/refactor` works through
|
|
14
|
+
`.claude/remember/fix-ledger.md` instead of taking a target: it requires a clean tree and
|
|
15
|
+
a non-`main` branch, revalidates every bullet before fixing anything, drops the ones
|
|
16
|
+
whose anchor no longer resolves, fixes the survivors one at a time under the existing
|
|
17
|
+
no-behaviour-change constraints, and deletes each bullet as its fix lands — so the fix
|
|
18
|
+
commit is the done record and there is no second place to keep it in sync.
|
|
19
|
+
- **`/branch-review` gains State ownership as a stage-1 finding category.** Two or more
|
|
20
|
+
functions assigning the same field, flag or view property is a finding on its own, with
|
|
21
|
+
no failing case required. Both writers must be named with `file:line`, since an unnamed
|
|
22
|
+
second writer is a hunch. Ordering counts as well as writers: a write arriving from a
|
|
23
|
+
callback, thread or lifecycle event is the dangerous one, and one app writer racing a
|
|
24
|
+
framework writer still counts as two.
|
|
25
|
+
- **`AGENT_RULES.md` — four Build Rules, each naming something observable.** One writer per
|
|
26
|
+
piece of state; split the decision from the machinery, extracting a branch into a pure
|
|
27
|
+
function to pin it with a test rather than to raise coverage; claims in comments must be
|
|
28
|
+
checkable, because a name search proves an edge exists and never that one does not; and
|
|
29
|
+
every line earns its place, meaning if you cannot say what breaks when it is deleted,
|
|
30
|
+
delete it. "Surgical changes only" was rewritten to say what to do with a problem you
|
|
31
|
+
pass on the way: fix it if it is in the code you are already changing and the fix changes
|
|
32
|
+
no behaviour, otherwise report it with what it costs to leave. A problem you do not fix
|
|
33
|
+
goes in the report, never in a comment.
|
|
34
|
+
- **`/remember` writes two hot rules inline into the `AGENT_RULES.md` section.** The file
|
|
35
|
+
stays a plain pointer and is never `@`-referenced, since that hot-loads roughly 300 lines
|
|
36
|
+
of standards guide into every session. The section now carries the path plus exactly the
|
|
37
|
+
two rules that change what you type — the ones you cannot look up because you do not know
|
|
38
|
+
you need them.
|
|
39
|
+
- **`docs/product/branch-review-README.md`** — reference for the pre-merge gate: the three
|
|
40
|
+
stages, what blocks a merge, the ledger's anchor design, and the review → ledger →
|
|
41
|
+
`/refactor` loop. Follows the existing `remember` and `docs-builder` product-doc pattern.
|
|
42
|
+
- **`/refactor` gains a `## Guardrails` block, ported from `/branch-review`.** Spawn a
|
|
43
|
+
worker at your tool's mid tier, stated explicitly on the spawn; escalate anything you
|
|
44
|
+
cannot decide rather than assuming; the worker never sub-delegates; edit only what a
|
|
45
|
+
surviving ledger bullet names, one change per bullet. The three parts that do not
|
|
46
|
+
transfer verbatim were rewritten: "no edits" inverts into a scope rule since `/refactor`
|
|
47
|
+
edits by design, and **the three HITL gates belong to the orchestrator, not the
|
|
48
|
+
worker** — a subagent cannot hold a conversation, so it stops and hands back the options
|
|
49
|
+
with no choice made rather than picking revert/patch/update-test on the user's behalf.
|
|
50
|
+
The blast-radius proof is now two checks: `git status --porcelain` at exit must list
|
|
51
|
+
only bullet-named files, and `md5sum .claude/remember/*` must show only `fix-ledger.md`
|
|
52
|
+
differing — `last-review.md` is off-limits to the fixer, since writing it would forge
|
|
53
|
+
the gate that judges its own work.
|
|
54
|
+
- **`docs/product/remember-README.md`** gains a `## 6. Known limitations` section.
|
|
55
|
+
Matching semantics and evidence are the same channel — an entry's `class_hints` are
|
|
56
|
+
fragments of the quotes that proved it, so its identity and its proof of recurrence are
|
|
57
|
+
the same strings — with two consequences pulling in opposite directions: tautological
|
|
58
|
+
matching (bounded, not removed, by session-hash dedup) and over-matching on thin
|
|
59
|
+
ledgers (`Open item 2`). Separately: a run cannot tell that its own work invalidated a
|
|
60
|
+
standing fact, since detecting that would mean re-checking every fact against the
|
|
61
|
+
working tree on every run, which trades a stale fact for a confidently wrong one.
|
|
62
|
+
|
|
63
|
+
### Changed
|
|
64
|
+
- **`/branch-review` writes a durable review record; `/release` reads it.** The reviewed
|
|
65
|
+
SHA previously existed only as prose in a chat message, so `/release`'s Phase 0.5
|
|
66
|
+
precondition resolved to the orchestrator's word — the one party the same paragraph
|
|
67
|
+
declares inadmissible — and vanished on a compaction or a handover. The review now
|
|
68
|
+
overwrites `.claude/remember/last-review.md` with sha/branch/target/verdict/date, and
|
|
69
|
+
Phase 0.5 matches against its `sha:` line. No record, or no `sha:` line, is no review.
|
|
70
|
+
|
|
71
|
+
**Migration:** a branch reviewed before this change has no record file, so the new
|
|
72
|
+
Phase 0.5 will correctly refuse it. That is migration, not a bug. Re-run
|
|
73
|
+
`/branch-review`; do **not** hand-write the record, which would turn the durable
|
|
74
|
+
artifact back into the unverified claim it exists to replace.
|
|
75
|
+
|
|
76
|
+
- **Only reproduced Critical/High failures block a merge.** Everything else is appended
|
|
77
|
+
to a local fix ledger at `.claude/remember/fix-ledger.md`, consumed by bare `/refactor`.
|
|
78
|
+
Re-review after fixes reads `<previously-reviewed-sha>..HEAD` rather than re-judging the
|
|
79
|
+
whole branch, which is what makes a review converge instead of surfacing a fresh nit
|
|
80
|
+
list every run. Style, wording and structure never block; a normative requirement stated
|
|
81
|
+
two incompatible ways still does, since conforming implementations built from it diverge.
|
|
82
|
+
|
|
83
|
+
- **`/release` Phase 0.5 is the SHA comparison alone.** A ledger-only exception was removed
|
|
84
|
+
rather than kept and narrowed: the ledger is gitignored, so it never reaches a commit
|
|
85
|
+
diff, and scoping a rule to a condition that cannot occur is how dead branches survive
|
|
86
|
+
review. A repo that does track `.claude/` will see a ledger commit make the review stale,
|
|
87
|
+
which is the gate working — re-review, or leave the ledger uncommitted until the release
|
|
88
|
+
is cut.
|
|
89
|
+
|
|
90
|
+
### Fixed
|
|
91
|
+
- **`/branch-review` — a disproved ledger bullet is deleted, and a dead run is not a
|
|
92
|
+
pass.** The append-only rule left nowhere to record that a bullet's stated consequence
|
|
93
|
+
was wrong: editing it broke the rule, and a second bullet read as a second finding. A
|
|
94
|
+
field session hit this and invented an indented sub-bullet. Disproof now deletes the
|
|
95
|
+
line, with the reason going in the report — the ledger is a work list, not an archive.
|
|
96
|
+
Separately, a review that dies mid-flight writes no record, and nothing said whether that
|
|
97
|
+
silence counted as a pass; it does not.
|
|
98
|
+
- **`/branch-review` — the review record carries blockers, level and coverage.** Five lines
|
|
99
|
+
proved that a review ran and what it concluded, but not *what* was blocked, so a session
|
|
100
|
+
inheriting a `blocked` verdict had to re-review the branch to rediscover why — the
|
|
101
|
+
non-convergence this command exists to stop, displaced one level up. The record now lists
|
|
102
|
+
one line per blocker (claim only; scenarios stay in the report, non-blocking findings stay
|
|
103
|
+
in the ledger), the effort level, and per-stage coverage. `/release` stops on any stage
|
|
104
|
+
marked `NOT RUN`, since a `ready` from a run that skipped the security stage is a
|
|
105
|
+
different fact. Deliberately absent: any override field — a hash is checkable by anyone
|
|
106
|
+
and consent is not, so a consent line would be forgeable by whatever writes the file, and
|
|
107
|
+
a persisted override would silently cover the next release too.
|
|
108
|
+
- **`/release` Phase 0.5 wrote a `verdict:` line nobody read.** The record carried the
|
|
109
|
+
review's conclusion, but the gate compared only the `sha:` line, so a record saying
|
|
110
|
+
`verdict: blocked` passed the mechanical check whenever the hash still matched — leaving
|
|
111
|
+
the conclusion to the orchestrator's recollection, which is the unverified claim the
|
|
112
|
+
record was created to replace. Both lines are now read mechanically; only `ready` plus a
|
|
113
|
+
matching hash is a pass.
|
|
114
|
+
- **`/release` — the handoff sequence went from `gh pr create` straight to `gh pr merge`,
|
|
115
|
+
with no wait for CI.** Every gate in the chain runs on one machine: `/branch-review`
|
|
116
|
+
reviews locally, `/ship` runs the suite locally, and `/release` never pushes. CI is the
|
|
117
|
+
only differently-configured instrument, and under this flow it sees the branch for the
|
|
118
|
+
first time *after* both gates have passed. Found in the field: a release merged and
|
|
119
|
+
tagged on green local gates, then failed CI on a test asserting against a path that
|
|
120
|
+
exists only on the author's machine, leaving a tag cut but never published — the exact
|
|
121
|
+
"local ahead of published" state Phase 0 warns about. The sequence now has a
|
|
122
|
+
`gh pr checks --watch` step between create and merge, and merges only on green.
|
|
123
|
+
- **`/release` — the exit-code rule applies to the orchestrator's own shell too.** `/ship`
|
|
124
|
+
carried it for the worker, but the handoff steps are typed by hand and were not covered.
|
|
125
|
+
In the same field run, `gh run watch --exit-status | tail -2; echo $?` printed `0` for a
|
|
126
|
+
failed run, turning a red CI into a green reading.
|
|
127
|
+
- **`/release` — the docs sweep may fix a line a ledger bullet names, but must not delete
|
|
128
|
+
the bullet.** Calling the sweep the place to "close" a doc-only item made `/release` a
|
|
129
|
+
second deleter of state with exactly one owner, contradicting both the ledger's
|
|
130
|
+
one-append-one-delete split and the one-writer-per-state build rule this release adds.
|
|
131
|
+
The sweep still fixes the line; `/refactor`'s next revalidation drops the bullet.
|
|
132
|
+
- **`/branch-review` — the exit-cleanliness check could not see its own target.** The
|
|
133
|
+
guardrail said porcelain must be empty or list only the two allowed paths, but `.claude/`
|
|
134
|
+
is gitignored, so porcelain is empty whether the reviewer wrote those files, wrote
|
|
135
|
+
nothing, or overwrote `MEMORY.md`. `git status --ignored` does not close it either — it
|
|
136
|
+
collapses to the directory, not the files. Porcelain keeps its real job (no tracked file
|
|
137
|
+
changed); an `md5sum` comparison over `.claude/remember/` now covers the two writes.
|
|
138
|
+
- **`/ship` and `/branch-review` — exit codes must be read off the bare command, not a
|
|
139
|
+
pipeline.** `$?` after a pipe is the last element's status, so the natural multi-suite
|
|
140
|
+
shape `out=$(cmd 2>&1 | tail -1); echo "exit=$?"` records `tail`'s success for a suite
|
|
141
|
+
that exited non-zero. Found in the field: a check printing "exit 2: prerequisites
|
|
142
|
+
missing" entered the gate as a pass. `${PIPESTATUS[0]}` does not rescue it inside a
|
|
143
|
+
command substitution either.
|
|
144
|
+
- **`/branch-review` — ledger dedupe uses plain `grep`, not `git grep`.** The ledger is
|
|
145
|
+
deliberately gitignored and `git grep` searches tracked content only, so the dedupe
|
|
146
|
+
check reported "not found" for snippets sitting in the file and would have re-appended
|
|
147
|
+
every finding on every run.
|
|
148
|
+
- **`/branch-review` — ledger bullets are subject to stage 3.** A field run produced a true
|
|
149
|
+
finding whose stated consequence was false. Bullets must now be verified or carry an
|
|
150
|
+
`UNVERIFIED:` prefix so `/refactor` retests before acting.
|
|
151
|
+
- **`/remember` — a marker pair already present in `CLAUDE.md` had no rule.** The clause
|
|
152
|
+
covered a missing pair (append it) and the AGENT_RULES exception (leave it alone) but
|
|
153
|
+
said nothing for an already-present MEMORY pair, which is the common case on every run
|
|
154
|
+
after the first. Now: replace its content in place; the AGENT_RULES bootstrap-once
|
|
155
|
+
exception directly below still overrides.
|
|
156
|
+
- **`docs/product/branch-review-README.md`** — dropped a stale "five-line record" count.
|
|
157
|
+
The record stopped being five lines once level, coverage and blockers were added.
|
|
158
|
+
- **`/refactor` — ledger mode's step 1 caught a dirty tree only after a worker already
|
|
159
|
+
existed.** Step 1 now documents the split: the orchestrator runs the tree check before
|
|
160
|
+
spawning, and the worker re-runs it as its own first act, matching `/branch-review`'s
|
|
161
|
+
Target section.
|
|
162
|
+
|
|
163
|
+
## [2.22.1] - 2026-09-01
|
|
164
|
+
|
|
165
|
+
### Fixed
|
|
166
|
+
- **`/docs-builder` — the trailing-newline phantom line is dropped from every line count.**
|
|
167
|
+
`text.split('\n')` returns a trailing empty element for any file ending in a newline, so
|
|
168
|
+
`lines.length` was one over the real count. That phantom line reached the index row's "N
|
|
169
|
+
lines" (every row +1), the last H2's line range (one line past EOF), `scan`'s outline.json
|
|
170
|
+
`s`/`e`/`lines`, the ledger's per-file line count, the cleanup cost estimate, and the PARTIAL
|
|
171
|
+
guard (a page one line short of `MIN_PAGE_LINES` passed as complete). Fixed with a
|
|
172
|
+
`splitLines()` helper applied at the 7 counting/bounding sites, deliberately not at the sites
|
|
173
|
+
that map-and-rejoin file text. `docs/index.md` regenerated. Mirrored across all four kits.
|
|
174
|
+
- **`/docs-builder` — an empty page now reports PARTIAL instead of crashing `plan`.** A
|
|
175
|
+
regression from the fix above: `splitLines()` returns `[]` for a 0-byte file where the raw
|
|
176
|
+
split returned `['']`, so `pageStatus`'s unguarded `lines[0].trim()` threw a `TypeError` and
|
|
177
|
+
took `plan` down with it. An empty `.md` page is reachable (a touched placeholder, or
|
|
178
|
+
page-writing interrupted). Fixed at the indexing site — 0 lines is the correct count for an
|
|
179
|
+
empty file — so `pageStatus` guards on `lines.length` instead. Mirrored across all four kits.
|
|
11
180
|
|
|
12
181
|
## [2.22.0] - 2026-09-01
|
|
13
182
|
|
package/README.md
CHANGED
|
@@ -147,11 +147,11 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
147
147
|
- **skill-creator** - Guide for creating new skills
|
|
148
148
|
- **debug-method** - Four-phase debugging framework
|
|
149
149
|
- **optimize** - Performance analysis
|
|
150
|
-
- **refactor** - Safe refactoring with behavior preservation
|
|
151
|
-
- **branch-review** - Pre-merge gate: general review at a chosen effort level
|
|
150
|
+
- **refactor** - Safe refactoring with behavior preservation; with no arguments, works through the fix ledger `/branch-review` accumulated
|
|
151
|
+
- **branch-review** - Pre-merge gate that reports and never fixes: general review at a chosen effort level, a full security audit that runs at full depth regardless of level, then an adversarial verify pass. Needs a clean tree. Only reproduced Critical/High failures block; everything else lands in the fix ledger for `/refactor`
|
|
152
152
|
- **security** - Standalone vulnerability scan; also runs as stage 2 of `/branch-review`
|
|
153
|
-
- **ship** -
|
|
154
|
-
- **release** - Prepare a release on the current branch: verify → docs sweep → version bump → local commit, then hand back the merge/tag/publish sequence (never pushes)
|
|
153
|
+
- **ship** - Mechanical pre-deploy gate: every item is answerable by running a command and reading its exit code, recorded as pass/fail/N/A. A check not run is a fail, and N/A needs a stated reason
|
|
154
|
+
- **release** - Prepare a release on the current branch: verify → docs sweep → version bump → local commit, then hand back the merge/tag/publish sequence (never pushes). Refuses without a review recorded at the current HEAD SHA
|
|
155
155
|
- **test-generate** - Generate test suites
|
|
156
156
|
|
|
157
157
|
> **Claude-only plugin:** `live-canvas-channel` is a bundled Claude Code MCP channel plugin that ships under `~/.claude/plugins/live-canvas-marketplace/`. One-time `/plugin install` + a session started with `--dangerously-load-development-channels` unlocks live mode. Skill probes for the channel on each invocation and handholds setup when missing. See [`packages/claude/skills/live-canvas/README.md`](packages/claude/skills/live-canvas/README.md) for the full walkthrough.
|
|
@@ -166,6 +166,7 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
166
166
|
| **[subagentic-manual.md](packages/subagentic-manual.md)** | Detailed agent/command reference |
|
|
167
167
|
| **[remember-README.md](docs/product/remember-README.md)** | How hot memory works — the `/stash` → `/remember` pipeline, the friction sensor, and the antigen ledger |
|
|
168
168
|
| **[docs-builder-README.md](docs/product/docs-builder-README.md)** | How `/docs-builder` works — the reorg/cleanup modes, what it measurably costs, and the ledger that tracks doc drift |
|
|
169
|
+
| **[branch-review-README.md](docs/product/branch-review-README.md)** | How the pre-merge gate works — the three stages, what blocks a merge, and the `/branch-review` → fix ledger → `/refactor` loop |
|
|
169
170
|
|
|
170
171
|
---
|
|
171
172
|
|
|
@@ -184,7 +185,8 @@ Results land in `.claude/remember/friction/antigen_review.md` with projects, err
|
|
|
184
185
|
**Code Quality:**
|
|
185
186
|
```
|
|
186
187
|
@quality-assurance Review this PR before merge
|
|
187
|
-
/branch-review main medium # review
|
|
188
|
+
/branch-review main medium # review + security audit; blockers reported, the rest to the fix ledger
|
|
189
|
+
/refactor # no args: work through the fix ledger branch-review accumulated, between features
|
|
188
190
|
/debug-method Investigate this race condition
|
|
189
191
|
```
|
|
190
192
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liteagents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.23.0",
|
|
4
4
|
"description": "AI development toolkit with 11 specialized agents and 18 commands including live-canvas UI design with click-to-annotate feedback. Simple one-question installer for Claude, Opencode, Ampcode, and Droid.",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"bin": {
|
|
@@ -10,6 +10,15 @@ audit** — followed by an adversarial verify pass. It **never edits code**: it
|
|
|
10
10
|
reports findings and hands them back. Fixing is a separate, separately
|
|
11
11
|
authorized action.
|
|
12
12
|
|
|
13
|
+
Only **Critical** and **High** findings block the merge. Everything else is
|
|
14
|
+
appended to the **fix ledger** (`.claude/remember/fix-ledger.md`) — a local,
|
|
15
|
+
cumulative list, living beside `MEMORY.md`, that `/refactor` (no arguments)
|
|
16
|
+
works through between features. Like its neighbours it is a private working
|
|
17
|
+
artifact, usually gitignored; it persists across reviews, it is not a
|
|
18
|
+
deliverable. The report is blockers plus the ledger count, so a review
|
|
19
|
+
converges instead of surfacing fresh nits every run. This command never runs
|
|
20
|
+
`/refactor` itself — it nudges, the way `/stash` nudges `/remember`.
|
|
21
|
+
|
|
13
22
|
Run this **before** `/release`. `/release` will refuse to run without a review
|
|
14
23
|
at the current HEAD SHA.
|
|
15
24
|
|
|
@@ -33,13 +42,29 @@ at the current HEAD SHA.
|
|
|
33
42
|
executed X" from a sub-worker is hearsay, and replacing hearsay with evidence
|
|
34
43
|
is the entire point of this command. A review that delegates its work is a
|
|
35
44
|
review of a report. (Same rule `/security` carries inside stage 2.)
|
|
36
|
-
- **No edits.** You have no authorization to change code,
|
|
37
|
-
you are certain about. Report it.
|
|
38
|
-
|
|
39
|
-
|
|
45
|
+
- **No edits — two exceptions.** You have no authorization to change code,
|
|
46
|
+
even for a finding you are certain about. Report it. The only files you may
|
|
47
|
+
write are `.claude/remember/fix-ledger.md` (append bullets; never rewrite or
|
|
48
|
+
delete) and `.claude/remember/last-review.md` (overwrite; the review record
|
|
49
|
+
described at the end of this file).
|
|
50
|
+
- **Prove it with two checks, because neither sees what the other does.**
|
|
51
|
+
`git status --porcelain`, at start and again before you report, proves no
|
|
52
|
+
**tracked** file changed — that is the "never edits code" guarantee, and it
|
|
53
|
+
is the one that matters. It cannot police your own two writes: `.claude/`
|
|
54
|
+
is normally gitignored, so porcelain stays empty whether you wrote the
|
|
55
|
+
allowed files, wrote nothing, or overwrote `MEMORY.md`. `git status
|
|
56
|
+
--ignored` does not close it either — it collapses to `!! .claude/`, the
|
|
57
|
+
directory, not the files. So also take `md5sum .claude/remember/*` before
|
|
58
|
+
you start and again before you report, and show the comparison: only
|
|
59
|
+
`fix-ledger.md` and `last-review.md` may differ.
|
|
40
60
|
|
|
41
61
|
## Target — check the tree first, then interpret `$ARGUMENTS`
|
|
42
62
|
|
|
63
|
+
**The orchestrator runs this check before spawning anyone**, so a dirty tree
|
|
64
|
+
costs no worker; the worker then re-runs it as its own first act, because a
|
|
65
|
+
review that takes the tree's state on trust is the thing this command exists
|
|
66
|
+
not to do. Both, not either.
|
|
67
|
+
|
|
43
68
|
**Before resolving anything, run `git status --porcelain`.** If it prints any
|
|
44
69
|
line — modified, staged, or untracked — **stop and report it**. Say all three
|
|
45
70
|
things, not just the first: (a) the tree is dirty, listing the uncommitted
|
|
@@ -70,6 +95,30 @@ Record the **HEAD SHA** you reviewed, and **report the target you resolved**
|
|
|
70
95
|
(the literal range or path) in your output, so the orchestrator can see what
|
|
71
96
|
was actually read rather than assuming.
|
|
72
97
|
|
|
98
|
+
**Re-review after fixes: read `.claude/remember/last-review.md` first.** Its
|
|
99
|
+
`sha:` line is the previously-reviewed commit and its `blockers:` list is what
|
|
100
|
+
you owe an answer on — take both from the file, never from the orchestrator's
|
|
101
|
+
recollection, for the same reason `/release` does. Then:
|
|
102
|
+
|
|
103
|
+
- **`sha:` ≠ HEAD** → this is a re-review. Target the range
|
|
104
|
+
`<that sha>..HEAD`. Stage 1 reads only the commits since, and stage 3
|
|
105
|
+
re-verifies each recorded blocker as fixed, unfixed, or dismissed with a
|
|
106
|
+
reason. The rest of the branch is **not** re-judged: a full re-read of an
|
|
107
|
+
already-reviewed branch produces fresh findings every run and never
|
|
108
|
+
converges. The range still ends at HEAD, so `/release`'s precondition is
|
|
109
|
+
satisfied and the new record replaces the old one.
|
|
110
|
+
- **`sha:` = HEAD** → nothing has changed since the last review. Say so and
|
|
111
|
+
stop; re-running against an identical tree can only produce noise. If the
|
|
112
|
+
recorded verdict was `blocked`, its blockers are still unfixed by
|
|
113
|
+
definition — repeat them rather than re-deriving them.
|
|
114
|
+
- **No file** → no prior review to build on. Review the whole branch.
|
|
115
|
+
|
|
116
|
+
**On a re-review, sweep the open ledger bullets for liveness first.** Their
|
|
117
|
+
anchors may sit in the part of the branch you are no longer reading, and the
|
|
118
|
+
fix commits you *are* reading can invalidate them. `grep -F` each open
|
|
119
|
+
snippet against its path; report any whose anchor is gone so `/refactor` can
|
|
120
|
+
drop them. Cheap, and it stops dead bullets accumulating unseen.
|
|
121
|
+
|
|
73
122
|
## Effort level
|
|
74
123
|
`low | medium | high | max` — default **medium** if not given. The level
|
|
75
124
|
governs **stage 1 only**:
|
|
@@ -92,7 +141,10 @@ judging.
|
|
|
92
141
|
not a fact to accept. Branches are commonly AI-authored now — including the
|
|
93
142
|
fixes to the fixes — so a review that trusts the message is reviewing prose.
|
|
94
143
|
Run the test suite and the typecheck/build yourself and cite the command and
|
|
95
|
-
its exit code.
|
|
144
|
+
its exit code. Read that code off the bare command (`cmd > /tmp/out 2>&1;
|
|
145
|
+
e=$?`), never off a pipeline — `$?` after a pipe is the last element's
|
|
146
|
+
status, so piping into `tail` reports `0` for a suite that failed. `/ship`
|
|
147
|
+
carries the reproduction.
|
|
96
148
|
|
|
97
149
|
- **Bugs needing a fix.** Logic errors, off-by-one, null/undefined paths,
|
|
98
150
|
races, wrong defaults, broken edge cases.
|
|
@@ -104,6 +156,14 @@ its exit code.
|
|
|
104
156
|
"temporary" names, abandoned feature flags.
|
|
105
157
|
- **Correctness.** Edge cases, error handling, type / contract violations,
|
|
106
158
|
broken invariants.
|
|
159
|
+
- **State ownership.** Two or more functions assigning the same field, flag, or
|
|
160
|
+
view property. A finding on its own — no failing case required. `git grep`
|
|
161
|
+
every assignment to that name repo-wide, not just in the diff; the second
|
|
162
|
+
writer is usually in a file the diff never touched. Name both writers with
|
|
163
|
+
`file:line` — an unnamed second writer is a hunch, not a finding. Count
|
|
164
|
+
ordering, not just writers: a write arriving from a callback, thread, or
|
|
165
|
+
lifecycle event is the dangerous one, and one app writer racing a framework
|
|
166
|
+
one still counts as two.
|
|
107
167
|
- **Performance.** N+1, blocking calls in hot paths, unbounded loops, indexes
|
|
108
168
|
the diff actually touches.
|
|
109
169
|
- **Test quality, not just test presence.** For every test the diff adds or
|
|
@@ -156,21 +216,127 @@ verdict first, then repeat it at the end.
|
|
|
156
216
|
|
|
157
217
|
Then the findings, ordered most severe first.
|
|
158
218
|
|
|
159
|
-
### 🚨 Critical (blocks merge)
|
|
160
|
-
|
|
161
|
-
|
|
219
|
+
### 🚨 Critical / High (blocks merge)
|
|
220
|
+
A **reproduced** failure only: a failing test, a broken build, a security
|
|
221
|
+
exposure, or a bug with a written failure scenario you confirmed in stage 3.
|
|
222
|
+
A finding about **style, wording or structure** is **never** a blocker —
|
|
223
|
+
including in a doc or spec. But prose is not automatically harmless: in a repo
|
|
224
|
+
whose deliverable *is* a specification, a **normative requirement stated two
|
|
225
|
+
incompatible ways** is a reproduced defect, because two conforming
|
|
226
|
+
implementations built from it diverge. Judge by whether a behaviour changes,
|
|
227
|
+
not by whether the file holds code — and judge it **per finding, not per
|
|
228
|
+
repo**, since a diff mixing code and specification is the normal case. A finding already dismissed with evidence in this project's stash
|
|
229
|
+
or memory cannot come back at a higher severity without **new** evidence —
|
|
230
|
+
check before escalating.
|
|
231
|
+
|
|
232
|
+
### Ledger (non-blocking — medium / low)
|
|
233
|
+
Not in the report. **Append** each one as a single bullet to
|
|
234
|
+
`.claude/remember/fix-ledger.md` (create the file with the header below if
|
|
235
|
+
missing):
|
|
236
|
+
|
|
237
|
+
```
|
|
238
|
+
# Fix ledger
|
|
239
|
+
> Non-blocking review findings. One bullet per item. Delete the bullet when
|
|
240
|
+
> fixed, or when its anchor no longer exists. Written by /branch-review;
|
|
241
|
+
> consumed by /refactor (ledger mode).
|
|
242
|
+
>
|
|
243
|
+
> A bullet's path may be a glob when the same finding exists in every kit —
|
|
244
|
+
> `git grep -F "<snippet>" -- <path>` accepts one.
|
|
245
|
+
|
|
246
|
+
- `path/file.js` · "verbatim snippet from the line" · what's wrong · failure
|
|
247
|
+
scenario · YYYY-MM-DD @ <short sha>
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
**A ledger bullet's failure scenario is subject to stage 3 like any other.**
|
|
251
|
+
Ledger items skip the report, so they are easy to skip verifying too, and an
|
|
252
|
+
unverified consequence written in the bullet's voice reads as established
|
|
253
|
+
fact to whoever fixes it later. Either confirm it, or prefix the scenario
|
|
254
|
+
with `UNVERIFIED:` so `/refactor` retests before acting.
|
|
162
255
|
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
256
|
+
The **snippet is the anchor**: 20–60 verbatim characters from the line,
|
|
257
|
+
unique enough for `git grep -F` to find it after lines shift. No line
|
|
258
|
+
numbers, no TODO comments in code — the ledger is the single writer. Before
|
|
259
|
+
appending, dedupe with **plain `grep -F "<snippet>" .claude/remember/fix-ledger.md`**;
|
|
260
|
+
if it is already there, skip it. Do not touch existing bullets.
|
|
166
261
|
|
|
167
|
-
|
|
168
|
-
|
|
262
|
+
**A bullet you disprove is deleted, not annotated.** If you establish that an
|
|
263
|
+
existing bullet's finding no longer holds — or never did — remove the line and
|
|
264
|
+
say why in your report. The ledger is a work list, not an archive: an
|
|
265
|
+
annotated bullet still reads as work, and a bullet arguing with itself is
|
|
266
|
+
worse than none. Deleting on disproof is the one case where a reviewer may
|
|
267
|
+
remove a line, and it is the same judgement `/refactor` makes at
|
|
268
|
+
revalidation. Use plain
|
|
269
|
+
`grep`, never `git grep`, on the ledger: the ledger is normally gitignored,
|
|
270
|
+
and `git grep` searches tracked content only, so it reports "not found" for a
|
|
271
|
+
snippet that is sitting right there — the dedupe would pass every time and
|
|
272
|
+
the same finding would be appended on every run.
|
|
273
|
+
|
|
274
|
+
Each blocking finding: **Location** (`file:line`) · **What's wrong** ·
|
|
275
|
+
**Failure scenario** (inputs/state → result) · **Why it matters** ·
|
|
276
|
+
**Suggested fix** (described, not applied) · **Verdict** (confirmed /
|
|
277
|
+
uncertain).
|
|
278
|
+
|
|
279
|
+
Then a coverage line: stage 1 at level `<level>`, stage 2 full, stage 3 —
|
|
280
|
+
each `ran ✓/✗` with its evidence. A stage you did not actually run is a **✗**, never an
|
|
169
281
|
assumed pass.
|
|
170
282
|
|
|
283
|
+
**Write the review record** to `.claude/remember/last-review.md`, overwriting
|
|
284
|
+
it. `/release` reads this file; a SHA that lives only in a chat message is
|
|
285
|
+
gone after a compaction or a handover, and the only remaining source is the
|
|
286
|
+
orchestrator — the one party this command already refuses to take a review's
|
|
287
|
+
word from. **Write it at the end of every run, unconditionally** — not after
|
|
288
|
+
someone decides what to do about it. The information exists now, and the file
|
|
289
|
+
earns its keep only by surviving a compaction, an abandoned session, or a
|
|
290
|
+
handover to someone who never saw the report.
|
|
291
|
+
|
|
292
|
+
```
|
|
293
|
+
sha: <full HEAD sha>
|
|
294
|
+
branch: <branch>
|
|
295
|
+
target: <resolved range or path>
|
|
296
|
+
level: <low | medium | high | max>
|
|
297
|
+
verdict: <ready | blocked>
|
|
298
|
+
date: <YYYY-MM-DD>
|
|
299
|
+
coverage: stage1 <ran|NOT RUN>, stage2 <ran|NOT RUN>, stage3 <ran|NOT RUN>
|
|
300
|
+
blockers:
|
|
301
|
+
- <file:line> · <one-sentence claim, no scenario, no suggested fix>
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
`blockers: none` when the verdict is ready. One line per blocker and nothing
|
|
305
|
+
more: the reasoning belongs in the report, and the non-blocking findings
|
|
306
|
+
belong in the ledger. This exists so a session that never saw the report can
|
|
307
|
+
learn *what* is blocked, not just *that* something is — otherwise the next
|
|
308
|
+
run rediscovers it by re-reviewing the branch, which is the
|
|
309
|
+
non-convergence this command exists to stop.
|
|
310
|
+
|
|
311
|
+
`coverage` is recorded because a `ready` from a run whose security stage did
|
|
312
|
+
not execute is not the same fact as one where it did, and the reader of this
|
|
313
|
+
file cannot tell them apart otherwise.
|
|
314
|
+
|
|
315
|
+
**There is no override field, and no `verdict: overridden`.** A SHA is
|
|
316
|
+
checkable by anyone; consent is not, so a consent line in a file is forgeable
|
|
317
|
+
by whatever writes the file — and a persisted override is reusable, silently
|
|
318
|
+
covering the next release as well as this one. Releasing over a blocked
|
|
319
|
+
review is a live decision made at `/release`'s hand-back, in conversation.
|
|
320
|
+
|
|
321
|
+
**Nothing clears this file.** It is overwritten whole on the next run, and the
|
|
322
|
+
`sha:` line is what expires it: fix something, commit, and the recorded hash
|
|
323
|
+
no longer matches HEAD, so the gate reports *stale* and asks for a
|
|
324
|
+
re-review rather than *blocked*. A blocked verdict can only persist while HEAD
|
|
325
|
+
does not move — which means nothing was fixed, which is the correct outcome.
|
|
326
|
+
|
|
171
327
|
End with:
|
|
172
328
|
- **Reviewed at HEAD `<sha>` on `<branch>`, target `<resolved range or path>`,
|
|
173
|
-
tree clean at start
|
|
329
|
+
tree clean at start; at exit clean or the two `.claude/remember/` paths
|
|
330
|
+
only.**
|
|
331
|
+
- **Fix ledger: N open, M added this run** (N = bullet count). When N > 0,
|
|
332
|
+
add: "N fixes waiting — run `/refactor` between features." The ledger is a
|
|
333
|
+
local artifact; in the usual case it is gitignored, so writing it moves
|
|
334
|
+
nothing and leaves HEAD untouched.
|
|
174
335
|
- One-line verdict: **Ready to merge? Yes / No / Not until these are fixed.**
|
|
336
|
+
- **A run that produces no record is not a review.** If you die mid-flight —
|
|
337
|
+
a rate limit, a crash, a cancelled turn — there is no report and no
|
|
338
|
+
`last-review.md`, and silence must never be read as a pass. `/release`
|
|
339
|
+
already treats a missing record as no review; state it here too so nobody
|
|
340
|
+
fills the gap from memory of a run that never finished.
|
|
175
341
|
- **Escalate to the orchestrator** with the findings. It decides what gets
|
|
176
342
|
fixed and by whom. Say plainly what you could not verify.
|
|
@@ -103,6 +103,18 @@ function fenceMask(lines) {
|
|
|
103
103
|
// prose), sentences()'s own regex strip, and checkCitations/checkLinks doing none at all — so
|
|
104
104
|
// a page documenting the citation/link syntax INSIDE a fence got its own example flagged as
|
|
105
105
|
// a real violation. One mechanism: mask with fenceMask(), drop the masked lines.
|
|
106
|
+
// `text.split('\n')` returns a trailing EMPTY element for any file ending in a newline —
|
|
107
|
+
// which is nearly every file — so `lines.length` is real_lines + 1. That phantom line reached
|
|
108
|
+
// the index row's "N lines", the last H2's range (one line past EOF) and scan()'s outline.json.
|
|
109
|
+
// Use this wherever lines are COUNTED or a range is BOUNDED. The raw `.split('\n')` is still
|
|
110
|
+
// correct where the array is mapped and re-joined back into file text (stripFences,
|
|
111
|
+
// replaceOutsideFences): dropping the element there would strip the file's final newline.
|
|
112
|
+
function splitLines(text) {
|
|
113
|
+
const lines = text.split('\n');
|
|
114
|
+
if (lines.length && lines[lines.length - 1] === '') lines.pop();
|
|
115
|
+
return lines;
|
|
116
|
+
}
|
|
117
|
+
|
|
106
118
|
function stripFences(text) {
|
|
107
119
|
const lines = text.split('\n');
|
|
108
120
|
const mask = fenceMask(lines);
|
|
@@ -229,7 +241,7 @@ function scan(files) {
|
|
|
229
241
|
if (!files.length) die('usage: docs-builder.cjs scan <file.md...>');
|
|
230
242
|
const records = [];
|
|
231
243
|
for (const f of files) {
|
|
232
|
-
const lines = read(f)
|
|
244
|
+
const lines = splitLines(read(f));
|
|
233
245
|
const mask = fenceMask(lines);
|
|
234
246
|
const { h1, heads } = headings(lines, mask);
|
|
235
247
|
const h2s = heads.filter(h => h.lvl === 2);
|
|
@@ -522,8 +534,13 @@ const MIN_PAGE_LINES = 10;
|
|
|
522
534
|
function pageStatus(file) {
|
|
523
535
|
if (!fs.existsSync(file)) return 'TODO';
|
|
524
536
|
const txt = fs.readFileSync(file, 'utf8');
|
|
525
|
-
const lines = txt
|
|
526
|
-
|
|
537
|
+
const lines = splitLines(txt);
|
|
538
|
+
// splitLines() returns [] for a 0-byte file — 0 lines is the right COUNT, but it means
|
|
539
|
+
// lines[0] can be undefined, where the old raw split('\n') always yielded ['']. An empty
|
|
540
|
+
// page is reachable (a touched placeholder, or page-writing interrupted before it wrote
|
|
541
|
+
// anything) and must read as PARTIAL, not throw and take `plan` down with it.
|
|
542
|
+
const hasFrontmatter = lines.length > 0 && lines[0].trim() === '---'
|
|
543
|
+
&& lines.slice(1).some(l => l.trim() === '---');
|
|
527
544
|
return hasFrontmatter && lines.length >= MIN_PAGE_LINES ? 'done' : 'PARTIAL';
|
|
528
545
|
}
|
|
529
546
|
|
|
@@ -732,7 +749,7 @@ const ARCHIVE_WARN_ROWS = 100; // stated default, not measured — see docs-buil
|
|
|
732
749
|
// section a reader is being routed into, so its row stays H1 + line count + link only.
|
|
733
750
|
function indexRow(rel, dest, includeH2) {
|
|
734
751
|
const text = read(rel);
|
|
735
|
-
const lines = text
|
|
752
|
+
const lines = splitLines(text);
|
|
736
753
|
// Same headings()+fenceMask() path scan() uses -- no second parser -- so an H2 inside a
|
|
737
754
|
// ``` fence is masked out here exactly as it is there.
|
|
738
755
|
const mask = fenceMask(lines);
|
|
@@ -1398,7 +1415,7 @@ function ledger() {
|
|
|
1398
1415
|
const head = git(['rev-parse', 'HEAD'], 'reading HEAD (is this a git repo?)');
|
|
1399
1416
|
const docs = docFiles().map(f => ({
|
|
1400
1417
|
path: f,
|
|
1401
|
-
lines: read(f)
|
|
1418
|
+
lines: splitLines(read(f)).length,
|
|
1402
1419
|
sha256: sha(path.join(REPO, f)).slice(0, 16)
|
|
1403
1420
|
}));
|
|
1404
1421
|
const out = { sha: head, at: new Date().toISOString(),
|
|
@@ -1492,7 +1509,7 @@ function lint(files) {
|
|
|
1492
1509
|
if (!files.length) die('usage: docs-builder.cjs lint <file.md...>');
|
|
1493
1510
|
const sections = [];
|
|
1494
1511
|
for (const f of files) {
|
|
1495
|
-
const lines = read(f)
|
|
1512
|
+
const lines = splitLines(read(f));
|
|
1496
1513
|
const mask = fenceMask(lines);
|
|
1497
1514
|
let cur = null;
|
|
1498
1515
|
const close = i => { if (cur) { cur.e = i; cur.body = lines.slice(cur.s, i).join('\n'); } };
|
|
@@ -1665,7 +1682,7 @@ function isIncludeStub(lines) {
|
|
|
1665
1682
|
// but its size. Oversized is now orthogonal to sorting: a product doc that's too big is
|
|
1666
1683
|
// still a product doc.
|
|
1667
1684
|
function classifyDoc(rel, text) {
|
|
1668
|
-
const lines = text
|
|
1685
|
+
const lines = splitLines(text);
|
|
1669
1686
|
const mask = fenceMask(lines);
|
|
1670
1687
|
const { h1 } = headings(lines, mask);
|
|
1671
1688
|
const snip = snippet(lines, mask, 0, lines.length, 200);
|
|
@@ -2214,7 +2231,7 @@ function cleanup(files) {
|
|
|
2214
2231
|
+ `would overwrite that split's still-in-flight outline.json/labels.json. Finish it `
|
|
2215
2232
|
+ `first: write its remaining pages, then re-run \`cleanup-apply ${inFlight} ...\` until `
|
|
2216
2233
|
+ `it archives — THEN run \`cleanup ${file}\`.`);
|
|
2217
|
-
const lines = read(file)
|
|
2234
|
+
const lines = splitLines(read(file)).length;
|
|
2218
2235
|
const est = writeCostEstimate(1, lines);
|
|
2219
2236
|
console.log(`${file}: ${lines} lines`);
|
|
2220
2237
|
console.log(`est. write cost: $${est.toFixed(2)} (mid tier, floor assuming 1 page — the `
|
|
@@ -1,12 +1,80 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: refactor
|
|
3
3
|
description: Refactor [code]
|
|
4
|
-
usage: /refactor <code-section>
|
|
5
|
-
argument-hint: [file-or-function]
|
|
6
|
-
allowed-tools: Read, Edit, Grep, Glob, Bash(npm test *), Bash(npx jest *), Bash(npx vitest *), Bash(pnpm test *), Bash(yarn test *), Bash(pytest *), Bash(python *), Bash(go test *), Bash(cargo test *), Bash(make test *), Bash(git diff *)
|
|
4
|
+
usage: /refactor <code-section> | /refactor (no args = fix-ledger mode)
|
|
5
|
+
argument-hint: [file-or-function, or empty for the fix ledger]
|
|
6
|
+
allowed-tools: Read, Edit, Grep, Glob, Bash(npm test *), Bash(npx jest *), Bash(npx vitest *), Bash(pnpm test *), Bash(yarn test *), Bash(pytest *), Bash(python *), Bash(go test *), Bash(cargo test *), Bash(make test *), Bash(git diff *), Bash(git grep *), Bash(git status *), Bash(git rev-parse *), Bash(git switch *)
|
|
7
7
|
---
|
|
8
8
|
Refactor $ARGUMENTS.
|
|
9
9
|
|
|
10
|
+
## Guardrails
|
|
11
|
+
- **Spawn a worker and explicitly select your tool's mid tier.** State the
|
|
12
|
+
tier on the spawn — do not omit it and rely on a default. An omitted tier
|
|
13
|
+
inherits the *parent's* tier, which is not the same thing as the balanced
|
|
14
|
+
one. Pick the judgment-capable tier that is cheaper and faster than your top
|
|
15
|
+
reasoning tier. **Not the cheapest/fastest tier**: on judgment work it
|
|
16
|
+
measurably degrades (misclassification rates several times higher). Choose by
|
|
17
|
+
tier, not by a vendor model name copied from this file — names drift, and
|
|
18
|
+
this command ships to several tools. Fall back to running inline if your tool
|
|
19
|
+
has no subagent mechanism.
|
|
20
|
+
- **Escalate, never assume.** Anything you cannot decide, cannot verify, or
|
|
21
|
+
that this spec does not cover → **stop and report it to the orchestrator**
|
|
22
|
+
(the main session). Never improvise, never widen scope, never fix a side
|
|
23
|
+
issue you noticed along the way.
|
|
24
|
+
- **The worker does the work itself — no delegation.** The fixer must **not**
|
|
25
|
+
spawn subagents of its own. Every edit it reports, and every test run it
|
|
26
|
+
cites, has to be one it made or ran with its own tool calls: a relayed "I
|
|
27
|
+
fixed it and the suite is green" from a sub-worker is hearsay, and this
|
|
28
|
+
command's whole output is the claim that a change landed and the tests still
|
|
29
|
+
pass. A fix that delegates its work is a report about a report.
|
|
30
|
+
- **The HITL gates below belong to the orchestrator, not the worker.** A
|
|
31
|
+
subagent cannot hold a conversation with the user, so it cannot run a gate
|
|
32
|
+
that ends in *stop and ask*. When one trips — a failing test, a crossed
|
|
33
|
+
public API boundary, a change bigger than the bullet asked for — the worker
|
|
34
|
+
**stops there and hands the situation back**, with the options and its
|
|
35
|
+
reasoning but no choice made. The orchestrator asks. A worker that picks
|
|
36
|
+
revert / patch / update-test on the user's behalf has answered a question it
|
|
37
|
+
was never allowed to ask.
|
|
38
|
+
- **Edit only what a surviving bullet names.** Ledger mode's scope is the
|
|
39
|
+
bullets that survive revalidation, one change per bullet — not the
|
|
40
|
+
neighbouring code, not the formatting, not a second finding noticed on the
|
|
41
|
+
way past. Anything else goes back to the orchestrator to become a new
|
|
42
|
+
bullet.
|
|
43
|
+
- **Prove the blast radius with two checks, because neither sees what the
|
|
44
|
+
other does.** `git status --porcelain` at exit must list only files a
|
|
45
|
+
surviving bullet named — that is this command's scope guarantee, and unlike
|
|
46
|
+
`/branch-review` it is not expected to be empty. It cannot police the
|
|
47
|
+
memory directory: `.claude/` is normally gitignored, so porcelain stays
|
|
48
|
+
empty whether you deleted a fixed bullet, wrote nothing, or overwrote
|
|
49
|
+
`MEMORY.md`. So also take `md5sum .claude/remember/*` before you start and
|
|
50
|
+
again before you report, and show the comparison: only `fix-ledger.md` may
|
|
51
|
+
differ. `last-review.md` in particular is `/branch-review`'s to write —
|
|
52
|
+
a fixer that touches it forges the gate that judges its own work.
|
|
53
|
+
|
|
54
|
+
## Ledger mode — `$ARGUMENTS` empty
|
|
55
|
+
Work through `.claude/remember/fix-ledger.md`, the non-blocking findings
|
|
56
|
+
`/branch-review` has accumulated. Everything below (goals, constraints,
|
|
57
|
+
verification, HITL gates) still applies; this section only says what to
|
|
58
|
+
refactor and how to close each item.
|
|
59
|
+
|
|
60
|
+
1. **Tree must be clean and not on `main`.** The orchestrator runs this check
|
|
61
|
+
before spawning the worker, so a dirty tree costs no worker; the worker
|
|
62
|
+
then re-runs it as its own first act. `git status --porcelain` non-empty
|
|
63
|
+
→ stop, say what is uncommitted. On `main` → `git switch -c chore/fix-ledger`.
|
|
64
|
+
2. **Ledger missing or has zero bullets** → say so and stop. Nothing to do.
|
|
65
|
+
3. **Revalidate every bullet first, fix nothing yet.** For each: `git grep -F
|
|
66
|
+
"<snippet>" -- <path>`. **No hit → delete the bullet** and list it as
|
|
67
|
+
"cleaned by other work". Hit → re-read the surrounding code; if the finding
|
|
68
|
+
no longer holds, delete the bullet with a one-line reason. What survives is
|
|
69
|
+
the work list.
|
|
70
|
+
4. **Fix the survivors, one bullet per change**, under the constraints below.
|
|
71
|
+
Delete each bullet as its fix lands. A fix that turns out to need a
|
|
72
|
+
behaviour change is not a refactor — leave the bullet, note it in the report.
|
|
73
|
+
5. Run the tests as described below. Then report: **fixed / dropped / left**
|
|
74
|
+
with the reason per left item, and the remaining bullet count.
|
|
75
|
+
6. Say plainly: **commit, then run `/branch-review`** on this branch — ledger
|
|
76
|
+
mode is a fixer, not a review, and its diff gets the ordinary gate.
|
|
77
|
+
|
|
10
78
|
## Goals
|
|
11
79
|
- Reduce complexity
|
|
12
80
|
- Improve readability
|