liteagents 2.21.1 → 2.22.1
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 +51 -0
- package/package.json +1 -1
- package/packages/ampcode/commands/branch-review.md +68 -15
- package/packages/ampcode/commands/docs-builder/docs-builder.cjs +25 -8
- package/packages/ampcode/commands/release.md +55 -19
- package/packages/ampcode/commands/stash.md +6 -5
- package/packages/claude/commands/branch-review.md +68 -15
- package/packages/claude/commands/docs-builder/docs-builder.cjs +25 -8
- package/packages/claude/commands/release.md +55 -19
- package/packages/claude/commands/stash.md +6 -5
- package/packages/droid/commands/branch-review.md +68 -15
- package/packages/droid/commands/docs-builder/docs-builder.cjs +25 -8
- package/packages/droid/commands/release.md +55 -19
- package/packages/droid/commands/stash.md +6 -5
- package/packages/opencode/command/branch-review.md +68 -15
- package/packages/opencode/command/docs-builder/docs-builder.cjs +25 -8
- package/packages/opencode/command/release.md +55 -19
- package/packages/opencode/command/stash.md +6 -5
package/CHANGELOG.md
CHANGED
|
@@ -9,6 +9,57 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
## [2.22.1] - 2026-09-01
|
|
13
|
+
|
|
14
|
+
### Fixed
|
|
15
|
+
- **`/docs-builder` — the trailing-newline phantom line is dropped from every line count.**
|
|
16
|
+
`text.split('\n')` returns a trailing empty element for any file ending in a newline, so
|
|
17
|
+
`lines.length` was one over the real count. That phantom line reached the index row's "N
|
|
18
|
+
lines" (every row +1), the last H2's line range (one line past EOF), `scan`'s outline.json
|
|
19
|
+
`s`/`e`/`lines`, the ledger's per-file line count, the cleanup cost estimate, and the PARTIAL
|
|
20
|
+
guard (a page one line short of `MIN_PAGE_LINES` passed as complete). Fixed with a
|
|
21
|
+
`splitLines()` helper applied at the 7 counting/bounding sites, deliberately not at the sites
|
|
22
|
+
that map-and-rejoin file text. `docs/index.md` regenerated. Mirrored across all four kits.
|
|
23
|
+
- **`/docs-builder` — an empty page now reports PARTIAL instead of crashing `plan`.** A
|
|
24
|
+
regression from the fix above: `splitLines()` returns `[]` for a 0-byte file where the raw
|
|
25
|
+
split returned `['']`, so `pageStatus`'s unguarded `lines[0].trim()` threw a `TypeError` and
|
|
26
|
+
took `plan` down with it. An empty `.md` page is reachable (a touched placeholder, or
|
|
27
|
+
page-writing interrupted). Fixed at the indexing site — 0 lines is the correct count for an
|
|
28
|
+
empty file — so `pageStatus` guards on `lines.length` instead. Mirrored across all four kits.
|
|
29
|
+
|
|
30
|
+
## [2.22.0] - 2026-09-01
|
|
31
|
+
|
|
32
|
+
### Fixed
|
|
33
|
+
- **`/branch-review` — dirty tree is now a hard stop, not a silent partial review.** The
|
|
34
|
+
staged/working-tree fallback only fired when the earlier step was empty, so a branch with
|
|
35
|
+
both committed and uncommitted changes reviewed the commits and silently skipped the
|
|
36
|
+
uncommitted lines. It now reports the resolved review target and re-checks
|
|
37
|
+
`git status --porcelain` at exit as well as at start, in all four kits.
|
|
38
|
+
- **`/release` — Phase 0 no longer commits a dirty tree for the user.** Committing on the
|
|
39
|
+
user's behalf produced a commit made *after* the review, which Phase 0.5 then had no way to
|
|
40
|
+
accept — a dirty tree is now a stop, with the fix pushed back to the user
|
|
41
|
+
(`git add`/`git commit`, re-run `/branch-review`, then `/release`), in all four kits.
|
|
42
|
+
- **`/release` Phase 0.5 compares SHAs mechanically instead of asking the orchestrator to
|
|
43
|
+
judge it.** The worker now runs `git rev-parse HEAD` itself and compares it against the
|
|
44
|
+
review's recorded SHA — no recorded SHA, or a mismatch, is a stop, never a "looks close
|
|
45
|
+
enough" pass.
|
|
46
|
+
|
|
47
|
+
### Changed
|
|
48
|
+
- **`/branch-review` — test quality is a stage-1 item.** A test's ability to fail is proven by
|
|
49
|
+
reverting the *source* change outside the repo, not by reverting the test itself; commit
|
|
50
|
+
messages are treated as claims to re-verify, never as evidence on their own.
|
|
51
|
+
- **`/branch-review` — the review worker may not spawn sub-workers**, and the verdict line is
|
|
52
|
+
now printed at both the top and the bottom of the report.
|
|
53
|
+
- **`/release` — records local vs. published version in Phase 0**, and Phase 3 treats a gap
|
|
54
|
+
between them as the ask-if-ambiguous trigger for picking a version. The release commit made
|
|
55
|
+
in Phase 3 is documented as the one commit `/release` is permitted to make post-review.
|
|
56
|
+
Documents that `gh pr merge` requires an explicit merge-method flag (`--squash` /
|
|
57
|
+
`--merge` / `--rebase`) or it will not merge.
|
|
58
|
+
- **`/branch-review`, `/release`, `/stash` — tier guidance no longer self-contradicts.**
|
|
59
|
+
"Balanced default tier" plus "never hardcode a vendor name" resolved, in practice, to
|
|
60
|
+
whatever tier the parent session happened to be running at. All three now say: explicitly
|
|
61
|
+
select your tool's mid tier and state it on the spawn.
|
|
62
|
+
|
|
12
63
|
## [2.21.1] - 2026-08-30
|
|
13
64
|
|
|
14
65
|
### Changed
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "liteagents",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.22.1",
|
|
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": {
|
|
@@ -14,31 +14,61 @@ Run this **before** `/release`. `/release` will refuse to run without a review
|
|
|
14
14
|
at the current HEAD SHA.
|
|
15
15
|
|
|
16
16
|
## Guardrails
|
|
17
|
-
- **Spawn a worker
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
judgment
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
- **Spawn a worker and explicitly select your tool's mid tier.** State the
|
|
18
|
+
tier on the spawn — do not omit it and rely on a default. An omitted tier
|
|
19
|
+
inherits the *parent's* tier, which is not the same thing as the balanced
|
|
20
|
+
one. Pick the judgment-capable tier that is cheaper and faster than your top
|
|
21
|
+
reasoning tier. **Not the cheapest/fastest tier**: on judgment work it
|
|
22
|
+
measurably degrades (misclassification rates several times higher). Choose by
|
|
23
|
+
tier, not by a vendor model name copied from this file — names drift, and
|
|
24
|
+
this command ships to several tools. Fall back to running inline if your tool
|
|
25
|
+
has no subagent mechanism.
|
|
23
26
|
- **Escalate, never assume.** Anything you cannot decide, cannot verify, or
|
|
24
27
|
that this spec does not cover → **stop and report it to the orchestrator**
|
|
25
28
|
(the main session). Never improvise, never widen scope, never fix a side
|
|
26
29
|
issue you noticed along the way.
|
|
30
|
+
- **The worker does the work itself — no delegation.** The review subagent
|
|
31
|
+
must **not** spawn subagents of its own. Everything it reports has to be
|
|
32
|
+
something it read, ran, or grepped with its own tool calls: a relayed "I
|
|
33
|
+
executed X" from a sub-worker is hearsay, and replacing hearsay with evidence
|
|
34
|
+
is the entire point of this command. A review that delegates its work is a
|
|
35
|
+
review of a report. (Same rule `/security` carries inside stage 2.)
|
|
27
36
|
- **No edits.** You have no authorization to change code, even for a finding
|
|
28
|
-
you are certain about. Report it.
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
you are certain about. Report it. Re-run `git status --porcelain` before you
|
|
38
|
+
report and confirm it is still empty — if it is not, say what changed. That
|
|
39
|
+
turns "it never edits" from a claim into a checked fact.
|
|
40
|
+
|
|
41
|
+
## Target — check the tree first, then interpret `$ARGUMENTS`
|
|
42
|
+
|
|
43
|
+
**Before resolving anything, run `git status --porcelain`.** If it prints any
|
|
44
|
+
line — modified, staged, or untracked — **stop and report it**. Say all three
|
|
45
|
+
things, not just the first: (a) the tree is dirty, listing the uncommitted
|
|
46
|
+
paths; (b) `/branch-review` reviews commits, not the working tree; (c) **commit
|
|
47
|
+
the work to the branch, then re-run `/branch-review`.** A stop that names the
|
|
48
|
+
problem without the remedy invites the orchestrator to stash the changes or
|
|
49
|
+
hand-review the working tree instead. Do not review a subset and do not fall
|
|
50
|
+
back to the staged diff or the working tree. A dirty
|
|
51
|
+
tree is an **error**, never a silent partial review — the most expensive
|
|
52
|
+
failure this command can have is reviewing 800 committed lines while 200
|
|
53
|
+
uncommitted lines of today's actual work go unread.
|
|
54
|
+
|
|
55
|
+
This is forced by the design, not a preference: `/release`'s precondition is a
|
|
56
|
+
review at the current HEAD SHA, and any commit made after the review makes it
|
|
57
|
+
stale. **The only correct order is commit → review → release.**
|
|
58
|
+
|
|
59
|
+
With a clean tree, interpret `$ARGUMENTS` in this order:
|
|
31
60
|
1. **Empty** → the current branch vs its merge-base with `main`
|
|
32
|
-
(`git diff $(git merge-base main HEAD)..HEAD`). If that is empty
|
|
33
|
-
|
|
61
|
+
(`git diff $(git merge-base main HEAD)..HEAD`). If that is empty there is
|
|
62
|
+
nothing committed to review — say so and stop.
|
|
34
63
|
2. **A range** like `main..HEAD` or `origin/main...HEAD` → `git diff <range>`.
|
|
35
64
|
3. **A single ref** (branch / tag / SHA — confirm with `git rev-parse
|
|
36
65
|
--verify`) → that ref's merge-base against `HEAD`.
|
|
37
66
|
4. **A file or directory path** → that target.
|
|
38
67
|
5. Otherwise → ask.
|
|
39
68
|
|
|
40
|
-
Record the **HEAD SHA** you reviewed
|
|
41
|
-
|
|
69
|
+
Record the **HEAD SHA** you reviewed, and **report the target you resolved**
|
|
70
|
+
(the literal range or path) in your output, so the orchestrator can see what
|
|
71
|
+
was actually read rather than assuming.
|
|
42
72
|
|
|
43
73
|
## Effort level
|
|
44
74
|
`low | medium | high | max` — default **medium** if not given. The level
|
|
@@ -57,6 +87,13 @@ hunk-only read cannot see that a caller further down the same file is now
|
|
|
57
87
|
wrong. For multi-commit ranges, skim `git log <range>` for intent before
|
|
58
88
|
judging.
|
|
59
89
|
|
|
90
|
+
**Commit messages are claims, not evidence.** A message saying a fix was
|
|
91
|
+
"proven red→green", a bug reproduced, or a test added is something to re-test,
|
|
92
|
+
not a fact to accept. Branches are commonly AI-authored now — including the
|
|
93
|
+
fixes to the fixes — so a review that trusts the message is reviewing prose.
|
|
94
|
+
Run the test suite and the typecheck/build yourself and cite the command and
|
|
95
|
+
its exit code.
|
|
96
|
+
|
|
60
97
|
- **Bugs needing a fix.** Logic errors, off-by-one, null/undefined paths,
|
|
61
98
|
races, wrong defaults, broken edge cases.
|
|
62
99
|
- **Dead code.** Unreferenced functions / vars / imports / params, unreachable
|
|
@@ -69,6 +106,16 @@ judging.
|
|
|
69
106
|
broken invariants.
|
|
70
107
|
- **Performance.** N+1, blocking calls in hot paths, unbounded loops, indexes
|
|
71
108
|
the diff actually touches.
|
|
109
|
+
- **Test quality, not just test presence.** For every test the diff adds or
|
|
110
|
+
changes, establish that it **can actually fail**. Reasoning about
|
|
111
|
+
falsifiability does not work; executing it does. **Revert the source, not the
|
|
112
|
+
test:** take the pre-change version of the file under test with `git show
|
|
113
|
+
<base-sha>:<path>`, run the test against that copy, and watch it go red. Do
|
|
114
|
+
this **without dirtying the branch** — write the old version to a temp
|
|
115
|
+
location outside the repo; the tree must still be clean at exit. A test that
|
|
116
|
+
passes against both the buggy and the fixed source is a tautology and proves
|
|
117
|
+
nothing. Flag every one you find, and say so explicitly when the tests are
|
|
118
|
+
the branch's only evidence for its claims.
|
|
72
119
|
- **Maintainability.** Complexity, naming, duplication — only when material.
|
|
73
120
|
|
|
74
121
|
## Stage 2 — Security (always full)
|
|
@@ -102,7 +149,12 @@ cannot write that sentence, the finding is not ready — drop it or mark it
|
|
|
102
149
|
uncertain. No vibes.
|
|
103
150
|
|
|
104
151
|
## Report — then escalate
|
|
105
|
-
|
|
152
|
+
**Open with the one-line verdict**, before any section: **Ready to merge? Yes /
|
|
153
|
+
No / Not until these are fixed.** A report that opens with "Critical: none
|
|
154
|
+
found" reads as a pass at a glance even when the verdict is not one — state the
|
|
155
|
+
verdict first, then repeat it at the end.
|
|
156
|
+
|
|
157
|
+
Then the findings, ordered most severe first.
|
|
106
158
|
|
|
107
159
|
### 🚨 Critical (blocks merge)
|
|
108
160
|
### ⚠️ Warnings (should fix)
|
|
@@ -117,7 +169,8 @@ with its evidence. A stage you did not actually run is a **✗**, never an
|
|
|
117
169
|
assumed pass.
|
|
118
170
|
|
|
119
171
|
End with:
|
|
120
|
-
- **Reviewed at HEAD `<sha>` on `<branch
|
|
172
|
+
- **Reviewed at HEAD `<sha>` on `<branch>`, target `<resolved range or path>`,
|
|
173
|
+
tree clean at start and at exit.**
|
|
121
174
|
- One-line verdict: **Ready to merge? Yes / No / Not until these are fixed.**
|
|
122
175
|
- **Escalate to the orchestrator** with the findings. It decides what gets
|
|
123
176
|
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 `
|
|
@@ -13,12 +13,15 @@ It does not re-implement checks, and it does not review code. Review is a
|
|
|
13
13
|
separate command that must have run first.
|
|
14
14
|
|
|
15
15
|
## Guardrails
|
|
16
|
-
- **Spawn a worker
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
judgment
|
|
20
|
-
|
|
21
|
-
|
|
16
|
+
- **Spawn a worker and explicitly select your tool's mid tier.** State the
|
|
17
|
+
tier on the spawn — do not omit it and rely on a default. An omitted tier
|
|
18
|
+
inherits the *parent's* tier, which is not the same thing as the balanced
|
|
19
|
+
one. Pick the judgment-capable tier that is cheaper and faster than your top
|
|
20
|
+
reasoning tier. **Not the cheapest/fastest tier**: on judgment work it
|
|
21
|
+
measurably degrades (misclassification rates several times higher). Choose by
|
|
22
|
+
tier, not by a vendor model name copied from this file — names drift, and
|
|
23
|
+
this command ships to several tools. Fall back to running inline if your tool
|
|
24
|
+
has no subagent mechanism.
|
|
22
25
|
- **Escalate, never assume.** Anything you cannot decide, cannot verify, or
|
|
23
26
|
that this spec does not cover → **stop and report it to the orchestrator**
|
|
24
27
|
(the main session). Never improvise, never widen scope, never fix a finding
|
|
@@ -31,24 +34,45 @@ separate command that must have run first.
|
|
|
31
34
|
- **Release the branch you are on.** No branch argument, no branch creation.
|
|
32
35
|
- **On `main` → stop and ask** what should be released. `main` is only ever
|
|
33
36
|
the merge target; never release it, never commit to it.
|
|
34
|
-
-
|
|
35
|
-
|
|
37
|
+
- **The tree must be clean — do not commit for the user.** Run `git status
|
|
38
|
+
--porcelain`. Any line at all — modified, staged, or untracked — is a
|
|
39
|
+
**stop**: list the paths and say "commit this to the branch, re-run
|
|
40
|
+
`/branch-review`, then re-run `/release`." Committing here would create a
|
|
41
|
+
commit *after* the review and so fail Phase 0.5 by its own rule on the very
|
|
42
|
+
next step; a phase that guarantees the next phase fails is not a phase.
|
|
36
43
|
- `git fetch origin`; the release diff is `origin/main...HEAD`. Empty →
|
|
37
44
|
**stop**, nothing to release.
|
|
38
|
-
-
|
|
45
|
+
- **Record both version numbers.** Read the **local** version (`package.json`
|
|
46
|
+
or this project's equivalent) and, if the project has a publish path, the
|
|
47
|
+
**published** one (`npm view <pkg> version`, or the registry equivalent).
|
|
48
|
+
Report them side by side. Local *ahead* of published means a version was cut
|
|
49
|
+
on a branch and never published — Phase 3 cannot see that gap unless you
|
|
50
|
+
record it here, and a worker that cannot see it will re-cut a number that
|
|
51
|
+
already exists.
|
|
52
|
+
- Print a one-line plan: branch · commit count · files changed · HEAD SHA ·
|
|
53
|
+
local version → published version.
|
|
39
54
|
|
|
40
55
|
## Phase 0.5 — Review precondition (do not skip)
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
56
|
+
A review must have run on this branch **at the current HEAD SHA**.
|
|
57
|
+
|
|
58
|
+
**Compare the SHAs yourself; do not settle for an answer.** Run `git rev-parse
|
|
59
|
+
HEAD` and compare it against the SHA the review recorded — `/branch-review`
|
|
60
|
+
ends with `Reviewed at HEAD <sha>`. Asking the orchestrator "did a review run?"
|
|
61
|
+
puts the question to the one party with an incentive to say yes, so its word is
|
|
62
|
+
not evidence: obtain the review's own recorded SHA and match the two strings.
|
|
63
|
+
**No recorded SHA to compare = no review**, never a pass.
|
|
64
|
+
|
|
65
|
+
- **No review**, or no recorded SHA obtainable → **stop**: "No review at
|
|
66
|
+
`<sha>`. Run `/branch-review medium` (or `/code-review medium`) first."
|
|
67
|
+
- **Stale** — recorded SHA ≠ `git rev-parse HEAD`, i.e. commits landed after
|
|
68
|
+
the review (including fix commits) → **stop** and ask for a re-review. This
|
|
69
|
+
is what makes "all findings fixed" checkable instead of promised.
|
|
49
70
|
- **Reviewed at this SHA with findings outstanding** → **stop**. Findings are
|
|
50
71
|
resolved before a release is cut.
|
|
51
72
|
|
|
73
|
+
Report the comparison you actually ran: recorded `<sha>` vs HEAD `<sha>`,
|
|
74
|
+
match yes/no.
|
|
75
|
+
|
|
52
76
|
This is the only thing guaranteeing the branch was reviewed *and* security
|
|
53
77
|
scanned, so treat a missing answer as a **stop**, never as a pass.
|
|
54
78
|
|
|
@@ -90,7 +114,11 @@ If a doc needs no change, **say so** rather than editing it for its own sake.
|
|
|
90
114
|
|
|
91
115
|
## Phase 3 — Cut (local only)
|
|
92
116
|
1. **Version bump** — pick the semver level from the change (patch / minor /
|
|
93
|
-
major; **ask if ambiguous**) and update `package.json`. The
|
|
117
|
+
major; **ask if ambiguous**) and update `package.json`. The local-vs-
|
|
118
|
+
published gap you recorded in Phase 0 **is** an ambiguity: if local is ahead
|
|
119
|
+
of published, a version was cut and never published, so ask whether to
|
|
120
|
+
publish that number or bump past it. Never silently re-cut a version that
|
|
121
|
+
already exists locally. The bump must land
|
|
94
122
|
on the branch, before any merge — a version committed to `main` directly,
|
|
95
123
|
or added after the merge, breaks the tag/package match.
|
|
96
124
|
2. **Commit** — `release: vX.Y.Z — <summary>`, including the docs and the
|
|
@@ -98,6 +126,12 @@ If a doc needs no change, **say so** rather than editing it for its own sake.
|
|
|
98
126
|
|
|
99
127
|
Then **stop.** Nothing else.
|
|
100
128
|
|
|
129
|
+
This release commit is the **one** commit allowed to land after the review, and
|
|
130
|
+
only because it contains docs and a version number — no code, so it cannot
|
|
131
|
+
invalidate a finding. It does move HEAD past the reviewed SHA, which is why
|
|
132
|
+
`/release` must not be run twice on the same branch without a re-review: the
|
|
133
|
+
second run will correctly stop as stale.
|
|
134
|
+
|
|
101
135
|
## Report — the sequence, for a human to authorize
|
|
102
136
|
Print the evidence, then hand back the exact remaining steps so the
|
|
103
137
|
orchestrator can run them on the user's named go:
|
|
@@ -108,7 +142,9 @@ orchestrator can run them on the user's named go:
|
|
|
108
142
|
> 1. `git push -u origin <branch>`
|
|
109
143
|
> 2. `gh pr create` into `main`
|
|
110
144
|
> 3. `gh pr merge --admin --squash --delete-branch` (main is PR-protected;
|
|
111
|
-
> owner-authorized admin merge on a solo repo)
|
|
145
|
+
> owner-authorized admin merge on a solo repo). **Keep `--squash`** — `gh`
|
|
146
|
+
> requires an explicit merge-method flag (`--squash` / `--merge` /
|
|
147
|
+
> `--rebase`); drop it and the command will not squash-merge.
|
|
112
148
|
> 4. `git tag vX.Y.Z` on `main` and push the tag
|
|
113
149
|
> 5. Publish **if this project has a publish path** (e.g.
|
|
114
150
|
> `gh workflow run publish.yml`) — manual by design
|
|
@@ -15,11 +15,12 @@ Save session context for compaction recovery or handoffs.
|
|
|
15
15
|
- **Escalate, never assume.** Anything the subagent cannot do, cannot verify, or that this
|
|
16
16
|
spec does not cover → report it back to the orchestrator (the main session) rather than
|
|
17
17
|
improvising. Never widen scope beyond writing the file and counting the backlog.
|
|
18
|
-
- **
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
18
|
+
- **Explicitly select your tool's mid tier.** State the tier on the spawn — do not omit it
|
|
19
|
+
and rely on a default. An omitted tier inherits the *parent's* tier, which is not the same
|
|
20
|
+
thing as the balanced one. Pick the judgment-capable tier that is cheaper and faster than
|
|
21
|
+
your top reasoning tier. **Not the cheapest/fastest tier**: on judgment work it measurably
|
|
22
|
+
degrades (misclassification rates several times higher). Choose by tier, not by a vendor
|
|
23
|
+
model name copied from this file — names drift, and this command ships to several tools.
|
|
23
24
|
- **Background dispatch where supported.** Run the write-up subagent in the background
|
|
24
25
|
(non-blocking) so the session isn't held up waiting on formatting/file I/O. Fall back to
|
|
25
26
|
writing inline (today's behavior) if your tool has no subagent or background-dispatch
|
|
@@ -14,31 +14,61 @@ Run this **before** `/release`. `/release` will refuse to run without a review
|
|
|
14
14
|
at the current HEAD SHA.
|
|
15
15
|
|
|
16
16
|
## Guardrails
|
|
17
|
-
- **Spawn a worker
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
judgment
|
|
21
|
-
|
|
22
|
-
|
|
17
|
+
- **Spawn a worker and explicitly select your tool's mid tier.** State the
|
|
18
|
+
tier on the spawn — do not omit it and rely on a default. An omitted tier
|
|
19
|
+
inherits the *parent's* tier, which is not the same thing as the balanced
|
|
20
|
+
one. Pick the judgment-capable tier that is cheaper and faster than your top
|
|
21
|
+
reasoning tier. **Not the cheapest/fastest tier**: on judgment work it
|
|
22
|
+
measurably degrades (misclassification rates several times higher). Choose by
|
|
23
|
+
tier, not by a vendor model name copied from this file — names drift, and
|
|
24
|
+
this command ships to several tools. Fall back to running inline if your tool
|
|
25
|
+
has no subagent mechanism.
|
|
23
26
|
- **Escalate, never assume.** Anything you cannot decide, cannot verify, or
|
|
24
27
|
that this spec does not cover → **stop and report it to the orchestrator**
|
|
25
28
|
(the main session). Never improvise, never widen scope, never fix a side
|
|
26
29
|
issue you noticed along the way.
|
|
30
|
+
- **The worker does the work itself — no delegation.** The review subagent
|
|
31
|
+
must **not** spawn subagents of its own. Everything it reports has to be
|
|
32
|
+
something it read, ran, or grepped with its own tool calls: a relayed "I
|
|
33
|
+
executed X" from a sub-worker is hearsay, and replacing hearsay with evidence
|
|
34
|
+
is the entire point of this command. A review that delegates its work is a
|
|
35
|
+
review of a report. (Same rule `/security` carries inside stage 2.)
|
|
27
36
|
- **No edits.** You have no authorization to change code, even for a finding
|
|
28
|
-
you are certain about. Report it.
|
|
29
|
-
|
|
30
|
-
|
|
37
|
+
you are certain about. Report it. Re-run `git status --porcelain` before you
|
|
38
|
+
report and confirm it is still empty — if it is not, say what changed. That
|
|
39
|
+
turns "it never edits" from a claim into a checked fact.
|
|
40
|
+
|
|
41
|
+
## Target — check the tree first, then interpret `$ARGUMENTS`
|
|
42
|
+
|
|
43
|
+
**Before resolving anything, run `git status --porcelain`.** If it prints any
|
|
44
|
+
line — modified, staged, or untracked — **stop and report it**. Say all three
|
|
45
|
+
things, not just the first: (a) the tree is dirty, listing the uncommitted
|
|
46
|
+
paths; (b) `/branch-review` reviews commits, not the working tree; (c) **commit
|
|
47
|
+
the work to the branch, then re-run `/branch-review`.** A stop that names the
|
|
48
|
+
problem without the remedy invites the orchestrator to stash the changes or
|
|
49
|
+
hand-review the working tree instead. Do not review a subset and do not fall
|
|
50
|
+
back to the staged diff or the working tree. A dirty
|
|
51
|
+
tree is an **error**, never a silent partial review — the most expensive
|
|
52
|
+
failure this command can have is reviewing 800 committed lines while 200
|
|
53
|
+
uncommitted lines of today's actual work go unread.
|
|
54
|
+
|
|
55
|
+
This is forced by the design, not a preference: `/release`'s precondition is a
|
|
56
|
+
review at the current HEAD SHA, and any commit made after the review makes it
|
|
57
|
+
stale. **The only correct order is commit → review → release.**
|
|
58
|
+
|
|
59
|
+
With a clean tree, interpret `$ARGUMENTS` in this order:
|
|
31
60
|
1. **Empty** → the current branch vs its merge-base with `main`
|
|
32
|
-
(`git diff $(git merge-base main HEAD)..HEAD`). If that is empty
|
|
33
|
-
|
|
61
|
+
(`git diff $(git merge-base main HEAD)..HEAD`). If that is empty there is
|
|
62
|
+
nothing committed to review — say so and stop.
|
|
34
63
|
2. **A range** like `main..HEAD` or `origin/main...HEAD` → `git diff <range>`.
|
|
35
64
|
3. **A single ref** (branch / tag / SHA — confirm with `git rev-parse
|
|
36
65
|
--verify`) → that ref's merge-base against `HEAD`.
|
|
37
66
|
4. **A file or directory path** → that target.
|
|
38
67
|
5. Otherwise → ask.
|
|
39
68
|
|
|
40
|
-
Record the **HEAD SHA** you reviewed
|
|
41
|
-
|
|
69
|
+
Record the **HEAD SHA** you reviewed, and **report the target you resolved**
|
|
70
|
+
(the literal range or path) in your output, so the orchestrator can see what
|
|
71
|
+
was actually read rather than assuming.
|
|
42
72
|
|
|
43
73
|
## Effort level
|
|
44
74
|
`low | medium | high | max` — default **medium** if not given. The level
|
|
@@ -57,6 +87,13 @@ hunk-only read cannot see that a caller further down the same file is now
|
|
|
57
87
|
wrong. For multi-commit ranges, skim `git log <range>` for intent before
|
|
58
88
|
judging.
|
|
59
89
|
|
|
90
|
+
**Commit messages are claims, not evidence.** A message saying a fix was
|
|
91
|
+
"proven red→green", a bug reproduced, or a test added is something to re-test,
|
|
92
|
+
not a fact to accept. Branches are commonly AI-authored now — including the
|
|
93
|
+
fixes to the fixes — so a review that trusts the message is reviewing prose.
|
|
94
|
+
Run the test suite and the typecheck/build yourself and cite the command and
|
|
95
|
+
its exit code.
|
|
96
|
+
|
|
60
97
|
- **Bugs needing a fix.** Logic errors, off-by-one, null/undefined paths,
|
|
61
98
|
races, wrong defaults, broken edge cases.
|
|
62
99
|
- **Dead code.** Unreferenced functions / vars / imports / params, unreachable
|
|
@@ -69,6 +106,16 @@ judging.
|
|
|
69
106
|
broken invariants.
|
|
70
107
|
- **Performance.** N+1, blocking calls in hot paths, unbounded loops, indexes
|
|
71
108
|
the diff actually touches.
|
|
109
|
+
- **Test quality, not just test presence.** For every test the diff adds or
|
|
110
|
+
changes, establish that it **can actually fail**. Reasoning about
|
|
111
|
+
falsifiability does not work; executing it does. **Revert the source, not the
|
|
112
|
+
test:** take the pre-change version of the file under test with `git show
|
|
113
|
+
<base-sha>:<path>`, run the test against that copy, and watch it go red. Do
|
|
114
|
+
this **without dirtying the branch** — write the old version to a temp
|
|
115
|
+
location outside the repo; the tree must still be clean at exit. A test that
|
|
116
|
+
passes against both the buggy and the fixed source is a tautology and proves
|
|
117
|
+
nothing. Flag every one you find, and say so explicitly when the tests are
|
|
118
|
+
the branch's only evidence for its claims.
|
|
72
119
|
- **Maintainability.** Complexity, naming, duplication — only when material.
|
|
73
120
|
|
|
74
121
|
## Stage 2 — Security (always full)
|
|
@@ -102,7 +149,12 @@ cannot write that sentence, the finding is not ready — drop it or mark it
|
|
|
102
149
|
uncertain. No vibes.
|
|
103
150
|
|
|
104
151
|
## Report — then escalate
|
|
105
|
-
|
|
152
|
+
**Open with the one-line verdict**, before any section: **Ready to merge? Yes /
|
|
153
|
+
No / Not until these are fixed.** A report that opens with "Critical: none
|
|
154
|
+
found" reads as a pass at a glance even when the verdict is not one — state the
|
|
155
|
+
verdict first, then repeat it at the end.
|
|
156
|
+
|
|
157
|
+
Then the findings, ordered most severe first.
|
|
106
158
|
|
|
107
159
|
### 🚨 Critical (blocks merge)
|
|
108
160
|
### ⚠️ Warnings (should fix)
|
|
@@ -117,7 +169,8 @@ with its evidence. A stage you did not actually run is a **✗**, never an
|
|
|
117
169
|
assumed pass.
|
|
118
170
|
|
|
119
171
|
End with:
|
|
120
|
-
- **Reviewed at HEAD `<sha>` on `<branch
|
|
172
|
+
- **Reviewed at HEAD `<sha>` on `<branch>`, target `<resolved range or path>`,
|
|
173
|
+
tree clean at start and at exit.**
|
|
121
174
|
- One-line verdict: **Ready to merge? Yes / No / Not until these are fixed.**
|
|
122
175
|
- **Escalate to the orchestrator** with the findings. It decides what gets
|
|
123
176
|
fixed and by whom. Say plainly what you could not verify.
|