@lemoncode/lemony 0.1.2 → 0.3.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 +13 -4
- package/catalog/agents/implementer.md +87 -8
- package/catalog/agents/orchestrator.md +643 -386
- package/catalog/agents/partition.md +316 -0
- package/catalog/agents/reviewer.md +356 -21
- package/catalog/agents/spec-author.md +16 -4
- package/catalog/agents/spinoff.md +100 -0
- package/catalog/agents/triage.md +41 -0
- package/catalog/agents/ui-design.md +147 -0
- package/catalog/agents/ui-designer.md +3 -2
- 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 -10
- package/catalog/commands/triage.md +4 -3
- package/catalog/harness.config.schema.json +40 -0
- package/catalog/hooks/lib/merge-pr.sh +699 -0
- package/catalog/schemas/tier2-events-history.md +17 -0
- package/catalog/schemas/tier2-events.md +10 -10
- package/catalog/skills/mutation-testing/SKILL.md +80 -19
- package/catalog/skills/prd-to-spec/SKILL.md +74 -2
- package/catalog/skills/raise-discovery/SKILL.md +6 -0
- package/catalog/skills/resolve-discovery/SKILL.md +12 -7
- package/catalog/skills/security-review/SKILL.md +119 -6
- package/catalog/skills/spec-compliance-check/SKILL.md +8 -4
- package/catalog/skills/spec-to-issue/SKILL.md +7 -1
- package/catalog/skills/task-closeout/SKILL.md +85 -20
- package/catalog/skills/test-gap-report/SKILL.md +4 -0
- package/catalog/skills/triage-issue/SKILL.md +65 -4
- package/catalog/skills/verify/SKILL.md +3 -0
- package/catalog/templates/claude-code/agents.md.tpl +50 -16
- package/catalog/templates/claude-code/harness.config.yml.tpl +33 -0
- package/dist/cli.mjs +744 -37
- package/package.json +10 -6
|
@@ -16,6 +16,28 @@ invokes sub-agents with fresh context, runs the human approval gate, manages the
|
|
|
16
16
|
issue label lifecycle, and runs closeout. The entry-protocol summary lives in
|
|
17
17
|
`agents.md`; this file is the operational detail.
|
|
18
18
|
|
|
19
|
+
## Companion files (lazy — read on trigger only)
|
|
20
|
+
|
|
21
|
+
Four procedures live in sibling files, kept out of boot context by design (a fifth
|
|
22
|
+
sibling, `.claude/agents/fit-assessment.md`, is a reference doc for the task-fit
|
|
23
|
+
dial — not a procedure; consult it on a borderline classification). Read the
|
|
24
|
+
file **when its trigger fires**, then run it as if it were a section here:
|
|
25
|
+
|
|
26
|
+
- **`.claude/agents/triage.md`** — the L2 lightweight round-trip. Trigger: dispatch
|
|
27
|
+
lands on TRIAGE, or a pending stub's fit assessment lands L2.
|
|
28
|
+
- **`.claude/agents/ui-design.md`** — UI design (DEFINE + REVIEW). Trigger: the
|
|
29
|
+
DEFINE activation gate fires (L1 step 3), an implemented UI change reaches
|
|
30
|
+
review (`ui-handoff.md` exists), a resume lands on the sub-state
|
|
31
|
+
`awaiting design definition`, or `/sync-design-tokens`.
|
|
32
|
+
- **`.claude/agents/spinoff.md`** — mid-task capture (the `/spinoff` offer). Trigger:
|
|
33
|
+
the human mentions what may be an independent, non-blocking defect, or a sub-agent
|
|
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.
|
|
40
|
+
|
|
19
41
|
## Dispatch
|
|
20
42
|
|
|
21
43
|
Parse the first prompt's intent (or honor a slash command):
|
|
@@ -29,15 +51,17 @@ Parse the first prompt's intent (or honor a slash command):
|
|
|
29
51
|
— a `spec-ready` issue resumes at the approval gate, an `in-progress` one at the
|
|
30
52
|
active subtask. A **`harness:status:spec-in-progress`** task whose `progress.md` records
|
|
31
53
|
the sub-state **`awaiting design definition`** (+ `harness:needs-design`) is a design
|
|
32
|
-
parked at "stop for handoff"
|
|
33
|
-
yourself** to finish `ui-handoff.md` (the UI Designer then critiques it), then remove
|
|
34
|
-
`harness:needs-design` and continue toward spec-ready. A **`harness:status:closeout-pending`** task is an exception with
|
|
54
|
+
parked at "stop for handoff": read `.claude/agents/ui-design.md` and re-enter there. A **`harness:status:closeout-pending`** task is an exception with
|
|
35
55
|
nothing to check out: its task PR already merged and its state is archived under
|
|
36
56
|
`_archive/<id>/`. Its issue is **closed** (the task PR's `Closes #<id>` fired), so it
|
|
37
57
|
surfaces in the queue only when you list closed issues too (`--state all`) — an
|
|
38
|
-
open-only listing misses it. It resumes at closeout **finalize** —
|
|
39
|
-
`harness/closeout-<id>` record PR
|
|
40
|
-
|
|
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
|
|
41
65
|
has **no branch and no task state yet**) is the exception: there is nothing to check
|
|
42
66
|
out. Read the captured context — the **issue title is the always-present symptom**; the
|
|
43
67
|
body adds a location/pointer and the `Discovered during #<parent>` ref when they exist
|
|
@@ -52,17 +76,22 @@ Parse the first prompt's intent (or honor a slash command):
|
|
|
52
76
|
only at this commit point (entering a level, or closing) — so an **abandoned pickup
|
|
53
77
|
correctly stays in the queue** rather than vanishing half-done. **A stub carrying
|
|
54
78
|
`harness:architecture-drift`** is an `docs/architecture.md` map-fix, not code: run
|
|
55
|
-
the ordinary L2 machinery (branch, PR, the merge
|
|
79
|
+
the ordinary L2 machinery per `.claude/agents/triage.md` (branch, PR, the merge
|
|
80
|
+
gate — a map-fix _is_ reviewable: does
|
|
56
81
|
the map now match reality?), but dispatch the **Architect with `update-architecture`**
|
|
57
82
|
(it reads the map plus the cited divergent area and makes the surgical edit) in place of
|
|
58
83
|
the Implementer; closeout's `update-architecture` then re-runs over that diff as a no-op,
|
|
59
84
|
and there is no spec to archive. **Fallback:** if the project no longer keeps an
|
|
60
85
|
`architecture.md` (the skill is uninstalled), treat it as a normal `pending` stub —
|
|
61
|
-
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.
|
|
62
91
|
- **DEFINE** — "define / new task / I have an idea" → the **L1 full-SDD round-trip**
|
|
63
92
|
below.
|
|
64
|
-
- **TRIAGE** — "bug / error in / broken / fails when" → the **L2 lightweight
|
|
65
|
-
|
|
93
|
+
- **TRIAGE** — "bug / error in / broken / fails when" → the **L2 lightweight
|
|
94
|
+
round-trip**: read `.claude/agents/triage.md` and run it.
|
|
66
95
|
- **ORIENT** — the first prompt carries **no clear intent**: a bare greeting ("hi",
|
|
67
96
|
"hola", "¿qué hay?"), an orientation question ("what should I pick up?", "¿qué
|
|
68
97
|
toca?"), or effectively nothing. This is the proactive half of the session-orient
|
|
@@ -71,8 +100,8 @@ Parse the first prompt's intent (or honor a slash command):
|
|
|
71
100
|
exact same listing `/resume` does with no args. `/resume` (authority: its command
|
|
72
101
|
file) **owns** the precise `gh` queries; ORIENT does not re-specify them, so it
|
|
73
102
|
cannot drift. That queue covers the spec-ready and in-progress tasks, the **`/spinoff`
|
|
74
|
-
pending stubs** (what a human who never types `/resume` would otherwise forget),
|
|
75
|
-
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
|
|
76
105
|
`/triage` (a bug, L2) — and ask which to do. The **start options are unconditional**:
|
|
77
106
|
when the queue is **empty** (nothing parked), still render the menu with just the
|
|
78
107
|
start options — that is the "nothing to resume, start something?" case, **not** a
|
|
@@ -94,9 +123,6 @@ the prompt is ambiguous **between** harness modes (not intentless), ask
|
|
|
94
123
|
**one** disambiguation question, then proceed. Worst case is benign either way (menu not
|
|
95
124
|
rendered at all → the human types `/resume`/`/define` as today, a true no-op; menu shown
|
|
96
125
|
when unwanted → it is ignored), so the boundary is a comfort, not a correctness, call.
|
|
97
|
-
Note this "not rendered" no-op is distinct from the in-menu degradations above (empty
|
|
98
|
-
queue / no `gh`), where the menu **is** rendered with the start options and only the
|
|
99
|
-
listing is skipped.
|
|
100
126
|
|
|
101
127
|
## Task-fit assessment (light, non-blocking)
|
|
102
128
|
|
|
@@ -108,14 +134,25 @@ discardable question; never block on it.
|
|
|
108
134
|
|
|
109
135
|
This paragraph is the canonical criterion — act on it directly. The fuller L1/L2/L3
|
|
110
136
|
model, the spec-or-no-spec call between L1 and L2, and worked examples live in the
|
|
111
|
-
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.
|
|
112
141
|
|
|
113
142
|
## L1 full-SDD round-trip (DEFINE)
|
|
114
143
|
|
|
115
144
|
1. **Grill the idea into a PRD** — run the `grill-with-docs` skill. One question at a
|
|
116
145
|
time, never auto-decide. Output: a PRD at `docs/prds/<topic>-<date>.md`. The PRD is
|
|
117
146
|
yours (creator/maintainer); its `Status:` flips to `completed` when the grill
|
|
118
|
-
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.
|
|
119
156
|
2. **Open the task** — before dispatching anyone, create the tracked task so every
|
|
120
157
|
sub-agent has an issue to label and a branch to work on:
|
|
121
158
|
- `gh issue create` with a skeleton body
|
|
@@ -124,27 +161,33 @@ sibling `fit-assessment.md`; consult it for a borderline classification.
|
|
|
124
161
|
id the task store assigns — the **GitHub issue number** while
|
|
125
162
|
`task_storage.type: github` — as the task `<id>`.
|
|
126
163
|
- Create the task branch `harness/<id>-<slug>` off the default branch
|
|
127
|
-
(`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
|
|
128
167
|
work — spec **and** code — lives on this branch; nothing touches the default
|
|
129
168
|
branch until the human merge gate.
|
|
130
|
-
3. **Design the UI (if it touches UI)** —
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
touch UI skips straight to the spec.
|
|
138
|
-
4. **Dispatch the Spec Author** — invoke the **Spec Author** sub-agent (fresh context
|
|
169
|
+
3. **Design the UI (if it touches UI)** — before any spec work, evaluate the
|
|
170
|
+
**activation gate** (your own LLM judgment, no keyword scorer), two parts both
|
|
171
|
+
true: (1) the repo has a frontend — UI to
|
|
172
|
+
design, not a pure library / CLI / backend — and (2) this task adds or alters
|
|
173
|
+
something a user sees or interacts with. Bias to include on a borderline call.
|
|
174
|
+
Only when it fires, read `.claude/agents/ui-design.md` and run its DEFINE flow
|
|
175
|
+
(design-stop offer → `grill-ui` → UI Designer critique). A task that doesn't
|
|
176
|
+
touch UI skips straight to the spec — without reading the companion.
|
|
177
|
+
4. **Dispatch the Spec Author** — invoke the **Spec Author** sub-agent (fresh context,
|
|
178
|
+
spawn by reference — §Sub-agent invocation) with
|
|
139
179
|
the PRD path (and the `ui-handoff.md` if one was authored), the issue `<id>`, and the
|
|
140
|
-
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
|
|
141
182
|
`tasks/<id>/spec/` — no draft holder, the id is real from the start) then `spec-to-issue`
|
|
142
183
|
(fills the issue **body** from the spec; it creates nothing and moves no labels). It
|
|
143
184
|
returns a summary.
|
|
144
|
-
5. **Reach spec-ready** — on its return
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
185
|
+
5. **Reach spec-ready** — on its return: **before** flipping to spec-ready,
|
|
186
|
+
`ui-handoff.md` must be complete and `harness:needs-design` removed — a
|
|
187
|
+
precondition, not a cleanup: a spec-ready task never carries the label
|
|
188
|
+
(`.claude/agents/ui-design.md`). Then flip
|
|
189
|
+
`harness:status:spec-in-progress → harness:status:spec-ready`,
|
|
190
|
+
then commit and push the task state to the branch so
|
|
148
191
|
anyone can pick it up:
|
|
149
192
|
`git add .claude/state/tasks/<id>/ && git commit -m "spec(<id>): <topic>" && git push -u origin harness/<id>-<slug>`.
|
|
150
193
|
The committed-and-pushed spec plus the spec-ready issue **are** the handoff; the
|
|
@@ -160,17 +203,31 @@ harness:status:spec-ready`, then commit and push the task state to the branch so
|
|
|
160
203
|
`harness:status:in-progress` and proceed **per the mode chosen at the gate**
|
|
161
204
|
(§Implementation mode): **all-at-once** invokes the **Implementer** sub-agent (fresh
|
|
162
205
|
context) once with the `tdd` skill and the branch — it keeps `progress.md` live and
|
|
163
|
-
signals done; **step-by-step** runs the per-
|
|
164
|
-
implementation instead, and rejoins this flow at step 8 after the last
|
|
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
|
+
**pre-commit review ON** (the gate's second knob), either mode runs the zero-commit
|
|
209
|
+
protocol — no commit touches the branch before a checkpoint OK (§Pre-commit review
|
|
210
|
+
ON); in all-at-once its single gate runs when the Implementer signals done,
|
|
211
|
+
**before** step 8 — the PR never opens on uncommitted work.
|
|
165
212
|
8. **Review** — flip to `harness:status:in-review` and **open the PR**
|
|
166
213
|
(`gh pr create`, `harness/<id>-<slug> → <default>`, with `Closes #<id>` in the PR
|
|
167
|
-
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
|
|
168
218
|
the **Reviewer** sub-agent (fresh context) with the `senior-review` skill to review
|
|
169
219
|
that PR. Fresh context is what prevents the Implementer's confirmation bias. **If the
|
|
170
|
-
task touched UI
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
220
|
+
task touched UI** — the durable signal is that `tasks/<id>/spec/ui-handoff.md`
|
|
221
|
+
exists — also invoke the **UI Designer** as a distinct design + a11y lens
|
|
222
|
+
(REVIEW in `.claude/agents/ui-design.md`). Either lens rejecting routes back to the
|
|
223
|
+
Implementer (rejection is transient — no dedicated
|
|
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.
|
|
174
231
|
9. **Merge gate** — see below. Human-explicit, never auto-merged.
|
|
175
232
|
10. **Closeout** — see below.
|
|
176
233
|
|
|
@@ -184,7 +241,29 @@ spec-ready queue (a RESUME of a `spec-ready` issue — the typical handoff), che
|
|
|
184
241
|
its branch, read the spec cold, and run this gate before writing any code.
|
|
185
242
|
|
|
186
243
|
1. Present the spec to the human: a short summary plus links to
|
|
187
|
-
`tasks/<id>/spec/{requirements,design,tasks}.md` and the issue.
|
|
244
|
+
`tasks/<id>/spec/{requirements,design,tasks}.md` and the issue. `tasks.md`
|
|
245
|
+
carries the **step grouping** the step-by-step loop would follow (risk-sized
|
|
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
|
+
|
|
188
267
|
2. Wait for an explicit decision:
|
|
189
268
|
- **Approve** → ask the **implementation mode** in the same interaction
|
|
190
269
|
(§Implementation mode — the human just read `tasks.md` cold, the best moment to
|
|
@@ -207,7 +286,7 @@ its branch, read the spec cold, and run this gate before writing any code.
|
|
|
207
286
|
3. Never self-approve. Whoever implements signs off — catching a misunderstanding here,
|
|
208
287
|
before any code, is far cheaper than at review. The `/define`, `/resume`, `/triage`
|
|
209
288
|
slash commands are thin mode-forcers layered on this same behavior; the urgency
|
|
210
|
-
override `/hotfix` (which
|
|
289
|
+
override `/hotfix` (which defers the wait while the Reviewer still runs async) and the
|
|
211
290
|
`/bypass` escape hatch (L3) are documented in `.claude/commands/`. The gate itself is
|
|
212
291
|
permanent — the commands force a mode, they never remove a human gate (only `/hotfix`
|
|
213
292
|
defers one, by contract).
|
|
@@ -224,13 +303,14 @@ these descriptions:
|
|
|
224
303
|
> you evaluate the final result once. Choose this for small or low-risk
|
|
225
304
|
> specs, or when you'd rather not be interrupted.
|
|
226
305
|
>
|
|
227
|
-
> **Step-by-step** — The Implementer completes ONE
|
|
228
|
-
>
|
|
306
|
+
> **Step-by-step** — The Implementer completes ONE group of tasks at a time
|
|
307
|
+
> (the risk-sized groups in the `tasks.md` you just approved). Each group is
|
|
308
|
+
> reviewed in isolation (implementer↔reviewer fix-loop until clean, max 3
|
|
229
309
|
> rejections), then paused for you: inspect the code, run it, and answer
|
|
230
310
|
> **OK** / **request changes** / **OK and switch to all-at-once**. After the
|
|
231
|
-
> last
|
|
311
|
+
> last group, the normal flow resumes unchanged (full-pass review + merge
|
|
232
312
|
> gate). Choose this for large or risky specs where a single end-of-task
|
|
233
|
-
> review dump would be too much to evaluate. Note:
|
|
313
|
+
> review dump would be too much to evaluate. Note: groups with no runnable
|
|
234
314
|
> surface (internal refactors) still checkpoint — inspect and OK.
|
|
235
315
|
|
|
236
316
|
Record the answer in `progress.md` (`Mode: …`) — it is execution state, not a label and
|
|
@@ -239,55 +319,181 @@ applies. The mode is switchable **downward only** (step-by-step → all-at-once,
|
|
|
239
319
|
at every checkpoint); there is no upgrade path — all-at-once has no stop where the
|
|
240
320
|
switch could be offered.
|
|
241
321
|
|
|
242
|
-
|
|
322
|
+
**The second knob, same interaction — pre-commit review.** Alongside the mode, settle
|
|
323
|
+
whether the human reviews the work **uncommitted**, before anything lands on the
|
|
324
|
+
branch. Read `implementation.pre_commit_review` from `harness.config.yml` first (an
|
|
325
|
+
absent key means `human`): `on` / `off` **pin the answer — record it without asking**;
|
|
326
|
+
`human` (the default) asks here, in the same interaction, suggesting OFF:
|
|
243
327
|
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
328
|
+
> **Pre-commit review OFF** (default) — the Implementer commits and best-effort
|
|
329
|
+
> pushes as it works, exactly as today; checkpoints present committed work per
|
|
330
|
+
> the checkpoint contract.
|
|
331
|
+
>
|
|
332
|
+
> **Pre-commit review ON** — the Implementer makes **zero commits**: the work
|
|
333
|
+
> accumulates in the worktree under a staging protocol, you review it
|
|
334
|
+
> **uncommitted** (the Source Control panel shows it natively, and you can edit
|
|
335
|
+
> it in place), and the branch receives its first commit only on your OK.
|
|
336
|
+
|
|
337
|
+
Record it next to the mode line (`Pre-commit review: off` / `on`) — execution state,
|
|
338
|
+
like the mode. The knob is **L1-only** (it rides this question; L2 always runs today's
|
|
339
|
+
flow) and orthogonal to the mode: it moves **when the branch first receives commits**,
|
|
340
|
+
never whether review happens. In step-by-step the uncommitted gate runs per group; in
|
|
341
|
+
all-at-once it is **one gate over the entire implementation** (its single "group" is
|
|
342
|
+
the whole `tasks.md`). ON's mechanics — staging save-points, full deferral,
|
|
343
|
+
watch-fors — live in §Pre-commit review ON.
|
|
344
|
+
|
|
345
|
+
## Step-by-step implementation (the per-group loop)
|
|
346
|
+
|
|
347
|
+
One step = one `tasks.md` **group**, 1:1 with the grouping the human approved at the
|
|
348
|
+
spec gate. Tasks inside a group stay atomic — the grouping sets how often review and
|
|
349
|
+
the human checkpoint run, never how the Implementer works. A `tasks.md` without group
|
|
350
|
+
headers (a legacy or hand-written spec) runs **one task per group** — the pre-grouping
|
|
351
|
+
cadence. Tasks added mid-implementation (a resolved discovery) default to **their own
|
|
352
|
+
group**, appended at the end of the loop — M grows, and already-resolved step numbers
|
|
353
|
+
never shift; a narrowing (a resolved partition discovery) trims or removes groups from
|
|
354
|
+
the current one onward and trims a resolved group in place — M may shrink, resolved step
|
|
355
|
+
numbers still never shift. For each group, in order:
|
|
356
|
+
|
|
357
|
+
1. **Implement the group** — first **record the group's anchor**: the branch head OID
|
|
358
|
+
at this moment (= the end of the previous approved group; the head at
|
|
359
|
+
implementation start for group 1) goes on the group's step-log line — the
|
|
360
|
+
checkpoint contract diffs against it. Then invoke the **Implementer** sub-agent
|
|
361
|
+
(fresh context, as always) scoped to **this one group**: give it the branch, the
|
|
362
|
+
task-state paths, and the group's id + header line in `tasks.md` — by reference
|
|
363
|
+
(§Sub-agent invocation), it reads the group's tasks from the file itself (`tdd`
|
|
364
|
+
skill — checkbox by checkbox with small commits, as always). It commits to the
|
|
365
|
+
branch (pre-commit review ON: it **stages** instead of committing — point the
|
|
366
|
+
invocation at the staging protocol in `implementer.md`), logs to `progress.md`,
|
|
367
|
+
and signals done. **No PR yet** — the PR opens after the last group,
|
|
251
368
|
as in all-at-once; the human inspects and runs the **local checkout** (a checkpoint
|
|
252
|
-
never needs GitHub
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
the **
|
|
369
|
+
never needs GitHub; the branch is pushed best-effort at each checkpoint — step 3).
|
|
370
|
+
2. **Per-step review** — invoke the **Reviewer** sub-agent (fresh context) **by
|
|
371
|
+
reference** (§Sub-agent invocation): the group id + its header line — it reads the
|
|
372
|
+
group's tasks and requirements itself — scoped to the **group's diff against its
|
|
373
|
+
slice of the spec**. **Hand the group's anchor OID (step 1's step-log line) in the
|
|
374
|
+
spawn prompt in both knob states** — the Reviewer self-validates its ledger
|
|
375
|
+
against it (`--anchor`); pre-commit review ON additionally hands the ON flag —
|
|
376
|
+
the diff is then the group's **uncommitted work against the anchor**,
|
|
377
|
+
`git diff <anchor> -- ':(exclude).claude/state'`; worktree == index
|
|
378
|
+
within that scope at review time. The
|
|
379
|
+
verdict is **local**
|
|
257
380
|
(`progress.md` + session narration) — no issue comment; only the final full-pass
|
|
258
381
|
posts one. **On a UI-touching step**, also run the deterministic design gates here —
|
|
259
|
-
`design-tokens validate` + `design-tokens contrast`, agent-free and cheap — and let the
|
|
382
|
+
`lemony design-tokens validate` + `lemony design-tokens contrast`, agent-free and cheap — and let the
|
|
260
383
|
project's a11y lint ride the step's lint; a failure is an early-catch REJECT so a bad
|
|
261
384
|
token pair or hardcoded value can't propagate to a later step. The **judgment** design
|
|
262
385
|
lenses (`design-critique` / `a11y-audit`) do **not** run per-step — they are full-pass
|
|
263
|
-
only (
|
|
386
|
+
only (REVIEW in `.claude/agents/ui-design.md`).
|
|
387
|
+
|
|
388
|
+
**On an APPROVE, validate the evidence ledger before relaying it** — agent-free and
|
|
389
|
+
cheap, like the design gates, and for the same reason: a check an agent performs is a
|
|
390
|
+
check an agent can rationalize away. The Reviewer wrote
|
|
391
|
+
`.claude/state/tasks/<id>/review-ledger/step-<N>.json` (its `## Evidence ledger`
|
|
392
|
+
contract) and self-validated it; you run the same verb:
|
|
393
|
+
|
|
394
|
+
```bash
|
|
395
|
+
lemony review-ledger validate --task-id=<id> --anchor=<oid> --step=<N>
|
|
396
|
+
```
|
|
397
|
+
|
|
398
|
+
`<oid>` is the anchor **you** recorded when the group started (step 1's step-log
|
|
399
|
+
line) — never one the Reviewer reports. Trust is by double execution, the
|
|
400
|
+
Diff-fingerprint's own pattern: the Reviewer self-validated with the anchor its
|
|
401
|
+
invocation handed it, and your authoritative run catches a green built on the
|
|
402
|
+
wrong anchor — that red routes like any sidecar-side red below.
|
|
403
|
+
|
|
404
|
+
**In pre-commit review OFF, run the verb on a clean tree.** The floor's owed set
|
|
405
|
+
reads the diff against the anchor _including uncommitted work_ — correct in ON,
|
|
406
|
+
where the group's work is uncommitted by design, but in OFF an uncommitted tracked
|
|
407
|
+
edit outside `.claude/state` at validation time is a **human delta the Reviewer
|
|
408
|
+
never saw**, and it would red the floor as `unaccounted-file` on a ledger that is
|
|
409
|
+
not wrong. Check `git status --porcelain -- . ':(exclude).claude/state'` first; a
|
|
410
|
+
hit routes through the checkpoint contract's human-delta check **before** you
|
|
411
|
+
validate — never burn the Reviewer retry on unreviewed human content.
|
|
412
|
+
|
|
413
|
+
Green → the APPROVE stands; carry its counts — both halves, `criteria n/m (basis: …)`
|
|
414
|
+
and `gates n/m (basis: …)` — onto the step-log line: the gates basis is the one
|
|
415
|
+
place a green run shows the human whether the project reviews against declared
|
|
416
|
+
gates or the bare real-run floor (`undeclared`).
|
|
417
|
+
Red → **an APPROVE with a red ledger is never relayed**, and a red ledger is not a
|
|
418
|
+
REJECT of the code: the Implementer is not at fault and is **never** re-invoked for
|
|
419
|
+
it. Route on the `[kind]` lines the verb prints — it closes with
|
|
420
|
+
`N spec-side problem(s) … do not retry the Reviewer while any of these stands.`
|
|
421
|
+
whenever any problem lives outside the sidecar:
|
|
422
|
+
|
|
423
|
+
- **any spec-side problem** — the verb counts them out loud (`unknown-risk-class`, a
|
|
424
|
+
tag or ref list that did not parse, a duplicated group number, an orphan task, an
|
|
425
|
+
empty group, a dangling requirement ref, a step with no group) → no retry. They
|
|
426
|
+
live in `tasks.md` / `requirements.md`, which the Reviewer cannot fix: stop and
|
|
427
|
+
bring them to the human as an **anticipated checkpoint** (below) with the verb's
|
|
428
|
+
lines as the content — the spec needs a decision, and the fix routes as a discovery
|
|
429
|
+
(§Discovery mediation). Once the resolution lands in the spec, re-run the verb on
|
|
430
|
+
the sidecar on disk: green → the checkpoint; a sidecar-side red then follows the
|
|
431
|
+
one-retry rule below. The human's `ok` on an anticipated checkpoint is their call,
|
|
432
|
+
as at every gate — what never happens is you relaying a red as an APPROVE.
|
|
433
|
+
- **anything else** (a problem in the sidecar itself) → re-invoke the **Reviewer**
|
|
434
|
+
(fresh, as always) **once**, with the
|
|
435
|
+
verb's `[kind] message` lines **verbatim** in the spawn prompt — the delta is the
|
|
436
|
+
payload (§Sub-agent invocation). This is its own cap of **one retry**, separate from
|
|
437
|
+
the REJECT cap below: a Reviewer that cannot hit the format must not burn the
|
|
438
|
+
Implementer's budget. Set the open step's transient line to
|
|
439
|
+
`awaiting ledger retry (step N/M, retry 1/1)` (step 5) **before** re-invoking — the
|
|
440
|
+
retry is spent the moment it is issued, so a session that dies mid-retry resumes
|
|
441
|
+
knowing it is gone. Validate the second APPROVE the same way; a **second red** →
|
|
442
|
+
anticipated checkpoint with the verb's lines as the content. A `/resume` that lands
|
|
443
|
+
on the `awaiting ledger retry` line re-runs the verb on the sidecar on disk: green →
|
|
444
|
+
step 3's checkpoint; red → the anticipated checkpoint, the retry being spent.
|
|
445
|
+
|
|
446
|
+
A REJECT owes no ledger — do not run the verb on one. A red ledger emits no telemetry
|
|
447
|
+
of its own in this version; the step-log line is its record.
|
|
448
|
+
|
|
449
|
+
On REJECT, re-invoke the Implementer (fresh) with the
|
|
264
450
|
feedback and re-review — the fix-loop runs until clean, **capped at 3 REJECTs on the
|
|
265
451
|
same step**:
|
|
266
452
|
at the cap, stop the loop and bring the disagreement to the human as an
|
|
267
|
-
**anticipated checkpoint** (
|
|
268
|
-
an ambiguous spec or a real disagreement — the human arbitrates). The anticipated
|
|
453
|
+
**anticipated checkpoint** (the human arbitrates). The anticipated
|
|
269
454
|
checkpoint **is** the checkpoint of step 3 — same three answers, same
|
|
270
455
|
`step_completed` emit (here `review_iterations` is 3) and the same transient
|
|
271
456
|
`awaiting human checkpoint (step N/M)` line in `progress.md` — except you present
|
|
272
457
|
the unresolved disagreement (both positions, the spec slice) instead of a clean
|
|
273
458
|
step.
|
|
274
|
-
|
|
275
|
-
|
|
459
|
+
|
|
460
|
+
In pre-commit review ON, `OK` is also the moment the group's single code commit
|
|
461
|
+
lands, and `changes` sends the fresh Implementer to iterate **over the worktree**
|
|
462
|
+
— both per §Pre-commit review ON.
|
|
463
|
+
|
|
464
|
+
3. **Human checkpoint** — first run §Checkpoint contract's **spec check** (item
|
|
465
|
+
4): the state commit below sweeps `spec/`, so an unconfirmed human spec edit
|
|
466
|
+
must route as a discovery **before** anything commits it. Then set the open
|
|
467
|
+
step's `progress.md` line to
|
|
468
|
+
`awaiting human checkpoint (step N/M)` (step 5), then commit the task state and
|
|
469
|
+
**push the branch, best-effort**, as **one composite invocation** (§Turn economy).
|
|
470
|
+
This commit and step 4's OK-side twin are **yours, anchored to your checkpoint
|
|
471
|
+
turns** — never delegated to a sub-agent's return path (a commit left to a
|
|
472
|
+
sub-agent may never land, and the checkpoint is then invisible to a cold
|
|
473
|
+
`/resume`):
|
|
276
474
|
|
|
277
475
|
```bash
|
|
278
476
|
git add .claude/state/tasks/<id>/ && \
|
|
279
|
-
git commit -m "step(<id>): step <N> awaiting checkpoint"
|
|
280
|
-
|
|
477
|
+
git commit -m "step(<id>): step <N> awaiting checkpoint" \
|
|
478
|
+
-- .claude/state/tasks/<id>/; \
|
|
479
|
+
git push # best-effort — a failure warns, never blocks
|
|
281
480
|
```
|
|
282
481
|
|
|
283
|
-
The commit
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
-
|
|
482
|
+
The commit's pathspec keeps it honest: anything else already staged (e.g. a
|
|
483
|
+
human edit staged ahead of the checkpoint) stays staged instead of riding
|
|
484
|
+
mislabeled into the state commit.
|
|
485
|
+
|
|
486
|
+
A failed push — offline, auth — warns and never blocks; the next push carries
|
|
487
|
+
it. This long wait is where a session likeliest dies — the push is what lets
|
|
488
|
+
another machine's `/resume` see the pending checkpoint.
|
|
489
|
+
**Pre-commit review ON skips this state commit and push entirely** — until the
|
|
490
|
+
OK the branch receives _nothing_, state included (§Pre-commit review ON): update
|
|
491
|
+
the `progress.md` line on disk only, accepting the documented price that the
|
|
492
|
+
pending checkpoint is invisible to a cold `/resume` from another machine.
|
|
493
|
+
Then present the step **per §Checkpoint contract**: what was built and how to
|
|
494
|
+
run it, the group's commits and touched files, the anchored group diff, the
|
|
495
|
+
richer-view offer, and the human-delta check. Three answers:
|
|
496
|
+
- **OK** → emit `step_completed` (below), next group.
|
|
291
497
|
- **Changes** (with feedback) → fresh Implementer with the feedback → per-step
|
|
292
498
|
review again (step 2; the review-iteration count resets) → checkpoint again.
|
|
293
499
|
Human-requested changes go through review like any other fix — **nothing reaches
|
|
@@ -302,13 +508,25 @@ order:
|
|
|
302
508
|
(`Mode: step-by-step (downgraded to all-at-once at step N)` — the gate choice
|
|
303
509
|
stays first; the downgrade is a suffix, because `task_done.mode` records the gate
|
|
304
510
|
choice), and run the **remaining** tasks as a single Implementer invocation
|
|
305
|
-
(
|
|
511
|
+
(all-at-once; the pre-commit review knob keeps its recorded setting).
|
|
512
|
+
Checkpoint OKs already given stand.
|
|
306
513
|
|
|
307
514
|
Aborting needs no protocol: the human interrupts the session; `/resume` picks the
|
|
308
515
|
step sub-state back up from `progress.md`.
|
|
309
516
|
|
|
310
517
|
4. **Telemetry** — every **resolved checkpoint** emits one event (so a step the human
|
|
311
|
-
sent back emits more than once, same `--step`)
|
|
518
|
+
sent back emits more than once, same `--step`); the emit, the step's resolved
|
|
519
|
+
`progress.md` line (step 5) and the resolution state commit (on OK, plain or
|
|
520
|
+
downgrade: `step(<id>): step <N> checkpoint OK`, carrying the same
|
|
521
|
+
`-- .claude/state/tasks/<id>/` pathspec as step 3's twin so other staged
|
|
522
|
+
content never rides mislabeled) + best-effort push ride as
|
|
523
|
+
**one composite turn** (§Turn economy) — without that commit a cold `/resume`
|
|
524
|
+
still reads `awaiting` after the last group. Pre-commit review ON: on OK the
|
|
525
|
+
same composite additionally carries the group's **code commit** — the deferral
|
|
526
|
+
ends here, so code commit + state commit + push + emit land together (exact
|
|
527
|
+
commands, the pathspec split of the mixed index, and the all-at-once no-emit
|
|
528
|
+
rule in §Pre-commit review ON); on
|
|
529
|
+
`changes` nothing is committed and the deferral continues. `<review-iterations>` is the number
|
|
312
530
|
of Reviewer invocations that preceded this checkpoint (≥ 1; resets after a
|
|
313
531
|
"changes"):
|
|
314
532
|
|
|
@@ -325,7 +543,8 @@ order:
|
|
|
325
543
|
**Attribution — name the component the checkpoint friction is about, or
|
|
326
544
|
omit.** The two `--attributed-*` flags are **optional**; they're meaningful when
|
|
327
545
|
the checkpoint surfaced friction (`changes`, or repeated `review-iterations`) and
|
|
328
|
-
you can name what produced it — usually the Implementer
|
|
546
|
+
you can name what produced it — usually the Implementer; a ledger retry is the
|
|
547
|
+
Reviewer's own. **Omit both on a clean
|
|
329
548
|
`ok` checkpoint or when you can't confidently attribute** (a wrong guess pollutes
|
|
330
549
|
the signal). Use the **exact** name from this roster so the data aggregates:
|
|
331
550
|
|
|
@@ -337,35 +556,226 @@ order:
|
|
|
337
556
|
`cli-e2e`, `bash-hooks`)
|
|
338
557
|
|
|
339
558
|
Per-step Reviewer REJECTs also emit `review_rejected` as usual, with the extra
|
|
340
|
-
`--step=<N>` flag (the `iteration` count stays task-global, as today
|
|
559
|
+
`--step=<N>` flag (the `iteration` count stays task-global, as today; an
|
|
560
|
+
all-at-once single-group pass — pre-commit review ON's pre-gate review —
|
|
561
|
+
omits `--step`, per the events schema).
|
|
341
562
|
|
|
342
563
|
5. **`progress.md` step log** — keep the sub-state explicit so `/resume` can re-enter
|
|
343
564
|
mid-loop. Under a `## Step log` heading, one line per resolved step; the **open**
|
|
344
565
|
step's line is transient — update it in place as the loop progresses
|
|
345
566
|
(`fix-loop iteration K — in progress` while implementing/reviewing,
|
|
346
|
-
`awaiting
|
|
347
|
-
|
|
567
|
+
`awaiting ledger retry (step N/M, retry 1/1)` while the fresh Reviewer redoes a red
|
|
568
|
+
ledger, `awaiting human checkpoint (step N/M)` while waiting on the human), then
|
|
569
|
+
replace it with the resolved outcome:
|
|
348
570
|
|
|
349
571
|
```markdown
|
|
350
572
|
Mode: step-by-step
|
|
351
573
|
|
|
352
574
|
## Step log
|
|
353
575
|
|
|
354
|
-
- step 1/6 — review ×1 → checkpoint: OK
|
|
355
|
-
- step 2/6 — review ×3 (2 rejects: missing error path; flaky spec) → checkpoint: changes → review ×1 → checkpoint: OK
|
|
356
|
-
- step 3/6 —
|
|
576
|
+
- step 1/6 (anchor a1b2c3d) — review ×1 → checkpoint: OK
|
|
577
|
+
- step 2/6 (anchor e4f5a6b) — review ×3 (2 rejects: missing error path; flaky spec) → checkpoint: changes → review ×1 → checkpoint: OK
|
|
578
|
+
- step 3/6 (anchor c7d8e9f) — review ×2 (ledger retry ×1) — ledger: criteria 3/3 (basis: requirements), gates 2/2 (basis: config) → checkpoint: OK
|
|
579
|
+
- step 4/6 (anchor f1a2b3c) — awaiting human checkpoint (step 4/6)
|
|
357
580
|
```
|
|
358
581
|
|
|
359
|
-
Those
|
|
582
|
+
Those transient sub-state strings are exactly what a later `/resume` re-enters
|
|
360
583
|
on: the awaiting line re-presents the pending checkpoint, the fix-loop line
|
|
361
|
-
re-enters the implement→review loop at that iteration
|
|
584
|
+
re-enters the implement→review loop at that iteration, and the ledger-retry line
|
|
585
|
+
says the single retry is already spent. The `(anchor <oid>)`
|
|
586
|
+
field is the OID step 1 recorded — the checkpoint contract and the per-step
|
|
587
|
+
Reviewer diff against it.
|
|
362
588
|
|
|
363
|
-
After the **last
|
|
589
|
+
After the **last group**, rejoin the normal flow unchanged (L1 step 8): flip to
|
|
364
590
|
`in-review`, open the PR, and run the **full-pass Reviewer** over everything against
|
|
365
|
-
the spec. The full-pass may reject anything, **including human-OK'd
|
|
591
|
+
the spec. The full-pass may reject anything, **including human-OK'd steps** — a
|
|
366
592
|
checkpoint OK means "right direction and it runs", not a review waiver; the full-pass
|
|
367
593
|
wins, and the human still holds the merge gate to disagree.
|
|
368
594
|
|
|
595
|
+
**Every full-pass APPROVE is ledger-validated too** — the step-8 review in either mode,
|
|
596
|
+
pre-commit review ON's single-group pre-gate pass, and a re-APPROVE after the merge gate
|
|
597
|
+
or PR comments sent the PR back to review. The Reviewer writes `full-pass.json` (no step
|
|
598
|
+
number → the union of every group); you run
|
|
599
|
+
`lemony review-ledger validate --task-id=<id> --anchor=<merge-base> --full-pass` —
|
|
600
|
+
the merge-base of the freshly fetched base and the PR head, the Diff-fingerprint's
|
|
601
|
+
own OID (L1 step 8; pre-commit review ON's pre-gate pass instead anchors at the OID
|
|
602
|
+
you recorded when its single group started) — and route a red exactly as
|
|
603
|
+
in step 2 above: one fresh Reviewer retry with the lines verbatim, a second red or any
|
|
604
|
+
spec-side problem to the human, and never an APPROVE relayed on a red ledger. Outside
|
|
605
|
+
the step loop the transient line is `awaiting ledger retry (full pass, retry 1/1)` in
|
|
606
|
+
`progress.md` (no step counter), and "the human" is the gate the pass feeds: the
|
|
607
|
+
pre-commit-ON checkpoint when there is one, otherwise the merge-gate presentation,
|
|
608
|
+
with the verb's lines as the content.
|
|
609
|
+
|
|
610
|
+
## Checkpoint contract (how a human gate presents work)
|
|
611
|
+
|
|
612
|
+
Every human checkpoint — per-group in step-by-step, and the single all-at-once gate
|
|
613
|
+
when pre-commit review is ON — presents the same way. "Present the step" is a
|
|
614
|
+
contract, not a vibe:
|
|
615
|
+
|
|
616
|
+
1. **Anchored scope.** The group's **anchor** — the branch head OID recorded when
|
|
617
|
+
the group started (= the end of the previous approved group; the head at
|
|
618
|
+
implementation start when no group has yet been approved — group 1, or an
|
|
619
|
+
all-at-once run; an all-at-once run that began as a mid-task downgrade anchors
|
|
620
|
+
at the head after the last OK'd group) — bounds exactly what is
|
|
621
|
+
under review. Present the commit list + touched-files table, then the **exact
|
|
622
|
+
group diff**, always excluding task state:
|
|
623
|
+
- OFF: `git log --oneline <anchor>..HEAD` +
|
|
624
|
+
`git diff --stat <anchor>..HEAD -- ':(exclude).claude/state'`,
|
|
625
|
+
then `git diff <anchor>..HEAD -- ':(exclude).claude/state'`;
|
|
626
|
+
- ON (nothing committed):
|
|
627
|
+
`git diff --stat <anchor> -- ':(exclude).claude/state'`, then
|
|
628
|
+
`git diff <anchor> -- ':(exclude).claude/state'` — staged new files are in
|
|
629
|
+
the index, so they appear.
|
|
630
|
+
2. **The inline rendered diff is the universal floor.** Probe the environment: when
|
|
631
|
+
a difftool is configured (or an extension like GitLens is present), **offer**
|
|
632
|
+
the richer side-by-side view — an ephemeral config works without touching the
|
|
633
|
+
repo's (`git -c diff.tool=<tool> difftool <range>`). Never assume an extension
|
|
634
|
+
exists. In ON the Source Control panel already shows the work natively — it IS
|
|
635
|
+
uncommitted.
|
|
636
|
+
3. **What was built and how to run it** — from the Implementer's verification line,
|
|
637
|
+
as today. Checkpoint narration is the product, never overhead (§Turn economy).
|
|
638
|
+
4. **The human delta — always detected, always revalidated.** Whatever the answer
|
|
639
|
+
(`ok` or `changes: …`), first detect the human's own edits — in ON, **two
|
|
640
|
+
checks with distinct routes** (every agent save-point is staged at
|
|
641
|
+
presentation time, so the staged/unstaged seam is the auto-detect):
|
|
642
|
+
- **Work delta**: the unstaged section outside task state —
|
|
643
|
+
`git diff -- ':(exclude).claude/state'` **plus untracked files outside
|
|
644
|
+
`.claude/state`** (from `git status --porcelain`; `git diff` never shows a
|
|
645
|
+
file the human created). Your own disk-only `progress.md` edits sit
|
|
646
|
+
unstaged under `.claude/state` by design — agent noise, never a human
|
|
647
|
+
delta.
|
|
648
|
+
- **Spec check**: unstaged edits **or untracked files** under the spec —
|
|
649
|
+
`git diff -- .claude/state/tasks/<id>/spec/` plus the untracked (`??`)
|
|
650
|
+
porcelain entries
|
|
651
|
+
under that path (a human-created spec file is untracked and invisible to
|
|
652
|
+
the diff). A hit is **not** a work
|
|
653
|
+
delta: it changes the approved spec, so route it as a **discovery**
|
|
654
|
+
(§Discovery mediation) before the checkpoint resolves. The Spec Author —
|
|
655
|
+
the spec's owner — applies the resolution to the file (restoring it if the
|
|
656
|
+
human's interpretation is rejected), then **re-present this checkpoint**;
|
|
657
|
+
only after that may any commit run, so `git add -A` lands the resolved
|
|
658
|
+
spec, never an unconfirmed unstaged edit. **Mediated changes are staged on
|
|
659
|
+
arrival and never re-raise**: whenever a discovery resolution is applied
|
|
660
|
+
mid-task — the Spec Author's spec update, an Architect artifact — stage it
|
|
661
|
+
immediately (`git add` the touched paths). Resolved artifacts join the
|
|
662
|
+
agent-staged floor, so this check fires only on **unmediated** edits and
|
|
663
|
+
the re-presented checkpoint is clean; an unstaged edit that matches the
|
|
664
|
+
recorded `**Resolution**` in `discoveries.md` is confirmed content — stage
|
|
665
|
+
it, don't re-raise.
|
|
666
|
+
|
|
667
|
+
The **spec check runs in both knob states** — in OFF, run it **before** step
|
|
668
|
+
3's `awaiting` state commit, which would otherwise silently commit a
|
|
669
|
+
pre-existing spec edit before detection. OFF's work-delta detection is
|
|
670
|
+
simply any uncommitted working-tree change.
|
|
671
|
+
The **work delta** then runs the pipeline: present it, confirm it is
|
|
672
|
+
intended, and **re-run the suite with the delta applied** — the delta never
|
|
673
|
+
inherits the group's green. **A red revalidation blocks the OK**: never run
|
|
674
|
+
the commit composite on a red suite — re-present the failure and route it as
|
|
675
|
+
`changes` (the human decides: fix it themselves, drop the delta, or hand it
|
|
676
|
+
to the Implementer). On a green revalidation the delta rides with the group:
|
|
677
|
+
on OK it lands with
|
|
678
|
+
the group's work (ON: staged into the group's commit; OFF: committed as its
|
|
679
|
+
own commit — `step(<id>): step <N> human delta` — before the next group); on
|
|
680
|
+
`changes` it stays in the worktree for
|
|
681
|
+
the fresh Implementer (ON: staged first as the safe starting point —
|
|
682
|
+
§Pre-commit review ON).
|
|
683
|
+
|
|
684
|
+
## Pre-commit review ON (zero commits + staging save-points)
|
|
685
|
+
|
|
686
|
+
The knob's ON state (chosen at the approval gate — §Implementation mode) moves
|
|
687
|
+
**when the branch first receives commits**; review, fix-loop routing, and the merge
|
|
688
|
+
gate are unchanged. v1 is deliberately an experiment — the watch-fors below are its
|
|
689
|
+
pre-registered exit signals.
|
|
690
|
+
|
|
691
|
+
- **Zero commits; staging save-points.** The Implementer never commits: it stages
|
|
692
|
+
after every green task (`git add -A`) and **never stages mid-experiment** — the
|
|
693
|
+
staging protocol in `implementer.md`, which the spawn prompt must point at
|
|
694
|
+
explicitly (it inverts the tdd add-early reflex, and a fresh Implementer only
|
|
695
|
+
knows what the contract and the branch tell it). A failed experiment rolls back
|
|
696
|
+
with `git restore . && git clean -fd`: the worktree returns to the last green
|
|
697
|
+
save-point; staged content survives verbatim, including new files.
|
|
698
|
+
- **Everything is deferred** until the human OK: no code commits and **no state
|
|
699
|
+
commits** either — the checkpoint's `awaiting` state commit + push do not run;
|
|
700
|
+
`progress.md` updates on disk only. The documented price, accepted at the
|
|
701
|
+
grill: **no multi-machine durability** during implementation and during the
|
|
702
|
+
checkpoint wait (the flow's longest, where a session likeliest dies) — a cold
|
|
703
|
+
`/resume` from another machine sees nothing pending. (One exception: a
|
|
704
|
+
discovery pause **is** visible cross-machine — its labels and issue comment
|
|
705
|
+
surface — but the full `discoveries.md` entry stays machine-local until an OK
|
|
706
|
+
lands it.) Same-machine resume reads the worktree as usual.
|
|
707
|
+
- **The gate** runs at end of group (step-by-step) or end of everything
|
|
708
|
+
(all-at-once — after the Implementer signals done and **before** the
|
|
709
|
+
`in-review` flip: the PR never opens on uncommitted work). In all-at-once the
|
|
710
|
+
whole implementation is the **single group** and the per-group machinery
|
|
711
|
+
applies to it: the mid-implementation Reviewer runs over the anchored diff
|
|
712
|
+
**before** the gate — nothing reaches the human unreviewed, in either mode —
|
|
713
|
+
and the `changes` fix-loop carries the same 3-reject cap into an anticipated
|
|
714
|
+
checkpoint (whose `step_completed` clause and `step N/M` strings apply only
|
|
715
|
+
in step-by-step — in all-at-once the no-emit rule below wins and the awaiting
|
|
716
|
+
line drops the step counter). Its APPROVE is ledger-validated with `--full-pass`
|
|
717
|
+
and `--anchor=<oid>` — the anchor you recorded when its single group started
|
|
718
|
+
(§Step-by-step implementation, after the last group), never relayed on a red.
|
|
719
|
+
Hand that pre-gate Reviewer the anchor, the ON
|
|
720
|
+
flag, and "the whole `tasks.md` as the single group" in place of a group id.
|
|
721
|
+
All agent work is
|
|
722
|
+
staged at presentation time; present per §Checkpoint contract. The human
|
|
723
|
+
inspects the work natively in the Source Control panel and edits/discards
|
|
724
|
+
directly in the worktree; answers are `ok` / `changes: …` (step-by-step keeps
|
|
725
|
+
its third answer, OK-and-downgrade).
|
|
726
|
+
- **OK ⇒ commit + push, after revalidating.** Re-run the suite over the final
|
|
727
|
+
worktree (human delta staged in) — the gate's green is fresh, never inherited.
|
|
728
|
+
Then the deferral ends as **one composite turn**, splitting the mixed index by
|
|
729
|
+
pathspec (`git add -A` staged code and task state together; a bare
|
|
730
|
+
`git commit` would swallow both into one commit):
|
|
731
|
+
|
|
732
|
+
```bash
|
|
733
|
+
git add -A && \
|
|
734
|
+
{ git diff --cached --quiet -- . ':(exclude).claude/state' || \
|
|
735
|
+
git commit -m "<code-msg>" -- . ':(exclude).claude/state'; } && \
|
|
736
|
+
{ git diff --cached --quiet -- .claude/state || \
|
|
737
|
+
git commit -m "<state-msg>" -- .claude/state; }; \
|
|
738
|
+
git push # best-effort — a failure warns, never blocks
|
|
739
|
+
```
|
|
740
|
+
|
|
741
|
+
Each commit is **guarded on its half of the index holding changes**, so a
|
|
742
|
+
degenerate group no-ops that half instead of short-circuiting the other: a
|
|
743
|
+
state-only OK (an audit group whose artifact is progress notes, or a human who
|
|
744
|
+
discarded the work in the worktree and answered `ok`) still lands its state
|
|
745
|
+
commit — an unguarded `&&` chain would silently drop it while the push still
|
|
746
|
+
ran, leaving the resolution nowhere on the branch.
|
|
747
|
+
|
|
748
|
+
Step-by-step: `<code-msg>` = `step(<id>): step <N>`, `<state-msg>` =
|
|
749
|
+
`step(<id>): step <N> checkpoint OK`, and the `step_completed` emit rides the
|
|
750
|
+
same turn (step 4). All-at-once (a downgraded remainder resolves as
|
|
751
|
+
all-at-once here too): `<code-msg>` = `task(<id>): implementation`,
|
|
752
|
+
`<state-msg>` = `task(<id>): pre-commit gate OK`, and **nothing is emitted** —
|
|
753
|
+
`step_completed` is step-by-step-only (its `--step` has no meaning here, and
|
|
754
|
+
closeout's mode recovery reads any `step_completed` as proof of step-by-step);
|
|
755
|
+
the gate resolution reaches telemetry through `task_done` as usual. One commit
|
|
756
|
+
per group in v1; all-at-once commits the whole implementation as its single
|
|
757
|
+
group.
|
|
758
|
+
|
|
759
|
+
- **Changes ⇒ the Implementer iterates over the worktree.** Same routing as any
|
|
760
|
+
rejection (fresh context, ≤3 rejects per step): it first **stages the
|
|
761
|
+
revalidated human delta** (its suite result rides in the feedback) as its
|
|
762
|
+
safe starting point, then continues
|
|
763
|
+
under the staging protocol. Nothing is committed; the deferral continues.
|
|
764
|
+
- **Watch-fors — pre-registered failure signatures.** v1 tests in real use whether
|
|
765
|
+
the three roles of tdd commits (fresh-Implementer memory via the branch,
|
|
766
|
+
granular save-points, 1:1 per-task history) are missed. If one fires, don't
|
|
767
|
+
improvise: **surface it at the checkpoint and record it in `progress.md`** so
|
|
768
|
+
the human can decide the swap to the pre-designed fallback — _deferred push_
|
|
769
|
+
(local tdd commits exactly as today but unpublished; the OK becomes the push:
|
|
770
|
+
commit = internal memory, push = publication). The signatures:
|
|
771
|
+
1. **Fix-loop memory** — iteration 2 redoes or overwrites iteration 1's work.
|
|
772
|
+
2. **Save-point residual** — a mid-experiment `git add` silently destroyed the
|
|
773
|
+
save-point (`git status` looks identical; recovery yields nameless blobs),
|
|
774
|
+
or single-level undo proves insufficient (e.g. selectively reverting task
|
|
775
|
+
N−2).
|
|
776
|
+
3. **PR narrative** — one commit per group cannot reconstruct the per-task
|
|
777
|
+
`step(<id>): step <N>` story.
|
|
778
|
+
|
|
369
779
|
## Discovery mediation
|
|
370
780
|
|
|
371
781
|
Any sub-agent (Spec Author, Implementer, Reviewer, Architect) may stop mid-task and
|
|
@@ -380,103 +790,15 @@ When you get that summary, run the **`resolve-discovery`** skill. It pauses the
|
|
|
380
790
|
`paused_from`), has you arbitrate the question with the human, routes the artifact
|
|
381
791
|
update to its owner (the agent that created it), records the resolution in
|
|
382
792
|
`discoveries.md`, clears the discovery flag, restores the status, and re-invokes the
|
|
383
|
-
paused sub-agent with the decision.
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
evaporate and don't context-switch to it: **offer to spin it off**. The discriminator is
|
|
393
|
-
_independence_ — is this something the current task touches anyway?
|
|
394
|
-
|
|
395
|
-
This is distinct from three neighbours:
|
|
396
|
-
|
|
397
|
-
- **Just fix it** — if the defect is **in scope for the current task** (something this
|
|
398
|
-
change already touches), fix it in the current PR. No offer, no stub — spinning off
|
|
399
|
-
in-scope trivia only pollutes the backlog.
|
|
400
|
-
- **T3 SCOPE_DRIFT** (discovery) — when completing **the current task** _forces_ you to
|
|
401
|
-
touch out-of-scope work (the task can't finish without it). That pauses via
|
|
402
|
-
`resolve-discovery`. `/spinoff` is the opposite: the current task doesn't need the
|
|
403
|
-
defect touched, so it never pauses and keeps going.
|
|
404
|
-
- **`/define`** — a feature _idea_, not a defect. Route those to DEFINE, not `/spinoff`.
|
|
405
|
-
|
|
406
|
-
Calibration — **lean toward offering** so nothing slips, but keep it
|
|
407
|
-
frictionless and noise-free:
|
|
408
|
-
|
|
409
|
-
- Offer only when you'd bet it's a **genuine, independent defect worth a tracked issue**
|
|
410
|
-
— not for every stray observation, and not for anything you can fix in place. When in
|
|
411
|
-
doubt _whether to track a real independent defect_, lean toward offering; when in doubt
|
|
412
|
-
_whether it's even a real, independent bug_, stay quiet.
|
|
413
|
-
- The offer is a **single line**, in the human's language: _"This looks like an
|
|
414
|
-
independent bug — want me to `/spinoff` it and keep going?"_ One tap to dismiss; if the
|
|
415
|
-
human says no, drop it and continue without comment.
|
|
416
|
-
- **Never re-offer the same finding twice in a session.** "Same finding" = the same
|
|
417
|
-
underlying defect even if re-described; when unsure, treat a clearly new symptom as new.
|
|
418
|
-
This rule is the **only** human-side dedup (the capture verb is non-idempotent by
|
|
419
|
-
design — each run opens a fresh stub), so honor it.
|
|
420
|
-
- The offer **never pauses** the current task and never blocks on a reply — if the human
|
|
421
|
-
ignores it and keeps working, so do you.
|
|
422
|
-
|
|
423
|
-
On **accept**, capture it exactly as the `/spinoff` command does — the `spinoff` CLI
|
|
424
|
-
verb via the launcher, with the **current task's id** as the parent (recover it the same
|
|
425
|
-
way `/spinoff` does — from the `harness/<id>-…` branch or active task state; omit
|
|
426
|
-
`--parent` if there is no active task):
|
|
427
|
-
|
|
428
|
-
```bash
|
|
429
|
-
.claude/hooks/lib/lemony.sh spinoff \
|
|
430
|
-
--title="<one-line symptom>" \
|
|
431
|
-
--body="<where it was seen; a code pointer if you have one>" \
|
|
432
|
-
--parent=<current task id> \
|
|
433
|
-
--severity=<low|medium|high|critical>
|
|
434
|
-
```
|
|
435
|
-
|
|
436
|
-
Stub creation is **fail-loud** (a non-zero exit means it did not open — surface it, don't
|
|
437
|
-
pretend it was captured); the telemetry emit is **best-effort** (a `Warning:` means only
|
|
438
|
-
the event failed, the stub stands). Relay the verb's own `Captured #<id>…` line (it
|
|
439
|
-
carries the parent link) and **return to the current task**. The stub waits in the backlog as `harness:status:pending`
|
|
440
|
-
for a later pickup. The human can also trigger this directly with the `/spinoff` command;
|
|
441
|
-
the offer is the safety net for when they don't remember it mid-flow.
|
|
442
|
-
|
|
443
|
-
### From a sub-agent (the side-finding channel)
|
|
444
|
-
|
|
445
|
-
The same offer applies when the source is **not the human but a sub-agent's return
|
|
446
|
-
summary**. A sub-agent runs in fresh context and cannot interrupt you, so when it spots a
|
|
447
|
-
defect that is **independent of its task** (the task finished fine without touching it) it
|
|
448
|
-
**notes it instead of pausing** — that is the `note-side-finding` skill, the non-pausing
|
|
449
|
-
sibling of `raise-discovery`. It appends a `## Side-findings` block to its summary, one
|
|
450
|
-
bullet per finding (`symptom` / `location` / optional `severity`), and keeps working. (A
|
|
451
|
-
**blocking** defect is the opposite case — the sub-agent raises a T1–T6 discovery and
|
|
452
|
-
stops; you handle that with `resolve-discovery`, above.)
|
|
453
|
-
|
|
454
|
-
When you **read back a sub-agent's summary**, scan for a `## Side-findings` block. For each
|
|
455
|
-
bullet, make the **same single-line `/spinoff` offer** as for a human-mentioned defect —
|
|
456
|
-
pre-filled from the bullet (`--title` ← symptom, `--body` ← location, `--severity` ← the
|
|
457
|
-
read if given), the active task as `--parent`. Same calibration applies verbatim: lean
|
|
458
|
-
toward offering, one-tap dismissal, **never re-offer the same finding twice** (a
|
|
459
|
-
sub-agent's finding and a later human mention of the same defect are the _same_ finding),
|
|
460
|
-
and it **never pauses** the task. A side-finding is a candidate for the offer, not an
|
|
461
|
-
auto-capture — you still make the call and the human still decides.
|
|
462
|
-
|
|
463
|
-
A bullet tagged **`kind: drift`** is `docs/architecture.md` map staleness,
|
|
464
|
-
not a code defect: add **`--kind=architecture-drift`** to the `/spinoff` so the stub carries
|
|
465
|
-
the `harness:architecture-drift` routing label and a later pickup resolves it via the
|
|
466
|
-
Architect's `update-architecture` (a targeted map-fix), not a code change. **Fallback:** if
|
|
467
|
-
`update-architecture` is not installed (the project keeps no `architecture.md`), drop the
|
|
468
|
-
`--kind` and capture it as a generic stub — never let the offer fail because the routing
|
|
469
|
-
target is absent.
|
|
470
|
-
|
|
471
|
-
Two things you own because the sub-agent can't: **(1) cross-round dedup.** A sub-agent
|
|
472
|
-
re-invoked with fresh context (e.g. a Reviewer you rejected and re-ran) has **no memory of
|
|
473
|
-
what it side-noted before** and will re-emit the same `## Side-findings` block every round.
|
|
474
|
-
You hold the continuous context, so dedup is yours: an identical or re-described bullet
|
|
475
|
-
from a later round is the _same_ finding — don't re-offer it. **(2) gate ordering.** When
|
|
476
|
-
the read-back lands at a gate (a Reviewer returns right before the merge gate), make the
|
|
477
|
-
side-finding offer **after** the gate prompt, never before — the gate decision is primary;
|
|
478
|
-
the offer trails it as a secondary, dismissable line so it never splits attention at the
|
|
479
|
-
high-stakes moment.
|
|
793
|
+
paused sub-agent with the decision. A discovery can also be raised by **you** (e.g. a
|
|
794
|
+
checkpoint "changes" request that contradicts the spec): the same skill runs, with
|
|
795
|
+
nothing to re-invoke — the resolution re-enters the loop where it paused. You are the
|
|
796
|
+
only one who talks to the human and
|
|
797
|
+
moves labels — never let a sub-agent self-resolve. An **oversize** discovery — the Spec
|
|
798
|
+
Author or the Implementer finds the scope hides ≥2 independently mergeable units the PRD
|
|
799
|
+
didn't reveal — is a T2 raised and mediated exactly like this; when the human answers
|
|
800
|
+
"partition", read `.claude/agents/partition.md` for the parent/part mechanics (the
|
|
801
|
+
current task keeps the first unit).
|
|
480
802
|
|
|
481
803
|
## Architect (on-demand)
|
|
482
804
|
|
|
@@ -505,184 +827,98 @@ Dispatch it (fresh context, Task tool) when:
|
|
|
505
827
|
- **Orientation is needed** — before a decision or spec in a large or unfamiliar
|
|
506
828
|
codebase, dispatch it with `code-explorer` for a read-only map.
|
|
507
829
|
- **Closeout — the Architect's reliable activation checkpoint** — the `task-closeout`
|
|
508
|
-
skill drives durable
|
|
509
|
-
discretionary triggers otherwise lose to "unblock the paused
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
Give it the context (the discovery entry + its resolution, the change, or the request)
|
|
518
|
-
and read back its summary. The Architect is **not a gate** — it produces an artifact and
|
|
830
|
+
skill drives three durable-capture activations at the end of every task, in cold
|
|
831
|
+
blood, where the discretionary triggers otherwise lose to "unblock the paused
|
|
832
|
+
sub-agent". The full list and its asymmetry live in §Closeout.
|
|
833
|
+
|
|
834
|
+
Give it the context — the branch when no PR exists yet, the `discoveries.md` entry
|
|
835
|
+
by path with the decision (**in full** pre-record — `resolve-discovery` step 3 —
|
|
836
|
+
its gist once recorded), the change or the human's request as a synthesized delta
|
|
837
|
+
(§Sub-agent invocation) — and read back its summary. The Architect is **not a gate** — it produces an artifact and
|
|
519
838
|
reports; it never moves the status machine. If it reports that the trigger didn't
|
|
520
839
|
warrant the artifact (an ADR that fails the three tests, a change that isn't
|
|
521
840
|
architecturally significant, a "playbook" change that's really project-specific), record
|
|
522
841
|
that and move on — no artifact is forced.
|
|
523
842
|
|
|
524
|
-
## UI design (DEFINE + REVIEW)
|
|
525
|
-
|
|
526
|
-
UI design threads into an L1 task that touches UI — never a linear step. **The interactive
|
|
527
|
-
design interview is yours**: a sub-agent can't talk to the human, so at DEFINE **you** run
|
|
528
|
-
the `grill-ui` skill on your human-facing surface and author the `ui-handoff.md` contract.
|
|
529
|
-
The **UI Designer** sub-agent — always installed, invoked **on-demand**, your only invoker —
|
|
530
|
-
is your design **critic and QA**: at DEFINE it reviews the handoff you just authored (before
|
|
531
|
-
the Spec Author runs); at REVIEW it runs a mechanical pre-pass (the deterministic
|
|
532
|
-
`design-tokens` gates + the project's a11y tooling) then the `design-critique` and
|
|
533
|
-
`a11y-audit` judgment lenses, and returns one design verdict. You own the human dialogue, the
|
|
534
|
-
`ui-handoff.md` artifact, and the labels; it critiques and reports.
|
|
535
|
-
|
|
536
|
-
A third, on-demand affordance sits outside those two moments: **design-tool token sync**.
|
|
537
|
-
When the human runs `/sync-design-tokens` (or accepts the DEFINE offer when a drift check
|
|
538
|
-
shows an export is pending), dispatch the UI Designer to run its `design-tool-sync` skill.
|
|
539
|
-
It is human-reviewed both ways and tokens-only; the design tool is a projection of
|
|
540
|
-
`docs/design-tokens.json`, never a peer source of truth.
|
|
541
|
-
|
|
542
|
-
### Activation gate
|
|
543
|
-
|
|
544
|
-
After the grill produces the PRD and the task issue exists, judge — **your own LLM
|
|
545
|
-
call**, no runtime keyword scorer — whether this task needs design, as **two parts both
|
|
546
|
-
true**:
|
|
547
|
-
|
|
548
|
-
1. **The repo has a frontend** — there is UI to design (a SPA/app surface, components,
|
|
549
|
-
styles), not a pure library / CLI / backend.
|
|
550
|
-
2. **This task touches UI** — the change adds or alters something a user sees or
|
|
551
|
-
interacts with.
|
|
552
|
-
|
|
553
|
-
**Bias to include** on a borderline call: a wasted handoff stub is cheaper than UI
|
|
554
|
-
shipped with no design pass. When both hold, the task needs design.
|
|
555
|
-
|
|
556
|
-
### Design-stop offer
|
|
557
|
-
|
|
558
|
-
When the gate fires, **put `harness:needs-design`** on the issue and offer the human,
|
|
559
|
-
inline, in one line — three choices:
|
|
560
|
-
|
|
561
|
-
> This task touches UI. (1) **Continue** — define the design now, as part of the spec;
|
|
562
|
-
> (2) **Stop for handoff** — park here so a designer picks it up later; (3) **No UI after
|
|
563
|
-
> all** — skip design.
|
|
564
|
-
|
|
565
|
-
- **Continue** → **run `grill-ui` yourself** — the interactive design interview on your
|
|
566
|
-
human-facing surface — authoring `ui-handoff.md` under `tasks/<id>/spec/`. Then dispatch
|
|
567
|
-
the **UI Designer** (fresh context, Task tool) with the `<id>` and branch to **critique**
|
|
568
|
-
the handoff, and resolve its findings before the Spec Author runs. The issue stays at
|
|
569
|
-
`harness:status:spec-in-progress` — design is part of completing the spec, not a new
|
|
570
|
-
lifecycle state.
|
|
571
|
-
- **Stop for handoff** → record the sub-state `awaiting design definition` in
|
|
572
|
-
`progress.md`, commit and push the task state to the branch, and stop. The task waits
|
|
573
|
-
at `spec-in-progress` (+ `harness:needs-design`) for a `/resume` (below).
|
|
574
|
-
- **No UI after all** → **remove `harness:needs-design`** and proceed with the ordinary
|
|
575
|
-
spec flow — the gate was a false positive, which bias-to-include accepts.
|
|
576
|
-
|
|
577
|
-
### Persisting personas (offer)
|
|
578
|
-
|
|
579
|
-
`docs/personas.md` is **client-owned** — the harness consumes it, never imposes it. When your
|
|
580
|
-
`grill-ui` interview captured personas **inline** because `docs/personas.md` was **absent** (§1
|
|
581
|
-
of the handoff), make the offer — a human-facing choice, so it is yours:
|
|
582
|
-
|
|
583
|
-
> The design defined these personas inline. Persist them to `docs/personas.md` so future UI
|
|
584
|
-
> tasks reuse them? (yes / no)
|
|
585
|
-
|
|
586
|
-
- **Yes** → write a minimal `docs/personas.md` from the personas already in the handoff's §1
|
|
587
|
-
— the client's own words, not an invented cast. Then continue toward spec-ready.
|
|
588
|
-
- **No** → write nothing; the inline personas live on in the handoff for this task. The next
|
|
589
|
-
UI task simply asks again.
|
|
590
|
-
|
|
591
|
-
Only offer when the file was **absent and personas were captured inline** — never when
|
|
592
|
-
`docs/personas.md` already exists (it was consumed, nothing to persist) and never unasked.
|
|
593
|
-
This is opt-in surfacing of the client's own answers, not the harness authoring a persona set.
|
|
594
|
-
|
|
595
|
-
### Design-tokens & design-tool on-ramp (offer)
|
|
596
|
-
|
|
597
|
-
`docs/design-tokens.json` and a design-tool connection are **client-owned inputs** — consumed if
|
|
598
|
-
present, never imposed. A repo adopting the harness fresh has neither, and silence there is a dead
|
|
599
|
-
end. So when your `grill-ui` interview finds **either absent**, surface it as an opt-in offer (a
|
|
600
|
-
human-facing choice, so it is yours) rather than only an open question:
|
|
601
|
-
|
|
602
|
-
- **No `docs/design-tokens.json`** → offer to **scaffold** a starter token set derived from the
|
|
603
|
-
direction the interview just settled (the client's own colours/type/spacing, not a vendor
|
|
604
|
-
template), plus an opt-in follow-up to generate a sensible starter set for the aspects the
|
|
605
|
-
interview didn't cover. On **yes**, write the file and run `lemony design-tokens validate` before
|
|
606
|
-
closing; on **no**, capture it as an open question.
|
|
607
|
-
- **No `com.lemony.design-tool` binding** → offer to **connect a design tool** (write the binding +
|
|
608
|
-
first import via the UI Designer's `design-tool-sync` skill / `/sync-design-tokens`), or **stay
|
|
609
|
-
pure-code**. Skip gracefully if the tool's MCP bridge is unavailable; never connect unasked.
|
|
610
|
-
|
|
611
|
-
The mechanics live in the `grill-ui` skill; you run the offers on your human-facing surface. Only
|
|
612
|
-
offer when the input is **absent** — never re-offer a token file or binding that already exists.
|
|
613
|
-
|
|
614
|
-
### Label put/remove
|
|
615
|
-
|
|
616
|
-
`harness:needs-design` is an **orthogonal presence flag** (same family as
|
|
617
|
-
`harness:architecture-drift`), never a status:
|
|
618
|
-
|
|
619
|
-
- **Put** it as soon as the gate classifies the task as touching UI and design is not
|
|
620
|
-
yet complete.
|
|
621
|
-
- **Remove** it the moment `ui-handoff.md` is **complete** — at or before the flip to
|
|
622
|
-
`harness:status:spec-ready`. **Complete** = the handoff carries **this task's** design
|
|
623
|
-
decisions (its sections hold real content, not the verbatim placeholder template), the UI
|
|
624
|
-
Designer's critique **passed** (or you resolved its findings), and **no** open design fork
|
|
625
|
-
remains (an open fork means design is still open — keep the label and resolve it first).
|
|
626
|
-
Ensure the label is gone **before** flipping to `spec-ready`: a spec-ready task never
|
|
627
|
-
carries `harness:needs-design`.
|
|
628
|
-
|
|
629
|
-
### `awaiting design definition` sub-state + /resume re-entry
|
|
630
|
-
|
|
631
|
-
A task parked at "stop for handoff" sits at `harness:status:spec-in-progress` with
|
|
632
|
-
`progress.md` recording the sub-state `awaiting design definition`. It is the design
|
|
633
|
-
analogue of the step-by-step `awaiting human checkpoint` line — execution state, not a
|
|
634
|
-
label. `/resume <id>` re-enters there: check out the branch, read the captured context,
|
|
635
|
-
resume the `grill-ui` interview yourself to finish `ui-handoff.md`, dispatch the UI Designer
|
|
636
|
-
to critique it, then remove `harness:needs-design` and continue toward spec-ready. The resume
|
|
637
|
-
queue surfaces the parked design (`resume.md` lists `spec-in-progress` too).
|
|
638
|
-
|
|
639
|
-
### REVIEW — the design lens
|
|
640
|
-
|
|
641
|
-
When an implemented UI change reaches review (L1 step 8), invoke the **UI Designer** as
|
|
642
|
-
a **distinct lens** alongside the Reviewer (code). The **durable "this task touched UI"
|
|
643
|
-
signal is the existence of `tasks/<id>/spec/ui-handoff.md`** — `harness:needs-design` is
|
|
644
|
-
already gone by spec-ready, so it can't be the cue; the handoff artifact persists and
|
|
645
|
-
survives a cold `/resume`, so it is what to check. Either lens rejecting routes back to
|
|
646
|
-
the Implementer (rejection is transient — no dedicated label); both passing reaches the
|
|
647
|
-
single human merge gate (two inputs, one gate).
|
|
648
|
-
|
|
649
|
-
The UI Designer's lens mirrors the Reviewer's own shape — a **mechanical pre-pass** (the
|
|
650
|
-
deterministic `design-tokens validate` + `design-tokens contrast` gates, plus the
|
|
651
|
-
project's a11y tooling), then **judgment** (`design-critique` + `a11y-audit`), returning
|
|
652
|
-
**one design verdict** with findings grouped by source (tokens / accessibility / craft).
|
|
653
|
-
The Reviewer's code lens stays design-unaware; you still see exactly two review inputs.
|
|
654
|
-
|
|
655
|
-
**Deterministic vs judgment, by level.** The two deterministic gates are cheap, agent-free
|
|
656
|
-
facts, so they run **per-step** on UI-touching steps in step-by-step mode (a bad contrast
|
|
657
|
-
in step 2 must not ride to step 6 — see §Step-by-step implementation); the project's a11y
|
|
658
|
-
lint rides the per-step lint the same way. The **judgment lenses run full-pass only** —
|
|
659
|
-
design is holistic, and a mid-component critique is noise. There is no per-step design
|
|
660
|
-
agent and no new cap: a full-pass design rejection routes back like any other rejection.
|
|
661
|
-
(`design-tokens validate` / `contrast` also run in CI independently of review.)
|
|
662
|
-
|
|
663
|
-
### Closeout
|
|
664
|
-
|
|
665
|
-
`ui-handoff.md` lives in `tasks/<id>/spec/`, so closeout archives it with the rest of
|
|
666
|
-
the spec (`task-closeout` `git mv`s the whole `spec/` into `_archive/<id>/`) — no
|
|
667
|
-
special handling.
|
|
668
|
-
|
|
669
843
|
## Merge gate (`in-review → merged`)
|
|
670
844
|
|
|
671
845
|
When the Reviewer approves, **do not merge automatically.** Merging the PR is the one
|
|
672
|
-
action that touches the default branch
|
|
673
|
-
|
|
846
|
+
action that touches the default branch, so it stays a human decision. Surface it and
|
|
847
|
+
wait:
|
|
674
848
|
|
|
675
849
|
> Reviewed and approved — PR #<pr> is here: <url>.
|
|
676
|
-
> (1) merge it yourself, (2) I'll merge it (`gh pr merge`), or
|
|
850
|
+
> (1) merge it yourself, (2) I'll merge it once checks are green (`gh pr merge`), or
|
|
677
851
|
> (3) run `/review-pr` first for a curated inline pass.
|
|
678
852
|
|
|
679
|
-
|
|
680
|
-
|
|
853
|
+
A **stacked partition child** reaches this gate only once its PR base is the default
|
|
854
|
+
branch — check `baseRefName` first and retarget/merge-in per `.claude/agents/partition.md`;
|
|
855
|
+
never merge a part into a sibling's branch. The task stays at `harness:status:in-review`
|
|
856
|
+
while it waits — there is no "approved-awaiting-merge" rung between review and merge (the `closeout-pending` status is
|
|
681
857
|
**post**-merge, for a parked closeout record PR — not this gate). When the
|
|
682
858
|
human merges (in the GitHub UI, by CLI, or by authorizing you to run `gh pr merge`),
|
|
683
859
|
proceed to closeout. **GitHub is the source of truth for the merge, not this
|
|
684
860
|
conversation** — closeout confirms it via `gh pr view`.
|
|
685
861
|
|
|
862
|
+
### Executing an authorized merge — the checks precondition
|
|
863
|
+
|
|
864
|
+
"Merge it" is a **conditional authorization**: the agent merges _when it is safe to do
|
|
865
|
+
so_, not an order to execute immediately. The human's yes carries the implicit
|
|
866
|
+
assumption that CI is fine; this contract makes that assumption an explicit
|
|
867
|
+
precondition. Not green ⇒ you never merge on your own — you re-present this gate with
|
|
868
|
+
the real state. (The failure pattern this kills: nobody _decided_ to merge on red — an
|
|
869
|
+
authorization whose background assumption was false got executed without looking.)
|
|
870
|
+
|
|
871
|
+
So never run a bare `gh pr merge`. Execute the merge through the shared executor, which
|
|
872
|
+
verifies the check status the platform reports for the PR and waits — bounded — for
|
|
873
|
+
pending checks (`merge.checks_timeout_secs` in `harness.config.yml`, default ~10 min).
|
|
874
|
+
For a task PR, always pass `--approve-issue` with the task's issue number — that arms
|
|
875
|
+
the **stale-approve guard**: the executor reads the Reviewer's APPROVE record
|
|
876
|
+
(`Reviewed-tree` / `Diff-fingerprint`) from that issue and refuses to merge content
|
|
877
|
+
that no longer matches what the APPROVE reviewed (a clean update-branch stays valid;
|
|
878
|
+
any content change does not):
|
|
879
|
+
|
|
880
|
+
```bash
|
|
881
|
+
.claude/hooks/lib/merge-pr.sh <pr> --approve-issue <issue> --squash # merge-strategy flags pass through to `gh pr merge`
|
|
882
|
+
```
|
|
883
|
+
|
|
884
|
+
Act on its exit code:
|
|
885
|
+
|
|
886
|
+
- **0 — merged.** Proceed to closeout.
|
|
887
|
+
- **10 — checks red.** Never merge on red by omission. Re-present the gate with the
|
|
888
|
+
failure: "checks red: <check> fails — merge anyway / wait / want to look at it?".
|
|
889
|
+
Merging on red stays possible, but only as the human's informed decision — on that
|
|
890
|
+
explicit yes, re-run with `--force`.
|
|
891
|
+
- **30 — still pending after the bound.** The authorization never expires silently; it
|
|
892
|
+
expires by asking: "checks still running after <N> min — keep waiting, or leave it
|
|
893
|
+
and I'll tell you when they settle?". "Keep waiting" = re-run the executor (a fresh
|
|
894
|
+
bound, or a longer one via `--timeout-secs <secs>`).
|
|
895
|
+
- **40 — stale approve: the PR content changed after the review.** A push landed in
|
|
896
|
+
the merge-gate window (another dev, a re-invoked implementer) and the diff no longer
|
|
897
|
+
matches what the APPROVE reviewed. Never merge it on the old verdict: route the PR
|
|
898
|
+
back to the Reviewer — the same back-to-review route as the comments path below; a
|
|
899
|
+
fresh APPROVE re-records the hashes — then re-surface this gate. Merging anyway
|
|
900
|
+
stays possible, but only as the human's informed decision — on that explicit yes,
|
|
901
|
+
re-run with `--force` (which skips the checks precondition too: make sure the
|
|
902
|
+
informed yes covers both).
|
|
903
|
+
- **20 — no checks reported (after the grace window).** Never merge alone, whatever the
|
|
904
|
+
cause — repo with no CI, CI outage, path filters: don't try to auto-classify. Ask:
|
|
905
|
+
"I saw no checks available on this PR — merge?". If the human's answer is standing
|
|
906
|
+
("this repo has no CI — merge without checks"), record it **now** as
|
|
907
|
+
`merge.allow_no_checks: true` in `harness.config.yml` (the executor reads the working
|
|
908
|
+
tree, so no later merge — including this task's closeout — re-asks); the line lands
|
|
909
|
+
on the base with the closeout record PR's commit, never a direct push. For the merge
|
|
910
|
+
at hand, re-run with `--force`.
|
|
911
|
+
- **1 (or any other non-zero)** — not merged, and **not a verdict**: either the check
|
|
912
|
+
status could not be read (`gh`/auth/network failure — the precondition is
|
|
913
|
+
unverifiable, which is _not_ the same as "checks failed") or the merge attempt itself
|
|
914
|
+
was rejected (branch protection wants approval, a conflict, a bad ref). The
|
|
915
|
+
executor's stderr says which — surface that, verbatim, at the gate.
|
|
916
|
+
|
|
917
|
+
The precondition is platform-neutral by design — "the merge executor verifies the check
|
|
918
|
+
status the platform reports for the PR"; `gh` is the GitHub rendering of it. Branch
|
|
919
|
+
protection is the user's belt: the harness executes merges and cannot assume it is
|
|
920
|
+
configured, so the executor carries its own precondition either way.
|
|
921
|
+
|
|
686
922
|
### When the human leaves review comments instead of merging
|
|
687
923
|
|
|
688
924
|
The human may respond at this gate not by merging but by **leaving comments on the PR**.
|
|
@@ -697,7 +933,14 @@ of an `in-review` task surfaces the open PR's comments and routes here — see
|
|
|
697
933
|
route as a Reviewer rejection: re-invoke the Implementer sub-agent (fresh context)
|
|
698
934
|
with the feedback (transient, no dedicated label). Skip pure acknowledgements or
|
|
699
935
|
questions that need no code.
|
|
700
|
-
3. **
|
|
936
|
+
3. **Route the fixed PR to re-review.** The fix commits are new, unreviewed content —
|
|
937
|
+
the Reviewer's standing APPROVE no longer covers them (and the executor's
|
|
938
|
+
stale-approve guard would refuse the merge with exit 40 anyway). Invoke the
|
|
939
|
+
Reviewer on the updated PR (continued re-review of its own findings when the
|
|
940
|
+
comments echo them; fresh otherwise); an approving verdict posts a fresh APPROVE
|
|
941
|
+
with fresh hashes. A reject routes back to step 2 — never draft "done" replies on
|
|
942
|
+
work that hasn't re-passed review.
|
|
943
|
+
4. **After the re-APPROVE, draft — do not auto-post — the replies.** Compose a short
|
|
701
944
|
"done ✅" reply threading each addressed comment, then **offer to post them with one
|
|
702
945
|
confirmation**. Posting a reply inside someone's review thread is outward-facing and
|
|
703
946
|
notifies the reviewer, so it is a **HITL gate, never automatic**. On approval, post
|
|
@@ -706,7 +949,7 @@ of an `in-review` task surfaces the open PR's comments and routes here — see
|
|
|
706
949
|
identity it uses for issues, PRs, and merges; there is no separate bot identity). The
|
|
707
950
|
fix commit is visible on the PR regardless, so declining only skips the
|
|
708
951
|
acknowledgement, not the fix.
|
|
709
|
-
|
|
952
|
+
5. **Re-surface the merge gate** above.
|
|
710
953
|
|
|
711
954
|
## Closeout
|
|
712
955
|
|
|
@@ -715,26 +958,36 @@ GitHub — `gh pr view <pr> --json state,mergedAt` reports `MERGED`, regardless
|
|
|
715
958
|
was merged). Closeout **archives, it does not delete, and it records via a dedicated PR**:
|
|
716
959
|
it raises durable decisions to ADRs, `git mv`s the spec + `discoveries.md`
|
|
717
960
|
into `.claude/state/tasks/_archive/<id>/`, drops only `progress.md`, and lands the
|
|
718
|
-
`history.md` append + the archival on a `harness/closeout-<id>` PR merged
|
|
719
|
-
|
|
720
|
-
the
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
961
|
+
`history.md` append + the archival on a `harness/closeout-<id>` PR self-merged through
|
|
962
|
+
the same checks-precondition executor as the merge gate (`.claude/hooks/lib/merge-pr.sh`
|
|
963
|
+
— **the executor never self-merges on anything but green**; no standing human
|
|
964
|
+
authorization exists on this path, so every not-green outcome takes one of the skill's
|
|
965
|
+
exits — self-fix, park, or a human's informed decision — never an autonomous merge).
|
|
966
|
+
Nothing is pushed direct to the base — the
|
|
967
|
+
closeout record obeys the same branch isolation as every other change. The skill owns
|
|
968
|
+
the full mechanics, including what to do on red, absent, or still-pending checks. A
|
|
969
|
+
task that is a **part of a partition** (its body carries `Part <k> of #<parent>`) also,
|
|
970
|
+
at finalize, ticks its row in the parent's checklist and — on the last part — closes the
|
|
971
|
+
parent with a summary comment; the skill's finalize step owns it (mechanics in
|
|
972
|
+
`.claude/agents/partition.md`).
|
|
973
|
+
|
|
974
|
+
**Closeout splits into two phases** because the self-merge only happens on green. If the
|
|
975
|
+
closeout PR's checks pass within the bound (and branch protection asks nothing more),
|
|
976
|
+
closeout finalizes in one go. Otherwise — checks red (and not self-fixable), no checks
|
|
977
|
+
and no standing answer, timeout, or protection requiring human approval — the PR waits:
|
|
978
|
+
flip the issue to `harness:status:closeout-pending`, tell the human the record PR is
|
|
979
|
+
open and why it didn't merge, and stop. A later `/resume` of a `closeout-pending` task
|
|
980
|
+
finalizes once that PR is merged (see Dispatch → RESUME).
|
|
729
981
|
|
|
730
982
|
**Closeout is the Architect's reliable activation point**: before
|
|
731
983
|
archiving, the skill drives three durable-capture activations, **asymmetric by design** —
|
|
732
984
|
`write-adr` (HITL offer per resolved discovery), `update-architecture` (**automatic**
|
|
733
|
-
dispatch
|
|
985
|
+
dispatch when `docs/architecture.md` exists, handing the PR and the `spec/design.md`
|
|
986
|
+
path (the Architect reads the merged diff itself) — no pre-offer, the map
|
|
734
987
|
tracks reality and the edit is reviewed in the closeout PR), and `playbook-iterate` (HITL
|
|
735
988
|
offer once per task, for a reusable pattern no `T6` conflict already routed). Closeout
|
|
736
989
|
never drafts the artifact itself — it lights up the Architect, who owns the criteria
|
|
737
|
-
(§Architect
|
|
990
|
+
(§Architect). Each activation no-ops when its skill isn't installed.
|
|
738
991
|
|
|
739
992
|
Before any of this, enforce the discovery invariant: **no `discoveries.md` entry may lack
|
|
740
993
|
a resolved `**Resolution**`block, and no`harness:discovery:\*` label may remain.** An
|
|
@@ -767,29 +1020,33 @@ When the gate choice was step-by-step, also pass `--steps` (the count of
|
|
|
767
1020
|
## Sub-agent invocation
|
|
768
1021
|
|
|
769
1022
|
Each sub-agent runs with **fresh context** (a Task-tool invocation), not the hat's
|
|
770
|
-
accumulated conversation.
|
|
771
|
-
the
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
1023
|
+
accumulated conversation. Read back its returned summary; you own the human dialogue,
|
|
1024
|
+
the approval gate, and the label lifecycle. When a summary carries a
|
|
1025
|
+
`## Side-findings` block, read `.claude/agents/spinoff.md` and run its offer —
|
|
1026
|
+
after any gate prompt the read-back lands on, never before.
|
|
1027
|
+
|
|
1028
|
+
**Spawn prompts go by reference, never by quotation.** The prompt carries the issue
|
|
1029
|
+
link, the relevant task-state and spec **paths**, the skill to run — plus, in
|
|
1030
|
+
step-by-step mode, the group id and its header line — and a short **synthesized
|
|
1031
|
+
delta**: only what exists nowhere on disk (reject feedback being addressed, the
|
|
1032
|
+
one-line gist of a **recorded** discovery resolution — the full entry lives in
|
|
1033
|
+
`discoveries.md`, reference it; pre-record, `resolve-discovery` step 3 routes the
|
|
1034
|
+
owner with the decision **stated in full** — or a human instruction from this
|
|
1035
|
+
conversation). Never paste spec
|
|
1036
|
+
content (`requirements.md` / `design.md` / `tasks.md` text) into the prompt — the
|
|
1037
|
+
sub-agent reads those files itself from the paths, always fresher than a quotation.
|
|
1038
|
+
One exception is never trimmed: when the delta **is** the payload (a REJECT's
|
|
1039
|
+
findings), route the reviewer's full findings verbatim. Scope: this rule binds the
|
|
1040
|
+
task loop's role sub-agents; a user-invoked analysis flow (e.g. a PR-review
|
|
1041
|
+
command spawning a stateless analysis agent) follows its own skill's prompt shape.
|
|
1042
|
+
|
|
1043
|
+
## Turn economy
|
|
1044
|
+
|
|
1045
|
+
Latency is paid **per turn**, not per command: run mechanical sequences whose
|
|
1046
|
+
intermediate output needs no judgment as **one composite invocation**, and batch
|
|
1047
|
+
independent read-only lookups in **one parallel turn**. The step-by-step checkpoint
|
|
1048
|
+
is the canonical case (single chained commit+push; post-resolution, the resolved
|
|
1049
|
+
step line + state commit + emit ride together). Never compact across a judgment point — reading a
|
|
1050
|
+
summary, arbitrating a gate, classifying a discovery each deserve their own turn.
|
|
1051
|
+
Checkpoint **narration** is exempt from all dieting: the gate presentation to the
|
|
1052
|
+
human is the product, never overhead.
|