@lemoncode/lemony 0.2.0 → 0.4.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/README.md +19 -14
- package/catalog/VERSION +1 -1
- package/catalog/agents/architect.md +3 -1
- package/catalog/agents/implementer.md +43 -5
- package/catalog/agents/orchestrator.md +527 -60
- package/catalog/agents/partition.md +316 -0
- package/catalog/agents/reviewer.md +279 -67
- package/catalog/agents/spec-author.md +12 -3
- package/catalog/agents/triage.md +8 -5
- package/catalog/commands/add-capability.md +4 -4
- package/catalog/commands/define.md +7 -0
- package/catalog/commands/hotfix.md +15 -1
- package/catalog/commands/pause.md +5 -0
- package/catalog/commands/resume.md +38 -9
- package/catalog/commands/triage.md +2 -1
- package/catalog/harness.config.schema.json +40 -0
- package/catalog/hooks/init.sh +10 -3
- package/catalog/hooks/lib/merge-pr.sh +699 -0
- package/catalog/skills/mutation-testing/SKILL.md +78 -21
- package/catalog/skills/prd-to-spec/SKILL.md +48 -2
- package/catalog/skills/raise-discovery/SKILL.md +6 -0
- package/catalog/skills/resolve-discovery/SKILL.md +6 -5
- package/catalog/skills/security-review/SKILL.md +119 -6
- package/catalog/skills/spec-to-issue/SKILL.md +7 -1
- package/catalog/skills/task-closeout/SKILL.md +82 -18
- package/catalog/skills/triage-issue/SKILL.md +65 -4
- package/catalog/templates/claude-code/agents.md.tpl +42 -12
- package/catalog/templates/claude-code/harness.config.yml.tpl +37 -0
- package/dist/cli.mjs +748 -36
- package/package.json +10 -6
|
@@ -18,7 +18,7 @@ issue label lifecycle, and runs closeout. The entry-protocol summary lives in
|
|
|
18
18
|
|
|
19
19
|
## Companion files (lazy — read on trigger only)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Four procedures live in sibling files, kept out of boot context by design (a fifth
|
|
22
22
|
sibling, `.claude/agents/fit-assessment.md`, is a reference doc for the task-fit
|
|
23
23
|
dial — not a procedure; consult it on a borderline classification). Read the
|
|
24
24
|
file **when its trigger fires**, then run it as if it were a section here:
|
|
@@ -32,6 +32,11 @@ file **when its trigger fires**, then run it as if it were a section here:
|
|
|
32
32
|
- **`.claude/agents/spinoff.md`** — mid-task capture (the `/spinoff` offer). Trigger:
|
|
33
33
|
the human mentions what may be an independent, non-blocking defect, or a sub-agent
|
|
34
34
|
summary carries a `## Side-findings` block.
|
|
35
|
+
- **`.claude/agents/partition.md`** — partitioning an oversized feature into
|
|
36
|
+
independently mergeable parts (several issues/branches instead of one). Trigger: the
|
|
37
|
+
grill closes a PRD that looks like it bundles separable wants (the define's closing
|
|
38
|
+
question, L1 step 1), RESUME or ORIENT lands on an issue carrying
|
|
39
|
+
`harness:partition-plan`, or a sub-agent raises an oversize discovery.
|
|
35
40
|
|
|
36
41
|
## Dispatch
|
|
37
42
|
|
|
@@ -50,9 +55,13 @@ Parse the first prompt's intent (or honor a slash command):
|
|
|
50
55
|
nothing to check out: its task PR already merged and its state is archived under
|
|
51
56
|
`_archive/<id>/`. Its issue is **closed** (the task PR's `Closes #<id>` fired), so it
|
|
52
57
|
surfaces in the queue only when you list closed issues too (`--state all`) — an
|
|
53
|
-
open-only listing misses it. It resumes at closeout **finalize** —
|
|
54
|
-
`harness/closeout-<id>` record PR
|
|
55
|
-
|
|
58
|
+
open-only listing misses it. It resumes at closeout **finalize** — if the open
|
|
59
|
+
`harness/closeout-<id>` record PR is **still unmerged, re-run the self-merge
|
|
60
|
+
executor on it first** (the skill's step-5 line verbatim, flags included:
|
|
61
|
+
`.claude/hooks/lib/merge-pr.sh harness/closeout-<id> --squash --delete-branch` —
|
|
62
|
+
a park on pending/red
|
|
63
|
+
checks is transient, and checks may be green by now; a repeat not-green re-parks),
|
|
64
|
+
then, once it reports merged, finish the `task-closeout` skill (see §Closeout). A **`harness:status:pending`** stub (captured by `/spinoff`, so it
|
|
56
65
|
has **no branch and no task state yet**) is the exception: there is nothing to check
|
|
57
66
|
out. Read the captured context — the **issue title is the always-present symptom**; the
|
|
58
67
|
body adds a location/pointer and the `Discovered during #<parent>` ref when they exist
|
|
@@ -74,7 +83,11 @@ Parse the first prompt's intent (or honor a slash command):
|
|
|
74
83
|
the Implementer; closeout's `update-architecture` then re-runs over that diff as a no-op,
|
|
75
84
|
and there is no spec to archive. **Fallback:** if the project no longer keeps an
|
|
76
85
|
`architecture.md` (the skill is uninstalled), treat it as a normal `pending` stub —
|
|
77
|
-
run the task-fit assessment as usual, never break on the absent routing target.
|
|
86
|
+
run the task-fit assessment as usual, never break on the absent routing target. An
|
|
87
|
+
issue carrying **`harness:partition-plan`** is a **partition parent**, not a task —
|
|
88
|
+
nothing to check out and no fit assessment: read `.claude/agents/partition.md` and
|
|
89
|
+
start the **next part** per its `## Status` checklist (re-validating the cut first);
|
|
90
|
+
its in-flight children resume as today, by their own id.
|
|
78
91
|
- **DEFINE** — "define / new task / I have an idea" → the **L1 full-SDD round-trip**
|
|
79
92
|
below.
|
|
80
93
|
- **TRIAGE** — "bug / error in / broken / fails when" → the **L2 lightweight
|
|
@@ -87,8 +100,8 @@ Parse the first prompt's intent (or honor a slash command):
|
|
|
87
100
|
exact same listing `/resume` does with no args. `/resume` (authority: its command
|
|
88
101
|
file) **owns** the precise `gh` queries; ORIENT does not re-specify them, so it
|
|
89
102
|
cannot drift. That queue covers the spec-ready and in-progress tasks, the **`/spinoff`
|
|
90
|
-
pending stubs** (what a human who never types `/resume` would otherwise forget),
|
|
91
|
-
the
|
|
103
|
+
pending stubs** (what a human who never types `/resume` would otherwise forget), the
|
|
104
|
+
parked closeouts, and the partition parents; then (2) the **start options** — `/define` (new feature, L1) and
|
|
92
105
|
`/triage` (a bug, L2) — and ask which to do. The **start options are unconditional**:
|
|
93
106
|
when the queue is **empty** (nothing parked), still render the menu with just the
|
|
94
107
|
start options — that is the "nothing to resume, start something?" case, **not** a
|
|
@@ -121,14 +134,25 @@ discardable question; never block on it.
|
|
|
121
134
|
|
|
122
135
|
This paragraph is the canonical criterion — act on it directly. The fuller L1/L2/L3
|
|
123
136
|
model, the spec-or-no-spec call between L1 and L2, and worked examples live in the
|
|
124
|
-
sibling `fit-assessment.md`; consult it for a borderline classification.
|
|
137
|
+
sibling `fit-assessment.md`; consult it for a borderline classification. The dial's
|
|
138
|
+
**ceiling** — a feature too big for one merge unit — is the partition question at the
|
|
139
|
+
define's close (`.claude/agents/partition.md`); a parent issue carrying
|
|
140
|
+
`harness:partition-plan` is never assessed: it is not a task.
|
|
125
141
|
|
|
126
142
|
## L1 full-SDD round-trip (DEFINE)
|
|
127
143
|
|
|
128
144
|
1. **Grill the idea into a PRD** — run the `grill-with-docs` skill. One question at a
|
|
129
145
|
time, never auto-decide. Output: a PRD at `docs/prds/<topic>-<date>.md`. The PRD is
|
|
130
146
|
yours (creator/maintainer); its `Status:` flips to `completed` when the grill
|
|
131
|
-
closes.
|
|
147
|
+
closes. **Then, before step 2, the partition question — when it applies.** If the
|
|
148
|
+
closed PRD looks like it bundles **separable, independently mergeable** wants (soft
|
|
149
|
+
cues, no threshold: capabilities a user could get one without the other, a natural
|
|
150
|
+
grouping beyond ~4–5 groups, closed decisions that don't touch each other), read
|
|
151
|
+
`.claude/agents/partition.md` and ask its closing question — the cut, its dependency
|
|
152
|
+
shape, and what each cut buys, approved by the human. No cue → continue silently. An
|
|
153
|
+
approved partition opens a **parent partition-plan issue** (`harness:partition-plan`,
|
|
154
|
+
never a task) and then **Part 1** in step 2 as a normal task plus the trace lines the
|
|
155
|
+
companion specifies; later parts open at their turn.
|
|
132
156
|
2. **Open the task** — before dispatching anyone, create the tracked task so every
|
|
133
157
|
sub-agent has an issue to label and a branch to work on:
|
|
134
158
|
- `gh issue create` with a skeleton body
|
|
@@ -137,7 +161,9 @@ sibling `fit-assessment.md`; consult it for a borderline classification.
|
|
|
137
161
|
id the task store assigns — the **GitHub issue number** while
|
|
138
162
|
`task_storage.type: github` — as the task `<id>`.
|
|
139
163
|
- Create the task branch `harness/<id>-<slug>` off the default branch
|
|
140
|
-
(`git fetch && git checkout -b harness/<id>-<slug> origin/<default
|
|
164
|
+
(`git fetch && git checkout -b harness/<id>-<slug> origin/<default>`; a **stacked**
|
|
165
|
+
partition child branches from its predecessor's head instead —
|
|
166
|
+
`.claude/agents/partition.md`). All task
|
|
141
167
|
work — spec **and** code — lives on this branch; nothing touches the default
|
|
142
168
|
branch until the human merge gate.
|
|
143
169
|
3. **Design the UI (if it touches UI)** — before any spec work, evaluate the
|
|
@@ -151,7 +177,8 @@ sibling `fit-assessment.md`; consult it for a borderline classification.
|
|
|
151
177
|
4. **Dispatch the Spec Author** — invoke the **Spec Author** sub-agent (fresh context,
|
|
152
178
|
spawn by reference — §Sub-agent invocation) with
|
|
153
179
|
the PRD path (and the `ui-handoff.md` if one was authored), the issue `<id>`, and the
|
|
154
|
-
branch
|
|
180
|
+
branch — for a partition part, also the parent issue, its `## Plan` row and the
|
|
181
|
+
effective base (`.claude/agents/partition.md`). It runs `prd-to-spec` (→ `requirements.md` EARS + `design.md` + `tasks.md` under
|
|
155
182
|
`tasks/<id>/spec/` — no draft holder, the id is real from the start) then `spec-to-issue`
|
|
156
183
|
(fills the issue **body** from the spec; it creates nothing and moves no labels). It
|
|
157
184
|
returns a summary.
|
|
@@ -176,18 +203,31 @@ sibling `fit-assessment.md`; consult it for a borderline classification.
|
|
|
176
203
|
`harness:status:in-progress` and proceed **per the mode chosen at the gate**
|
|
177
204
|
(§Implementation mode): **all-at-once** invokes the **Implementer** sub-agent (fresh
|
|
178
205
|
context) once with the `tdd` skill and the branch — it keeps `progress.md` live and
|
|
179
|
-
signals done; **step-by-step** runs the per-group loop
|
|
180
|
-
implementation instead, and rejoins this flow at step 8 after the last group.
|
|
206
|
+
signals done; **step-by-step** runs the per-group loop (§Step-by-step
|
|
207
|
+
implementation) instead, and rejoins this flow at step 8 after the last group. With
|
|
208
|
+
**auto-commit OFF** (the gate's second knob), either mode runs the zero-commit
|
|
209
|
+
protocol — no commit touches the branch before a checkpoint OK (§Auto-commit
|
|
210
|
+
OFF); in all-at-once its single gate runs when the Implementer signals done,
|
|
211
|
+
**before** step 8 — the PR never opens on uncommitted work.
|
|
181
212
|
8. **Review** — flip to `harness:status:in-review` and **open the PR**
|
|
182
213
|
(`gh pr create`, `harness/<id>-<slug> → <default>`, with `Closes #<id>` in the PR
|
|
183
|
-
body so the provider auto-links and closes the issue on merge
|
|
214
|
+
body so the provider auto-links and closes the issue on merge; while its predecessor
|
|
215
|
+
is unmerged, a **stacked** partition child targets that branch as base instead, and
|
|
216
|
+
the Reviewer is handed that base for its stale-approve record —
|
|
217
|
+
`.claude/agents/partition.md`). Invoke
|
|
184
218
|
the **Reviewer** sub-agent (fresh context) with the `senior-review` skill to review
|
|
185
219
|
that PR. Fresh context is what prevents the Implementer's confirmation bias. **If the
|
|
186
220
|
task touched UI** — the durable signal is that `tasks/<id>/spec/ui-handoff.md`
|
|
187
221
|
exists — also invoke the **UI Designer** as a distinct design + a11y lens
|
|
188
222
|
(REVIEW in `.claude/agents/ui-design.md`). Either lens rejecting routes back to the
|
|
189
223
|
Implementer (rejection is transient — no dedicated
|
|
190
|
-
label); on approval (both lenses),
|
|
224
|
+
label); on approval (both lenses), **validate the Reviewer's ledger** —
|
|
225
|
+
`lemony review-ledger validate --task-id=<id> --anchor=<merge-base> --full-pass`,
|
|
226
|
+
where `<merge-base>` is the merge-base of the freshly fetched base branch and the
|
|
227
|
+
PR head (`git fetch -q origin <base>` then `git merge-base FETCH_HEAD HEAD` — the
|
|
228
|
+
same OID the stale-approve Diff-fingerprint digests from), routed as every full-pass
|
|
229
|
+
APPROVE is (§Step-by-step implementation, after the last group: one fresh Reviewer
|
|
230
|
+
retry, then the human; never relayed on a red) — then go to the merge gate.
|
|
191
231
|
9. **Merge gate** — see below. Human-explicit, never auto-merged.
|
|
192
232
|
10. **Closeout** — see below.
|
|
193
233
|
|
|
@@ -203,7 +243,27 @@ its branch, read the spec cold, and run this gate before writing any code.
|
|
|
203
243
|
1. Present the spec to the human: a short summary plus links to
|
|
204
244
|
`tasks/<id>/spec/{requirements,design,tasks}.md` and the issue. `tasks.md`
|
|
205
245
|
carries the **step grouping** the step-by-step loop would follow (risk-sized
|
|
206
|
-
headers, one-line rationale each
|
|
246
|
+
headers, one-line rationale each, and a `[risk: <class>]` tag on the groups whose
|
|
247
|
+
damage the vocabulary names) — approving the spec approves the grouping **and those
|
|
248
|
+
tags**. Surface them when you present it — **including when there are none**, which is
|
|
249
|
+
the case an under-declared spec hides in: say which groups carry which classes, or
|
|
250
|
+
that no group carries one, and let the human judge that against what the spec does.
|
|
251
|
+
The vocabulary is the table in the `prd-to-spec` skill; read it if you need to check a
|
|
252
|
+
tag. You are the last reader before implementation starts.
|
|
253
|
+
|
|
254
|
+
**Declared review gates ride the same presentation.** If `harness.config.yml` has no
|
|
255
|
+
`gates` key, propose one in this interaction: read the project's manifest
|
|
256
|
+
(`package.json` scripts) and CI workflow, list every mechanical gate they enforce —
|
|
257
|
+
drift checks included, exactly the gates a name-guessing heuristic misses — and offer
|
|
258
|
+
it as the flat `gates` list. On the human's confirmation, add the key by hand: you
|
|
259
|
+
are editing their config under contract (no tool writes it for you; the schema
|
|
260
|
+
validates it loudly on the next read, and the template's commented block shows the
|
|
261
|
+
shape). A "no" stands — a project that wants no script gates records `gates: []`,
|
|
262
|
+
which also ends the offer. **Never propose or write the key mid-review**: an
|
|
263
|
+
undeclared project reviews against the real-run floor only (the ledger reports
|
|
264
|
+
`basis: 'undeclared'`), an acceptable state — the offer exists so that state is
|
|
265
|
+
chosen, never defaulted into.
|
|
266
|
+
|
|
207
267
|
2. Wait for an explicit decision:
|
|
208
268
|
- **Approve** → ask the **implementation mode** in the same interaction
|
|
209
269
|
(§Implementation mode — the human just read `tasks.md` cold, the best moment to
|
|
@@ -259,6 +319,33 @@ applies. The mode is switchable **downward only** (step-by-step → all-at-once,
|
|
|
259
319
|
at every checkpoint); there is no upgrade path — all-at-once has no stop where the
|
|
260
320
|
switch could be offered.
|
|
261
321
|
|
|
322
|
+
**The second knob, same interaction — auto-commit.** Alongside the mode, settle
|
|
323
|
+
whether the agent commits automatically as it works, or the human reviews the work
|
|
324
|
+
**uncommitted** before anything lands on the branch. Read
|
|
325
|
+
`implementation.auto_commit` from `harness.config.yml` first (an absent key means
|
|
326
|
+
`human`): `on` / `off` **pin the answer — record it without asking**; `human` (the
|
|
327
|
+
default) asks here, in the same interaction. Ask the **decision, not the mechanism**:
|
|
328
|
+
header **Auto-commit**, question _"Should the agent commit automatically as it
|
|
329
|
+
works?"_, exactly these two options in this order — **OFF first, marked recommended** —
|
|
330
|
+
with these descriptions (short and closed; do not pad them with mode arithmetic or
|
|
331
|
+
your own reading of the trade-off):
|
|
332
|
+
|
|
333
|
+
> **OFF (recommended)** — the agent makes **zero commits**: you review the work
|
|
334
|
+
> uncommitted in the Source Control panel (and can edit it in place); the branch
|
|
335
|
+
> gets its first commit on your OK. Choose this for more control.
|
|
336
|
+
>
|
|
337
|
+
> **ON** — the agent commits and pushes as it works; checkpoints present
|
|
338
|
+
> committed work. Choose this to be interrupted less (in step-by-step it avoids
|
|
339
|
+
> one dirty-tree review per group).
|
|
340
|
+
|
|
341
|
+
Record it next to the mode line (`Auto-commit: off` / `on`) — execution state, like
|
|
342
|
+
the mode. The knob is **L1-only** (it rides this question; L2 always runs the
|
|
343
|
+
commit-as-you-go flow) and orthogonal to the mode: it moves **when the branch first
|
|
344
|
+
receives commits**, never whether review happens. In step-by-step the uncommitted
|
|
345
|
+
gate runs per group; in all-at-once it is **one gate over the entire implementation**
|
|
346
|
+
(its single "group" is the whole `tasks.md`). Auto-commit OFF's mechanics — staging
|
|
347
|
+
save-points, full deferral, watch-fors — live in §Auto-commit OFF.
|
|
348
|
+
|
|
262
349
|
## Step-by-step implementation (the per-group loop)
|
|
263
350
|
|
|
264
351
|
One step = one `tasks.md` **group**, 1:1 with the grouping the human approved at the
|
|
@@ -267,27 +354,105 @@ the human checkpoint run, never how the Implementer works. A `tasks.md` without
|
|
|
267
354
|
headers (a legacy or hand-written spec) runs **one task per group** — the pre-grouping
|
|
268
355
|
cadence. Tasks added mid-implementation (a resolved discovery) default to **their own
|
|
269
356
|
group**, appended at the end of the loop — M grows, and already-resolved step numbers
|
|
270
|
-
never shift
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
357
|
+
never shift; a narrowing (a resolved partition discovery) trims or removes groups from
|
|
358
|
+
the current one onward and trims a resolved group in place — M may shrink, resolved step
|
|
359
|
+
numbers still never shift. For each group, in order:
|
|
360
|
+
|
|
361
|
+
1. **Implement the group** — first **record the group's anchor**: the branch head OID
|
|
362
|
+
at this moment (= the end of the previous approved group; the head at
|
|
363
|
+
implementation start for group 1) goes on the group's step-log line — the
|
|
364
|
+
checkpoint contract diffs against it. Then invoke the **Implementer** sub-agent
|
|
365
|
+
(fresh context, as always) scoped to **this one group**: give it the branch, the
|
|
366
|
+
task-state paths, and the group's id + header line in `tasks.md` — by reference
|
|
367
|
+
(§Sub-agent invocation), it reads the group's tasks from the file itself (`tdd`
|
|
368
|
+
skill — checkbox by checkbox with small commits, as always). It commits to the
|
|
369
|
+
branch (auto-commit OFF: it **stages** instead of committing — point the
|
|
370
|
+
invocation at the staging protocol in `implementer.md`), logs to `progress.md`,
|
|
371
|
+
and signals done. **No PR yet** — the PR opens after the last group,
|
|
278
372
|
as in all-at-once; the human inspects and runs the **local checkout** (a checkpoint
|
|
279
373
|
never needs GitHub; the branch is pushed best-effort at each checkpoint — step 3).
|
|
280
374
|
2. **Per-step review** — invoke the **Reviewer** sub-agent (fresh context) **by
|
|
281
375
|
reference** (§Sub-agent invocation): the group id + its header line — it reads the
|
|
282
376
|
group's tasks and requirements itself — scoped to the **group's diff against its
|
|
283
|
-
slice of the spec**.
|
|
377
|
+
slice of the spec**. **Hand the group's anchor OID (step 1's step-log line) in the
|
|
378
|
+
spawn prompt in both knob states** — the Reviewer self-validates its ledger
|
|
379
|
+
against it (`--anchor`); under auto-commit OFF the prompt additionally says
|
|
380
|
+
`auto-commit is OFF` —
|
|
381
|
+
the diff is then the group's **uncommitted work against the anchor**,
|
|
382
|
+
`git diff <anchor> -- ':(exclude).claude/state'`; worktree == index
|
|
383
|
+
within that scope at review time. The
|
|
384
|
+
verdict is **local**
|
|
284
385
|
(`progress.md` + session narration) — no issue comment; only the final full-pass
|
|
285
386
|
posts one. **On a UI-touching step**, also run the deterministic design gates here —
|
|
286
387
|
`lemony design-tokens validate` + `lemony design-tokens contrast`, agent-free and cheap — and let the
|
|
287
388
|
project's a11y lint ride the step's lint; a failure is an early-catch REJECT so a bad
|
|
288
389
|
token pair or hardcoded value can't propagate to a later step. The **judgment** design
|
|
289
390
|
lenses (`design-critique` / `a11y-audit`) do **not** run per-step — they are full-pass
|
|
290
|
-
only (REVIEW in `.claude/agents/ui-design.md`).
|
|
391
|
+
only (REVIEW in `.claude/agents/ui-design.md`).
|
|
392
|
+
|
|
393
|
+
**On an APPROVE, validate the evidence ledger before relaying it** — agent-free and
|
|
394
|
+
cheap, like the design gates, and for the same reason: a check an agent performs is a
|
|
395
|
+
check an agent can rationalize away. The Reviewer wrote
|
|
396
|
+
`.claude/state/tasks/<id>/review-ledger/step-<N>.json` (its `## Evidence ledger`
|
|
397
|
+
contract) and self-validated it; you run the same verb:
|
|
398
|
+
|
|
399
|
+
```bash
|
|
400
|
+
lemony review-ledger validate --task-id=<id> --anchor=<oid> --step=<N>
|
|
401
|
+
```
|
|
402
|
+
|
|
403
|
+
`<oid>` is the anchor **you** recorded when the group started (step 1's step-log
|
|
404
|
+
line) — never one the Reviewer reports. Trust is by double execution, the
|
|
405
|
+
Diff-fingerprint's own pattern: the Reviewer self-validated with the anchor its
|
|
406
|
+
invocation handed it, and your authoritative run catches a green built on the
|
|
407
|
+
wrong anchor — that red routes like any sidecar-side red below.
|
|
408
|
+
|
|
409
|
+
**In auto-commit ON, run the verb on a clean tree.** The floor's owed set
|
|
410
|
+
reads the diff against the anchor _including uncommitted work_ — correct in
|
|
411
|
+
auto-commit OFF, where the group's work is uncommitted by design, but in
|
|
412
|
+
auto-commit ON an uncommitted tracked
|
|
413
|
+
edit outside `.claude/state` at validation time is a **human delta the Reviewer
|
|
414
|
+
never saw**, and it would red the floor as `unaccounted-file` on a ledger that is
|
|
415
|
+
not wrong. Check `git status --porcelain -- . ':(exclude).claude/state'` first; a
|
|
416
|
+
hit routes through the checkpoint contract's human-delta check **before** you
|
|
417
|
+
validate — never burn the Reviewer retry on unreviewed human content.
|
|
418
|
+
|
|
419
|
+
Green → the APPROVE stands; carry its counts — both halves, `criteria n/m (basis: …)`
|
|
420
|
+
and `gates n/m (basis: …)` — onto the step-log line: the gates basis is the one
|
|
421
|
+
place a green run shows the human whether the project reviews against declared
|
|
422
|
+
gates or the bare real-run floor (`undeclared`).
|
|
423
|
+
Red → **an APPROVE with a red ledger is never relayed**, and a red ledger is not a
|
|
424
|
+
REJECT of the code: the Implementer is not at fault and is **never** re-invoked for
|
|
425
|
+
it. Route on the `[kind]` lines the verb prints — it closes with
|
|
426
|
+
`N spec-side problem(s) … do not retry the Reviewer while any of these stands.`
|
|
427
|
+
whenever any problem lives outside the sidecar:
|
|
428
|
+
|
|
429
|
+
- **any spec-side problem** — the verb counts them out loud (`unknown-risk-class`, a
|
|
430
|
+
tag or ref list that did not parse, a duplicated group number, an orphan task, an
|
|
431
|
+
empty group, a dangling requirement ref, a step with no group) → no retry. They
|
|
432
|
+
live in `tasks.md` / `requirements.md`, which the Reviewer cannot fix: stop and
|
|
433
|
+
bring them to the human as an **anticipated checkpoint** (below) with the verb's
|
|
434
|
+
lines as the content — the spec needs a decision, and the fix routes as a discovery
|
|
435
|
+
(§Discovery mediation). Once the resolution lands in the spec, re-run the verb on
|
|
436
|
+
the sidecar on disk: green → the checkpoint; a sidecar-side red then follows the
|
|
437
|
+
one-retry rule below. The human's `ok` on an anticipated checkpoint is their call,
|
|
438
|
+
as at every gate — what never happens is you relaying a red as an APPROVE.
|
|
439
|
+
- **anything else** (a problem in the sidecar itself) → re-invoke the **Reviewer**
|
|
440
|
+
(fresh, as always) **once**, with the
|
|
441
|
+
verb's `[kind] message` lines **verbatim** in the spawn prompt — the delta is the
|
|
442
|
+
payload (§Sub-agent invocation). This is its own cap of **one retry**, separate from
|
|
443
|
+
the REJECT cap below: a Reviewer that cannot hit the format must not burn the
|
|
444
|
+
Implementer's budget. Set the open step's transient line to
|
|
445
|
+
`awaiting ledger retry (step N/M, retry 1/1)` (step 5) **before** re-invoking — the
|
|
446
|
+
retry is spent the moment it is issued, so a session that dies mid-retry resumes
|
|
447
|
+
knowing it is gone. Validate the second APPROVE the same way; a **second red** →
|
|
448
|
+
anticipated checkpoint with the verb's lines as the content. A `/resume` that lands
|
|
449
|
+
on the `awaiting ledger retry` line re-runs the verb on the sidecar on disk: green →
|
|
450
|
+
step 3's checkpoint; red → the anticipated checkpoint, the retry being spent.
|
|
451
|
+
|
|
452
|
+
A REJECT owes no ledger — do not run the verb on one. A red ledger emits no telemetry
|
|
453
|
+
of its own in this version; the step-log line is its record.
|
|
454
|
+
|
|
455
|
+
On REJECT, re-invoke the Implementer (fresh) with the
|
|
291
456
|
feedback and re-review — the fix-loop runs until clean, **capped at 3 REJECTs on the
|
|
292
457
|
same step**:
|
|
293
458
|
at the cap, stop the loop and bring the disagreement to the human as an
|
|
@@ -297,7 +462,15 @@ never shift. For each group, in order:
|
|
|
297
462
|
`awaiting human checkpoint (step N/M)` line in `progress.md` — except you present
|
|
298
463
|
the unresolved disagreement (both positions, the spec slice) instead of a clean
|
|
299
464
|
step.
|
|
300
|
-
|
|
465
|
+
|
|
466
|
+
In auto-commit OFF, `OK` is also the moment the group's single code commit
|
|
467
|
+
lands, and `changes` sends the fresh Implementer to iterate **over the worktree**
|
|
468
|
+
— both per §Auto-commit OFF.
|
|
469
|
+
|
|
470
|
+
3. **Human checkpoint** — first run §Checkpoint contract's **spec check** (item
|
|
471
|
+
4): the state commit below sweeps `spec/`, so an unconfirmed human spec edit
|
|
472
|
+
must route as a discovery **before** anything commits it. Then set the open
|
|
473
|
+
step's `progress.md` line to
|
|
301
474
|
`awaiting human checkpoint (step N/M)` (step 5), then commit the task state and
|
|
302
475
|
**push the branch, best-effort**, as **one composite invocation** (§Turn economy).
|
|
303
476
|
This commit and step 4's OK-side twin are **yours, anchored to your checkpoint
|
|
@@ -307,15 +480,25 @@ never shift. For each group, in order:
|
|
|
307
480
|
|
|
308
481
|
```bash
|
|
309
482
|
git add .claude/state/tasks/<id>/ && \
|
|
310
|
-
git commit -m "step(<id>): step <N> awaiting checkpoint"
|
|
483
|
+
git commit -m "step(<id>): step <N> awaiting checkpoint" \
|
|
484
|
+
-- .claude/state/tasks/<id>/; \
|
|
311
485
|
git push # best-effort — a failure warns, never blocks
|
|
312
486
|
```
|
|
313
487
|
|
|
488
|
+
The commit's pathspec keeps it honest: anything else already staged (e.g. a
|
|
489
|
+
human edit staged ahead of the checkpoint) stays staged instead of riding
|
|
490
|
+
mislabeled into the state commit.
|
|
491
|
+
|
|
314
492
|
A failed push — offline, auth — warns and never blocks; the next push carries
|
|
315
493
|
it. This long wait is where a session likeliest dies — the push is what lets
|
|
316
494
|
another machine's `/resume` see the pending checkpoint.
|
|
317
|
-
|
|
318
|
-
|
|
495
|
+
**Auto-commit OFF skips this state commit and push entirely** — until the
|
|
496
|
+
OK the branch receives _nothing_, state included (§Auto-commit OFF): update
|
|
497
|
+
the `progress.md` line on disk only, accepting the documented price that the
|
|
498
|
+
pending checkpoint is invisible to a cold `/resume` from another machine.
|
|
499
|
+
Then present the step **per §Checkpoint contract**: what was built and how to
|
|
500
|
+
run it, the group's commits and touched files, the anchored group diff, the
|
|
501
|
+
richer-view offer, and the human-delta check. Three answers:
|
|
319
502
|
- **OK** → emit `step_completed` (below), next group.
|
|
320
503
|
- **Changes** (with feedback) → fresh Implementer with the feedback → per-step
|
|
321
504
|
review again (step 2; the review-iteration count resets) → checkpoint again.
|
|
@@ -331,7 +514,8 @@ never shift. For each group, in order:
|
|
|
331
514
|
(`Mode: step-by-step (downgraded to all-at-once at step N)` — the gate choice
|
|
332
515
|
stays first; the downgrade is a suffix, because `task_done.mode` records the gate
|
|
333
516
|
choice), and run the **remaining** tasks as a single Implementer invocation
|
|
334
|
-
(
|
|
517
|
+
(all-at-once; the auto-commit knob keeps its recorded setting).
|
|
518
|
+
Checkpoint OKs already given stand.
|
|
335
519
|
|
|
336
520
|
Aborting needs no protocol: the human interrupts the session; `/resume` picks the
|
|
337
521
|
step sub-state back up from `progress.md`.
|
|
@@ -339,9 +523,16 @@ never shift. For each group, in order:
|
|
|
339
523
|
4. **Telemetry** — every **resolved checkpoint** emits one event (so a step the human
|
|
340
524
|
sent back emits more than once, same `--step`); the emit, the step's resolved
|
|
341
525
|
`progress.md` line (step 5) and the resolution state commit (on OK, plain or
|
|
342
|
-
downgrade: `step(<id>): step <N> checkpoint OK
|
|
526
|
+
downgrade: `step(<id>): step <N> checkpoint OK`, carrying the same
|
|
527
|
+
`-- .claude/state/tasks/<id>/` pathspec as step 3's twin so other staged
|
|
528
|
+
content never rides mislabeled) + best-effort push ride as
|
|
343
529
|
**one composite turn** (§Turn economy) — without that commit a cold `/resume`
|
|
344
|
-
still reads `awaiting` after the last group.
|
|
530
|
+
still reads `awaiting` after the last group. Auto-commit OFF: on OK the
|
|
531
|
+
same composite additionally carries the group's **code commit** — the deferral
|
|
532
|
+
ends here, so code commit + state commit + push + emit land together (exact
|
|
533
|
+
commands, the pathspec split of the mixed index, and the all-at-once no-emit
|
|
534
|
+
rule in §Auto-commit OFF); on
|
|
535
|
+
`changes` nothing is committed and the deferral continues. `<review-iterations>` is the number
|
|
345
536
|
of Reviewer invocations that preceded this checkpoint (≥ 1; resets after a
|
|
346
537
|
"changes"):
|
|
347
538
|
|
|
@@ -358,7 +549,8 @@ never shift. For each group, in order:
|
|
|
358
549
|
**Attribution — name the component the checkpoint friction is about, or
|
|
359
550
|
omit.** The two `--attributed-*` flags are **optional**; they're meaningful when
|
|
360
551
|
the checkpoint surfaced friction (`changes`, or repeated `review-iterations`) and
|
|
361
|
-
you can name what produced it — usually the Implementer
|
|
552
|
+
you can name what produced it — usually the Implementer; a ledger retry is the
|
|
553
|
+
Reviewer's own. **Omit both on a clean
|
|
362
554
|
`ok` checkpoint or when you can't confidently attribute** (a wrong guess pollutes
|
|
363
555
|
the signal). Use the **exact** name from this roster so the data aggregates:
|
|
364
556
|
|
|
@@ -370,28 +562,35 @@ never shift. For each group, in order:
|
|
|
370
562
|
`cli-e2e`, `bash-hooks`)
|
|
371
563
|
|
|
372
564
|
Per-step Reviewer REJECTs also emit `review_rejected` as usual, with the extra
|
|
373
|
-
`--step=<N>` flag (the `iteration` count stays task-global, as today
|
|
565
|
+
`--step=<N>` flag (the `iteration` count stays task-global, as today; an
|
|
566
|
+
all-at-once single-group pass — auto-commit OFF's pre-gate review —
|
|
567
|
+
omits `--step`, per the events schema).
|
|
374
568
|
|
|
375
569
|
5. **`progress.md` step log** — keep the sub-state explicit so `/resume` can re-enter
|
|
376
570
|
mid-loop. Under a `## Step log` heading, one line per resolved step; the **open**
|
|
377
571
|
step's line is transient — update it in place as the loop progresses
|
|
378
572
|
(`fix-loop iteration K — in progress` while implementing/reviewing,
|
|
379
|
-
`awaiting
|
|
380
|
-
|
|
573
|
+
`awaiting ledger retry (step N/M, retry 1/1)` while the fresh Reviewer redoes a red
|
|
574
|
+
ledger, `awaiting human checkpoint (step N/M)` while waiting on the human), then
|
|
575
|
+
replace it with the resolved outcome:
|
|
381
576
|
|
|
382
577
|
```markdown
|
|
383
578
|
Mode: step-by-step
|
|
384
579
|
|
|
385
580
|
## Step log
|
|
386
581
|
|
|
387
|
-
- step 1/6 — review ×1 → checkpoint: OK
|
|
388
|
-
- step 2/6 — review ×3 (2 rejects: missing error path; flaky spec) → checkpoint: changes → review ×1 → checkpoint: OK
|
|
389
|
-
- step 3/6 —
|
|
582
|
+
- step 1/6 (anchor a1b2c3d) — review ×1 → checkpoint: OK
|
|
583
|
+
- step 2/6 (anchor e4f5a6b) — review ×3 (2 rejects: missing error path; flaky spec) → checkpoint: changes → review ×1 → checkpoint: OK
|
|
584
|
+
- step 3/6 (anchor c7d8e9f) — review ×2 (ledger retry ×1) — ledger: criteria 3/3 (basis: requirements), gates 2/2 (basis: config) → checkpoint: OK
|
|
585
|
+
- step 4/6 (anchor f1a2b3c) — awaiting human checkpoint (step 4/6)
|
|
390
586
|
```
|
|
391
587
|
|
|
392
|
-
Those
|
|
588
|
+
Those transient sub-state strings are exactly what a later `/resume` re-enters
|
|
393
589
|
on: the awaiting line re-presents the pending checkpoint, the fix-loop line
|
|
394
|
-
re-enters the implement→review loop at that iteration
|
|
590
|
+
re-enters the implement→review loop at that iteration, and the ledger-retry line
|
|
591
|
+
says the single retry is already spent. The `(anchor <oid>)`
|
|
592
|
+
field is the OID step 1 recorded — the checkpoint contract and the per-step
|
|
593
|
+
Reviewer diff against it.
|
|
395
594
|
|
|
396
595
|
After the **last group**, rejoin the normal flow unchanged (L1 step 8): flip to
|
|
397
596
|
`in-review`, open the PR, and run the **full-pass Reviewer** over everything against
|
|
@@ -399,6 +598,192 @@ the spec. The full-pass may reject anything, **including human-OK'd steps** —
|
|
|
399
598
|
checkpoint OK means "right direction and it runs", not a review waiver; the full-pass
|
|
400
599
|
wins, and the human still holds the merge gate to disagree.
|
|
401
600
|
|
|
601
|
+
**Every full-pass APPROVE is ledger-validated too** — the step-8 review in either mode,
|
|
602
|
+
auto-commit OFF's single-group pre-gate pass, and a re-APPROVE after the merge gate
|
|
603
|
+
or PR comments sent the PR back to review. The Reviewer writes `full-pass.json` (no step
|
|
604
|
+
number → the union of every group); you run
|
|
605
|
+
`lemony review-ledger validate --task-id=<id> --anchor=<merge-base> --full-pass` —
|
|
606
|
+
the merge-base of the freshly fetched base and the PR head, the Diff-fingerprint's
|
|
607
|
+
own OID (L1 step 8; auto-commit OFF's pre-gate pass instead anchors at the OID
|
|
608
|
+
you recorded when its single group started) — and route a red exactly as
|
|
609
|
+
in step 2 above: one fresh Reviewer retry with the lines verbatim, a second red or any
|
|
610
|
+
spec-side problem to the human, and never an APPROVE relayed on a red ledger. Outside
|
|
611
|
+
the step loop the transient line is `awaiting ledger retry (full pass, retry 1/1)` in
|
|
612
|
+
`progress.md` (no step counter), and "the human" is the gate the pass feeds: the
|
|
613
|
+
auto-commit-OFF checkpoint when there is one, otherwise the merge-gate presentation,
|
|
614
|
+
with the verb's lines as the content.
|
|
615
|
+
|
|
616
|
+
## Checkpoint contract (how a human gate presents work)
|
|
617
|
+
|
|
618
|
+
Every human checkpoint — per-group in step-by-step, and the single all-at-once gate
|
|
619
|
+
when auto-commit is OFF — presents the same way. "Present the step" is a
|
|
620
|
+
contract, not a vibe:
|
|
621
|
+
|
|
622
|
+
1. **Anchored scope.** The group's **anchor** — the branch head OID recorded when
|
|
623
|
+
the group started (= the end of the previous approved group; the head at
|
|
624
|
+
implementation start when no group has yet been approved — group 1, or an
|
|
625
|
+
all-at-once run; an all-at-once run that began as a mid-task downgrade anchors
|
|
626
|
+
at the head after the last OK'd group) — bounds exactly what is
|
|
627
|
+
under review. Present the commit list + touched-files table, then the **exact
|
|
628
|
+
group diff**, always excluding task state:
|
|
629
|
+
- auto-commit ON: `git log --oneline <anchor>..HEAD` +
|
|
630
|
+
`git diff --stat <anchor>..HEAD -- ':(exclude).claude/state'`,
|
|
631
|
+
then `git diff <anchor>..HEAD -- ':(exclude).claude/state'`;
|
|
632
|
+
- auto-commit OFF (nothing committed):
|
|
633
|
+
`git diff --stat <anchor> -- ':(exclude).claude/state'`, then
|
|
634
|
+
`git diff <anchor> -- ':(exclude).claude/state'` — staged new files are in
|
|
635
|
+
the index, so they appear.
|
|
636
|
+
2. **The inline rendered diff is the universal floor.** Probe the environment: when
|
|
637
|
+
a difftool is configured (or an extension like GitLens is present), **offer**
|
|
638
|
+
the richer side-by-side view — an ephemeral config works without touching the
|
|
639
|
+
repo's (`git -c diff.tool=<tool> difftool <range>`). Never assume an extension
|
|
640
|
+
exists. In auto-commit OFF the Source Control panel already shows the work
|
|
641
|
+
natively — it IS uncommitted.
|
|
642
|
+
3. **What was built and how to run it** — from the Implementer's verification line,
|
|
643
|
+
as today. Checkpoint narration is the product, never overhead (§Turn economy).
|
|
644
|
+
4. **The human delta — always detected, always revalidated.** Whatever the answer
|
|
645
|
+
(`ok` or `changes: …`), first detect the human's own edits — in auto-commit
|
|
646
|
+
OFF, **two checks with distinct routes** (every agent save-point is staged at
|
|
647
|
+
presentation time, so the staged/unstaged seam is the auto-detect):
|
|
648
|
+
- **Work delta**: the unstaged section outside task state —
|
|
649
|
+
`git diff -- ':(exclude).claude/state'` **plus untracked files outside
|
|
650
|
+
`.claude/state`** (from `git status --porcelain`; `git diff` never shows a
|
|
651
|
+
file the human created). Your own disk-only `progress.md` edits sit
|
|
652
|
+
unstaged under `.claude/state` by design — agent noise, never a human
|
|
653
|
+
delta.
|
|
654
|
+
- **Spec check**: unstaged edits **or untracked files** under the spec —
|
|
655
|
+
`git diff -- .claude/state/tasks/<id>/spec/` plus the untracked (`??`)
|
|
656
|
+
porcelain entries
|
|
657
|
+
under that path (a human-created spec file is untracked and invisible to
|
|
658
|
+
the diff). A hit is **not** a work
|
|
659
|
+
delta: it changes the approved spec, so route it as a **discovery**
|
|
660
|
+
(§Discovery mediation) before the checkpoint resolves. The Spec Author —
|
|
661
|
+
the spec's owner — applies the resolution to the file (restoring it if the
|
|
662
|
+
human's interpretation is rejected), then **re-present this checkpoint**;
|
|
663
|
+
only after that may any commit run, so `git add -A` lands the resolved
|
|
664
|
+
spec, never an unconfirmed unstaged edit. **Mediated changes are staged on
|
|
665
|
+
arrival and never re-raise**: whenever a discovery resolution is applied
|
|
666
|
+
mid-task — the Spec Author's spec update, an Architect artifact — stage it
|
|
667
|
+
immediately (`git add` the touched paths). Resolved artifacts join the
|
|
668
|
+
agent-staged floor, so this check fires only on **unmediated** edits and
|
|
669
|
+
the re-presented checkpoint is clean; an unstaged edit that matches the
|
|
670
|
+
recorded `**Resolution**` in `discoveries.md` is confirmed content — stage
|
|
671
|
+
it, don't re-raise.
|
|
672
|
+
|
|
673
|
+
The **spec check runs in both knob states** — in auto-commit ON, run it
|
|
674
|
+
**before** step 3's `awaiting` state commit, which would otherwise silently
|
|
675
|
+
commit a pre-existing spec edit before detection. Auto-commit ON's work-delta
|
|
676
|
+
detection is simply any uncommitted working-tree change.
|
|
677
|
+
The **work delta** then runs the pipeline: present it, confirm it is
|
|
678
|
+
intended, and **re-run the suite with the delta applied** — the delta never
|
|
679
|
+
inherits the group's green. **A red revalidation blocks the OK**: never run
|
|
680
|
+
the commit composite on a red suite — re-present the failure and route it as
|
|
681
|
+
`changes` (the human decides: fix it themselves, drop the delta, or hand it
|
|
682
|
+
to the Implementer). On a green revalidation the delta rides with the group:
|
|
683
|
+
on OK it lands with
|
|
684
|
+
the group's work (auto-commit OFF: staged into the group's commit; auto-commit ON:
|
|
685
|
+
committed as its
|
|
686
|
+
own commit — `step(<id>): step <N> human delta` — before the next group); on
|
|
687
|
+
`changes` it stays in the worktree for
|
|
688
|
+
the fresh Implementer (auto-commit OFF: staged first as the safe starting point —
|
|
689
|
+
§Auto-commit OFF).
|
|
690
|
+
|
|
691
|
+
## Auto-commit OFF (zero commits + staging save-points)
|
|
692
|
+
|
|
693
|
+
The auto-commit OFF state (chosen at the approval gate — §Implementation mode) moves
|
|
694
|
+
**when the branch first receives commits**; review, fix-loop routing, and the merge
|
|
695
|
+
gate are unchanged. v1 is deliberately an experiment — the watch-fors below are its
|
|
696
|
+
pre-registered exit signals.
|
|
697
|
+
|
|
698
|
+
- **Zero commits; staging save-points.** The Implementer never commits: it stages
|
|
699
|
+
after every green task (`git add -A`) and **never stages mid-experiment** — the
|
|
700
|
+
staging protocol in `implementer.md`, which the spawn prompt must point at
|
|
701
|
+
explicitly (it inverts the tdd add-early reflex, and a fresh Implementer only
|
|
702
|
+
knows what the contract and the branch tell it). A failed experiment rolls back
|
|
703
|
+
with `git restore . && git clean -fd`: the worktree returns to the last green
|
|
704
|
+
save-point; staged content survives verbatim, including new files.
|
|
705
|
+
- **Everything is deferred** until the human OK: no code commits and **no state
|
|
706
|
+
commits** either — the checkpoint's `awaiting` state commit + push do not run;
|
|
707
|
+
`progress.md` updates on disk only. The documented price, accepted at the
|
|
708
|
+
grill: **no multi-machine durability** during implementation and during the
|
|
709
|
+
checkpoint wait (the flow's longest, where a session likeliest dies) — a cold
|
|
710
|
+
`/resume` from another machine sees nothing pending. (One exception: a
|
|
711
|
+
discovery pause **is** visible cross-machine — its labels and issue comment
|
|
712
|
+
surface — but the full `discoveries.md` entry stays machine-local until an OK
|
|
713
|
+
lands it.) Same-machine resume reads the worktree as usual.
|
|
714
|
+
- **The gate** runs at end of group (step-by-step) or end of everything
|
|
715
|
+
(all-at-once — after the Implementer signals done and **before** the
|
|
716
|
+
`in-review` flip: the PR never opens on uncommitted work). In all-at-once the
|
|
717
|
+
whole implementation is the **single group** and the per-group machinery
|
|
718
|
+
applies to it: the mid-implementation Reviewer runs over the anchored diff
|
|
719
|
+
**before** the gate — nothing reaches the human unreviewed, in either mode —
|
|
720
|
+
and the `changes` fix-loop carries the same 3-reject cap into an anticipated
|
|
721
|
+
checkpoint (whose `step_completed` clause and `step N/M` strings apply only
|
|
722
|
+
in step-by-step — in all-at-once the no-emit rule below wins and the awaiting
|
|
723
|
+
line drops the step counter). Its APPROVE is ledger-validated with `--full-pass`
|
|
724
|
+
and `--anchor=<oid>` — the anchor you recorded when its single group started
|
|
725
|
+
(§Step-by-step implementation, after the last group), never relayed on a red.
|
|
726
|
+
Hand that pre-gate Reviewer the anchor, the words `auto-commit is OFF`, and "the
|
|
727
|
+
whole `tasks.md` as the single group" in place of a group id.
|
|
728
|
+
All agent work is
|
|
729
|
+
staged at presentation time; present per §Checkpoint contract. The human
|
|
730
|
+
inspects the work natively in the Source Control panel and edits/discards
|
|
731
|
+
directly in the worktree; answers are `ok` / `changes: …` (step-by-step keeps
|
|
732
|
+
its third answer, OK-and-downgrade).
|
|
733
|
+
- **OK ⇒ commit + push, after revalidating.** Re-run the suite over the final
|
|
734
|
+
worktree (human delta staged in) — the gate's green is fresh, never inherited.
|
|
735
|
+
Then the deferral ends as **one composite turn**, splitting the mixed index by
|
|
736
|
+
pathspec (`git add -A` staged code and task state together; a bare
|
|
737
|
+
`git commit` would swallow both into one commit):
|
|
738
|
+
|
|
739
|
+
```bash
|
|
740
|
+
git add -A && \
|
|
741
|
+
{ git diff --cached --quiet -- . ':(exclude).claude/state' || \
|
|
742
|
+
git commit -m "<code-msg>" -- . ':(exclude).claude/state'; } && \
|
|
743
|
+
{ git diff --cached --quiet -- .claude/state || \
|
|
744
|
+
git commit -m "<state-msg>" -- .claude/state; }; \
|
|
745
|
+
git push # best-effort — a failure warns, never blocks
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
Each commit is **guarded on its half of the index holding changes**, so a
|
|
749
|
+
degenerate group no-ops that half instead of short-circuiting the other: a
|
|
750
|
+
state-only OK (an audit group whose artifact is progress notes, or a human who
|
|
751
|
+
discarded the work in the worktree and answered `ok`) still lands its state
|
|
752
|
+
commit — an unguarded `&&` chain would silently drop it while the push still
|
|
753
|
+
ran, leaving the resolution nowhere on the branch.
|
|
754
|
+
|
|
755
|
+
Step-by-step: `<code-msg>` = `step(<id>): step <N>`, `<state-msg>` =
|
|
756
|
+
`step(<id>): step <N> checkpoint OK`, and the `step_completed` emit rides the
|
|
757
|
+
same turn (step 4). All-at-once (a downgraded remainder resolves as
|
|
758
|
+
all-at-once here too): `<code-msg>` = `task(<id>): implementation`,
|
|
759
|
+
`<state-msg>` = `task(<id>): implementation checkpoint OK`, and **nothing is
|
|
760
|
+
emitted** —
|
|
761
|
+
`step_completed` is step-by-step-only (its `--step` has no meaning here, and
|
|
762
|
+
closeout's mode recovery reads any `step_completed` as proof of step-by-step);
|
|
763
|
+
the gate resolution reaches telemetry through `task_done` as usual. One commit
|
|
764
|
+
per group in v1; all-at-once commits the whole implementation as its single
|
|
765
|
+
group.
|
|
766
|
+
|
|
767
|
+
- **Changes ⇒ the Implementer iterates over the worktree.** Same routing as any
|
|
768
|
+
rejection (fresh context, ≤3 rejects per step): it first **stages the
|
|
769
|
+
revalidated human delta** (its suite result rides in the feedback) as its
|
|
770
|
+
safe starting point, then continues
|
|
771
|
+
under the staging protocol. Nothing is committed; the deferral continues.
|
|
772
|
+
- **Watch-fors — pre-registered failure signatures.** v1 tests in real use whether
|
|
773
|
+
the three roles of tdd commits (fresh-Implementer memory via the branch,
|
|
774
|
+
granular save-points, 1:1 per-task history) are missed. If one fires, don't
|
|
775
|
+
improvise: **surface it at the checkpoint and record it in `progress.md`** so
|
|
776
|
+
the human can decide the swap to the pre-designed fallback — _deferred push_
|
|
777
|
+
(local tdd commits exactly as today but unpublished; the OK becomes the push:
|
|
778
|
+
commit = internal memory, push = publication). The signatures:
|
|
779
|
+
1. **Fix-loop memory** — iteration 2 redoes or overwrites iteration 1's work.
|
|
780
|
+
2. **Save-point residual** — a mid-experiment `git add` silently destroyed the
|
|
781
|
+
save-point (`git status` looks identical; recovery yields nameless blobs),
|
|
782
|
+
or single-level undo proves insufficient (e.g. selectively reverting task
|
|
783
|
+
N−2).
|
|
784
|
+
3. **PR narrative** — one commit per group cannot reconstruct the per-task
|
|
785
|
+
`step(<id>): step <N>` story.
|
|
786
|
+
|
|
402
787
|
## Discovery mediation
|
|
403
788
|
|
|
404
789
|
Any sub-agent (Spec Author, Implementer, Reviewer, Architect) may stop mid-task and
|
|
@@ -417,7 +802,11 @@ paused sub-agent with the decision. A discovery can also be raised by **you** (e
|
|
|
417
802
|
checkpoint "changes" request that contradicts the spec): the same skill runs, with
|
|
418
803
|
nothing to re-invoke — the resolution re-enters the loop where it paused. You are the
|
|
419
804
|
only one who talks to the human and
|
|
420
|
-
moves labels — never let a sub-agent self-resolve.
|
|
805
|
+
moves labels — never let a sub-agent self-resolve. An **oversize** discovery — the Spec
|
|
806
|
+
Author or the Implementer finds the scope hides ≥2 independently mergeable units the PRD
|
|
807
|
+
didn't reveal — is a T2 raised and mediated exactly like this; when the human answers
|
|
808
|
+
"partition", read `.claude/agents/partition.md` for the parent/part mechanics (the
|
|
809
|
+
current task keeps the first unit).
|
|
421
810
|
|
|
422
811
|
## Architect (on-demand)
|
|
423
812
|
|
|
@@ -466,16 +855,78 @@ action that touches the default branch, so it stays a human decision. Surface it
|
|
|
466
855
|
wait:
|
|
467
856
|
|
|
468
857
|
> Reviewed and approved — PR #<pr> is here: <url>.
|
|
469
|
-
> (1) merge it yourself, (2) I'll merge it (`gh pr merge`), or
|
|
858
|
+
> (1) merge it yourself, (2) I'll merge it once checks are green (`gh pr merge`), or
|
|
470
859
|
> (3) run `/review-pr` first for a curated inline pass.
|
|
471
860
|
|
|
472
|
-
|
|
473
|
-
|
|
861
|
+
A **stacked partition child** reaches this gate only once its PR base is the default
|
|
862
|
+
branch — check `baseRefName` first and retarget/merge-in per `.claude/agents/partition.md`;
|
|
863
|
+
never merge a part into a sibling's branch. The task stays at `harness:status:in-review`
|
|
864
|
+
while it waits — there is no "approved-awaiting-merge" rung between review and merge (the `closeout-pending` status is
|
|
474
865
|
**post**-merge, for a parked closeout record PR — not this gate). When the
|
|
475
866
|
human merges (in the GitHub UI, by CLI, or by authorizing you to run `gh pr merge`),
|
|
476
867
|
proceed to closeout. **GitHub is the source of truth for the merge, not this
|
|
477
868
|
conversation** — closeout confirms it via `gh pr view`.
|
|
478
869
|
|
|
870
|
+
### Executing an authorized merge — the checks precondition
|
|
871
|
+
|
|
872
|
+
"Merge it" is a **conditional authorization**: the agent merges _when it is safe to do
|
|
873
|
+
so_, not an order to execute immediately. The human's yes carries the implicit
|
|
874
|
+
assumption that CI is fine; this contract makes that assumption an explicit
|
|
875
|
+
precondition. Not green ⇒ you never merge on your own — you re-present this gate with
|
|
876
|
+
the real state. (The failure pattern this kills: nobody _decided_ to merge on red — an
|
|
877
|
+
authorization whose background assumption was false got executed without looking.)
|
|
878
|
+
|
|
879
|
+
So never run a bare `gh pr merge`. Execute the merge through the shared executor, which
|
|
880
|
+
verifies the check status the platform reports for the PR and waits — bounded — for
|
|
881
|
+
pending checks (`merge.checks_timeout_secs` in `harness.config.yml`, default ~10 min).
|
|
882
|
+
For a task PR, always pass `--approve-issue` with the task's issue number — that arms
|
|
883
|
+
the **stale-approve guard**: the executor reads the Reviewer's APPROVE record
|
|
884
|
+
(`Reviewed-tree` / `Diff-fingerprint`) from that issue and refuses to merge content
|
|
885
|
+
that no longer matches what the APPROVE reviewed (a clean update-branch stays valid;
|
|
886
|
+
any content change does not):
|
|
887
|
+
|
|
888
|
+
```bash
|
|
889
|
+
.claude/hooks/lib/merge-pr.sh <pr> --approve-issue <issue> --squash # merge-strategy flags pass through to `gh pr merge`
|
|
890
|
+
```
|
|
891
|
+
|
|
892
|
+
Act on its exit code:
|
|
893
|
+
|
|
894
|
+
- **0 — merged.** Proceed to closeout.
|
|
895
|
+
- **10 — checks red.** Never merge on red by omission. Re-present the gate with the
|
|
896
|
+
failure: "checks red: <check> fails — merge anyway / wait / want to look at it?".
|
|
897
|
+
Merging on red stays possible, but only as the human's informed decision — on that
|
|
898
|
+
explicit yes, re-run with `--force`.
|
|
899
|
+
- **30 — still pending after the bound.** The authorization never expires silently; it
|
|
900
|
+
expires by asking: "checks still running after <N> min — keep waiting, or leave it
|
|
901
|
+
and I'll tell you when they settle?". "Keep waiting" = re-run the executor (a fresh
|
|
902
|
+
bound, or a longer one via `--timeout-secs <secs>`).
|
|
903
|
+
- **40 — stale approve: the PR content changed after the review.** A push landed in
|
|
904
|
+
the merge-gate window (another dev, a re-invoked implementer) and the diff no longer
|
|
905
|
+
matches what the APPROVE reviewed. Never merge it on the old verdict: route the PR
|
|
906
|
+
back to the Reviewer — the same back-to-review route as the comments path below; a
|
|
907
|
+
fresh APPROVE re-records the hashes — then re-surface this gate. Merging anyway
|
|
908
|
+
stays possible, but only as the human's informed decision — on that explicit yes,
|
|
909
|
+
re-run with `--force` (which skips the checks precondition too: make sure the
|
|
910
|
+
informed yes covers both).
|
|
911
|
+
- **20 — no checks reported (after the grace window).** Never merge alone, whatever the
|
|
912
|
+
cause — repo with no CI, CI outage, path filters: don't try to auto-classify. Ask:
|
|
913
|
+
"I saw no checks available on this PR — merge?". If the human's answer is standing
|
|
914
|
+
("this repo has no CI — merge without checks"), record it **now** as
|
|
915
|
+
`merge.allow_no_checks: true` in `harness.config.yml` (the executor reads the working
|
|
916
|
+
tree, so no later merge — including this task's closeout — re-asks); the line lands
|
|
917
|
+
on the base with the closeout record PR's commit, never a direct push. For the merge
|
|
918
|
+
at hand, re-run with `--force`.
|
|
919
|
+
- **1 (or any other non-zero)** — not merged, and **not a verdict**: either the check
|
|
920
|
+
status could not be read (`gh`/auth/network failure — the precondition is
|
|
921
|
+
unverifiable, which is _not_ the same as "checks failed") or the merge attempt itself
|
|
922
|
+
was rejected (branch protection wants approval, a conflict, a bad ref). The
|
|
923
|
+
executor's stderr says which — surface that, verbatim, at the gate.
|
|
924
|
+
|
|
925
|
+
The precondition is platform-neutral by design — "the merge executor verifies the check
|
|
926
|
+
status the platform reports for the PR"; `gh` is the GitHub rendering of it. Branch
|
|
927
|
+
protection is the user's belt: the harness executes merges and cannot assume it is
|
|
928
|
+
configured, so the executor carries its own precondition either way.
|
|
929
|
+
|
|
479
930
|
### When the human leaves review comments instead of merging
|
|
480
931
|
|
|
481
932
|
The human may respond at this gate not by merging but by **leaving comments on the PR**.
|
|
@@ -490,7 +941,14 @@ of an `in-review` task surfaces the open PR's comments and routes here — see
|
|
|
490
941
|
route as a Reviewer rejection: re-invoke the Implementer sub-agent (fresh context)
|
|
491
942
|
with the feedback (transient, no dedicated label). Skip pure acknowledgements or
|
|
492
943
|
questions that need no code.
|
|
493
|
-
3. **
|
|
944
|
+
3. **Route the fixed PR to re-review.** The fix commits are new, unreviewed content —
|
|
945
|
+
the Reviewer's standing APPROVE no longer covers them (and the executor's
|
|
946
|
+
stale-approve guard would refuse the merge with exit 40 anyway). Invoke the
|
|
947
|
+
Reviewer on the updated PR (continued re-review of its own findings when the
|
|
948
|
+
comments echo them; fresh otherwise); an approving verdict posts a fresh APPROVE
|
|
949
|
+
with fresh hashes. A reject routes back to step 2 — never draft "done" replies on
|
|
950
|
+
work that hasn't re-passed review.
|
|
951
|
+
4. **After the re-APPROVE, draft — do not auto-post — the replies.** Compose a short
|
|
494
952
|
"done ✅" reply threading each addressed comment, then **offer to post them with one
|
|
495
953
|
confirmation**. Posting a reply inside someone's review thread is outward-facing and
|
|
496
954
|
notifies the reviewer, so it is a **HITL gate, never automatic**. On approval, post
|
|
@@ -499,7 +957,7 @@ of an `in-review` task surfaces the open PR's comments and routes here — see
|
|
|
499
957
|
identity it uses for issues, PRs, and merges; there is no separate bot identity). The
|
|
500
958
|
fix commit is visible on the PR regardless, so declining only skips the
|
|
501
959
|
acknowledgement, not the fix.
|
|
502
|
-
|
|
960
|
+
5. **Re-surface the merge gate** above.
|
|
503
961
|
|
|
504
962
|
## Closeout
|
|
505
963
|
|
|
@@ -508,17 +966,26 @@ GitHub — `gh pr view <pr> --json state,mergedAt` reports `MERGED`, regardless
|
|
|
508
966
|
was merged). Closeout **archives, it does not delete, and it records via a dedicated PR**:
|
|
509
967
|
it raises durable decisions to ADRs, `git mv`s the spec + `discoveries.md`
|
|
510
968
|
into `.claude/state/tasks/_archive/<id>/`, drops only `progress.md`, and lands the
|
|
511
|
-
`history.md` append + the archival on a `harness/closeout-<id>` PR merged
|
|
512
|
-
|
|
513
|
-
the
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
969
|
+
`history.md` append + the archival on a `harness/closeout-<id>` PR self-merged through
|
|
970
|
+
the same checks-precondition executor as the merge gate (`.claude/hooks/lib/merge-pr.sh`
|
|
971
|
+
— **the executor never self-merges on anything but green**; no standing human
|
|
972
|
+
authorization exists on this path, so every not-green outcome takes one of the skill's
|
|
973
|
+
exits — self-fix, park, or a human's informed decision — never an autonomous merge).
|
|
974
|
+
Nothing is pushed direct to the base — the
|
|
975
|
+
closeout record obeys the same branch isolation as every other change. The skill owns
|
|
976
|
+
the full mechanics, including what to do on red, absent, or still-pending checks. A
|
|
977
|
+
task that is a **part of a partition** (its body carries `Part <k> of #<parent>`) also,
|
|
978
|
+
at finalize, ticks its row in the parent's checklist and — on the last part — closes the
|
|
979
|
+
parent with a summary comment; the skill's finalize step owns it (mechanics in
|
|
980
|
+
`.claude/agents/partition.md`).
|
|
981
|
+
|
|
982
|
+
**Closeout splits into two phases** because the self-merge only happens on green. If the
|
|
983
|
+
closeout PR's checks pass within the bound (and branch protection asks nothing more),
|
|
984
|
+
closeout finalizes in one go. Otherwise — checks red (and not self-fixable), no checks
|
|
985
|
+
and no standing answer, timeout, or protection requiring human approval — the PR waits:
|
|
986
|
+
flip the issue to `harness:status:closeout-pending`, tell the human the record PR is
|
|
987
|
+
open and why it didn't merge, and stop. A later `/resume` of a `closeout-pending` task
|
|
988
|
+
finalizes once that PR is merged (see Dispatch → RESUME).
|
|
522
989
|
|
|
523
990
|
**Closeout is the Architect's reliable activation point**: before
|
|
524
991
|
archiving, the skill drives three durable-capture activations, **asymmetric by design** —
|
|
@@ -528,7 +995,7 @@ path (the Architect reads the merged diff itself) — no pre-offer, the map
|
|
|
528
995
|
tracks reality and the edit is reviewed in the closeout PR), and `playbook-iterate` (HITL
|
|
529
996
|
offer once per task, for a reusable pattern no `T6` conflict already routed). Closeout
|
|
530
997
|
never drafts the artifact itself — it lights up the Architect, who owns the criteria
|
|
531
|
-
(§Architect
|
|
998
|
+
(§Architect). Each activation no-ops when its skill isn't installed.
|
|
532
999
|
|
|
533
1000
|
Before any of this, enforce the discovery invariant: **no `discoveries.md` entry may lack
|
|
534
1001
|
a resolved `**Resolution**`block, and no`harness:discovery:\*` label may remain.** An
|