@lemoncode/lemony 0.5.0 → 0.6.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 +17 -16
- package/catalog/VERSION +1 -1
- package/catalog/agents/implementer.md +36 -1
- package/catalog/agents/orchestrator.md +206 -83
- package/catalog/agents/reviewer.md +8 -3
- package/catalog/agents/spinoff.md +3 -2
- package/catalog/agents/ui-design.md +3 -1
- package/catalog/agents/ui-designer.md +6 -3
- package/catalog/commands/pause.md +50 -5
- package/catalog/commands/resume.md +13 -2
- package/catalog/commands/spinoff.md +5 -3
- package/catalog/commands/sync-design-tokens.md +6 -2
- package/catalog/harness.config.schema.json +4 -0
- package/catalog/hooks/init.sh +80 -7
- package/catalog/hooks/lib/live-branch.sh +37 -0
- package/catalog/hooks/lib/merge-pr.sh +17 -6
- package/catalog/hooks/lib/playbook-scan.sh +4 -2
- package/catalog/hooks/session-close.sh +26 -7
- package/catalog/schemas/tier2-events-history.md +33 -2
- package/catalog/schemas/tier2-events.md +30 -22
- package/catalog/skills/build-ui/SKILL.md +5 -2
- package/catalog/skills/design-tool-sync/SKILL.md +48 -7
- package/catalog/skills/grill-ui/SKILL.md +4 -1
- package/catalog/skills/grill-ui/ui-handoff-format.md +2 -1
- package/catalog/skills/mutation-testing/SKILL.md +22 -8
- package/catalog/skills/prd-to-spec/SKILL.md +27 -5
- package/catalog/skills/review-pr/SKILL.md +7 -1
- package/catalog/skills/review-pr/reference.md +3 -2
- package/catalog/templates/claude-code/agents.md.tpl +2 -1
- package/catalog/templates/claude-code/harness.config.yml.tpl +12 -3
- package/dist/cli.mjs +2579 -775
- package/package.json +11 -10
|
@@ -36,14 +36,14 @@ Every event line starts with this envelope. Per-type fields are added at the
|
|
|
36
36
|
same top level — there is no nested `payload`, so Zod discriminated unions key on
|
|
37
37
|
`type`.
|
|
38
38
|
|
|
39
|
-
| Field | Type | Required | Axis | Notes
|
|
40
|
-
| ----------------- | ------ | -------- | --------------- |
|
|
41
|
-
| `type` | string | yes | `internal-enum` | One of the 9 event types listed below. Discriminator.
|
|
42
|
-
| `ts` | string | yes | `metric` | UTC ISO 8601 with `Z` suffix (e.g. `2026-05-28T14:30:00.000Z`). **No local offsets.**
|
|
43
|
-
| `user` | string | yes | `local-only` | `git config user.email` of the actor. Never exported in any tier.
|
|
44
|
-
| `project` | string | yes | `identity` | `task_storage.repo` slug (e.g. `acme/widgets`), from `harness.config.yml`. **Never `OWNER/REPO`** — the CLI refuses to emit while that placeholder is the value (see [Placeholder guard](#placeholder-guard)).
|
|
45
|
-
| `task_id` | string | no | `identity` | Task issue id (e.g. `42`) when the event has a task context. Absent on global events; `session_closed` carries it when
|
|
46
|
-
| `harness_version` | string | yes | `metric` | `version` of the **installed** `@lemoncode/lemony` package — _not_ `vendor_version` from config.
|
|
39
|
+
| Field | Type | Required | Axis | Notes |
|
|
40
|
+
| ----------------- | ------ | -------- | --------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
41
|
+
| `type` | string | yes | `internal-enum` | One of the 9 event types listed below. Discriminator. |
|
|
42
|
+
| `ts` | string | yes | `metric` | UTC ISO 8601 with `Z` suffix (e.g. `2026-05-28T14:30:00.000Z`). **No local offsets.** |
|
|
43
|
+
| `user` | string | yes | `local-only` | `git config user.email` of the actor. Never exported in any tier. |
|
|
44
|
+
| `project` | string | yes | `identity` | `task_storage.repo` slug (e.g. `acme/widgets`), from `harness.config.yml`. **Never `OWNER/REPO`** — the CLI refuses to emit while that placeholder is the value (see [Placeholder guard](#placeholder-guard)). |
|
|
45
|
+
| `task_id` | string | no | `identity` | Task issue id (e.g. `42`) when the event has a task context. Absent on global events; `session_closed` carries it when the live branch is a `harness/<id>-<slug>` or `harness/closeout-<id>` task branch. A per-project correlator — only meaningful alongside `project`, so it shares the `identity` axis. |
|
|
46
|
+
| `harness_version` | string | yes | `metric` | `version` of the **installed** `@lemoncode/lemony` package — _not_ `vendor_version` from config. |
|
|
47
47
|
|
|
48
48
|
### Placeholder guard
|
|
49
49
|
|
|
@@ -115,8 +115,9 @@ forward-compatible — readers dispatch on `type` and ignore unknowns.
|
|
|
115
115
|
|
|
116
116
|
Emitted by `session-close.sh` on `SessionEnd` or `/pause` (manual). One per
|
|
117
117
|
session. The envelope's `task_id` is derived from the live branch at close time
|
|
118
|
-
(`harness/<id>-<slug>` → `<id>`)
|
|
119
|
-
detached
|
|
118
|
+
(`harness/<id>-<slug>` or `harness/closeout-<id>` → `<id>`) — while a rebase has
|
|
119
|
+
HEAD detached, the branch being rebased; a session closed on the default branch or
|
|
120
|
+
any other detached HEAD carries none.
|
|
120
121
|
|
|
121
122
|
| Field | Type | Required | Axis | Notes |
|
|
122
123
|
| ------------------ | ------- | -------- | --------------- | --------------------------------------------------------------------------------------------------------------- |
|
|
@@ -148,17 +149,18 @@ Emitted by the Orchestrator when it transitions `spec-in-progress → spec-ready
|
|
|
148
149
|
|
|
149
150
|
### 4. `task_done` _(P5)_
|
|
150
151
|
|
|
151
|
-
Emitted by the Orchestrator at closeout
|
|
152
|
-
|
|
152
|
+
Emitted by the Orchestrator at closeout **finalize** — after the closeout PR,
|
|
153
|
+
which archives the spec and drops the task's `progress.md`, has merged.
|
|
153
154
|
|
|
154
|
-
| Field | Type | Required | Axis | Notes
|
|
155
|
-
| ------------------- | ------ | -------- | --------------- |
|
|
156
|
-
| `task_id` | string | yes | `identity` | Required for this type.
|
|
157
|
-
| `level` | string | yes | `internal-enum` | `L1` \| `L2` \| `L3` — the task-fit dial value used.
|
|
158
|
-
| `cycle_time_h` | number | yes | `metric` | Wall-clock hours from issue creation to merge. ≥ 0, finite.
|
|
159
|
-
| `review_rejections` | number | yes | `metric` | Count of `review_rejected` events for this `task_id` (≥ 0, int).
|
|
160
|
-
| `mode` | string | no | `internal-enum` | `all_at_once` \| `step_by_step` — the mode chosen at the L1 approval gate. **Absent on L2** (the question only exists where `tasks.md` does).
|
|
161
|
-
| `steps` | number | no | `metric` | Count of `
|
|
155
|
+
| Field | Type | Required | Axis | Notes |
|
|
156
|
+
| ------------------- | ------ | -------- | --------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
|
157
|
+
| `task_id` | string | yes | `identity` | Required for this type. |
|
|
158
|
+
| `level` | string | yes | `internal-enum` | `L1` \| `L2` \| `L3` — the task-fit dial value used. |
|
|
159
|
+
| `cycle_time_h` | number | yes | `metric` | Wall-clock hours from issue creation to merge. ≥ 0, finite. |
|
|
160
|
+
| `review_rejections` | number | yes | `metric` | Count of `review_rejected` events for this `task_id` (≥ 0, int). |
|
|
161
|
+
| `mode` | string | no | `internal-enum` | `all_at_once` \| `step_by_step` — the mode chosen at the L1 approval gate. **Absent on L2** (the question only exists where `tasks.md` does), and on an L1 task whose gate choice closeout could not recover. |
|
|
162
|
+
| `steps` | number | no | `metric` | Count of **distinct steps** (distinct `step` values) across this task's `step_completed` events (≥ 1, int). Only meaningful when `mode` is `step_by_step`; < total groups after a mid-task downgrade. Before 0.6.0 it counted every event — see [history](tier2-events-history.md). |
|
|
163
|
+
| `checkpoints` | number | no | `metric` | Count of `step_completed` events for this task (≥ 1, int) — one per resolved human checkpoint, so a step sent back counts again. Sent together with `steps`; `checkpoints − steps` is the task's extra checkpoint rounds — normally its `changes` answers at step checkpoints. Not counted: any human gate after a downgrade to all-at-once (it emits nothing), and change requests at the merge gate. |
|
|
162
164
|
|
|
163
165
|
### 5. `review_rejected` _(P5)_
|
|
164
166
|
|
|
@@ -211,7 +213,9 @@ post-merge / production signal; conflating them would dirty the post-merge metri
|
|
|
211
213
|
### 9. `step_completed` _(step-by-step mode)_
|
|
212
214
|
|
|
213
215
|
Emitted by the Orchestrator each time a human checkpoint **resolves** in
|
|
214
|
-
step-by-step mode (L1 opt-in, chosen at the approval gate)
|
|
216
|
+
step-by-step mode (L1 opt-in, chosen at the approval gate), through the
|
|
217
|
+
`checkpoint` verb that also makes the resolution's commits — or by hand,
|
|
218
|
+
late, when that call never ran for an answer. One event per
|
|
215
219
|
checkpoint, not per step: a step the human sends back ("changes") emits again
|
|
216
220
|
when it re-checkpoints, with the same `step`. This is the signal that justifies
|
|
217
221
|
(or condemns) the mode — the rate of checkpoints that catch things, and where
|
|
@@ -272,7 +276,11 @@ A writer (the `lemony emit` CLI):
|
|
|
272
276
|
2. Merges per-type fields into the same top level (no nested `payload`).
|
|
273
277
|
3. Validates against the Zod schema for `type` (each schema is `.strict()`,
|
|
274
278
|
so an unknown key — typically a typo'd `--task-iid` flag — **rejects**
|
|
275
|
-
loud).
|
|
279
|
+
loud). `task_done` also rejects step counts that contradict each other:
|
|
280
|
+
`steps`/`checkpoints` on a task whose `mode` is not `step_by_step`,
|
|
281
|
+
`checkpoints` without `steps`, and `checkpoints < steps`. These rules run at
|
|
282
|
+
emit time only — readers do not re-check lines already written. Never writes
|
|
283
|
+
a partial line.
|
|
276
284
|
4. Appends the JSON line via `fs.appendFile` (single `O_APPEND` `write(2)`,
|
|
277
285
|
POSIX-atomic up to `PIPE_BUF`) to `.claude/state/events.jsonl`. Creates the
|
|
278
286
|
file (and parent dir, scaffolded by `install`) when missing.
|
|
@@ -10,7 +10,9 @@ invoked-by: [implementer]
|
|
|
10
10
|
# Build UI
|
|
11
11
|
|
|
12
12
|
The implementer's **build method**: turn a `ui-handoff.md` (the design contract) plus
|
|
13
|
-
`docs/design-tokens.json` (the token source of truth
|
|
13
|
+
`docs/design-tokens.json` (the token source of truth — or the path `design_tokens.file` names
|
|
14
|
+
in `harness.config.yml`, when set; read that file wherever this skill says
|
|
15
|
+
`docs/design-tokens.json`) into UI code that applies the
|
|
14
16
|
project's tokens correctly, carries the design's point of view instead of generic
|
|
15
17
|
defaults, and is accessible by construction.
|
|
16
18
|
|
|
@@ -44,7 +46,8 @@ v3→v4 jump turns it into a lie); the live model does not. So this skill names
|
|
|
44
46
|
|
|
45
47
|
## Tokens-as-code — the contract
|
|
46
48
|
|
|
47
|
-
`docs/design-tokens.json` is the **single, client-owned source of truth
|
|
49
|
+
`docs/design-tokens.json` is the **single, client-owned source of truth** (or the path
|
|
50
|
+
`harness.config.yml` names in `design_tokens.file`, when set — read that file instead), a 3-tier
|
|
48
51
|
W3C-DTCG model: **primitive** (raw scale values) → **semantic** (intent: `color.surface`,
|
|
49
52
|
`space.inset.md`) → **component** (a component's specific slots). Two rules hold on every
|
|
50
53
|
stack:
|
|
@@ -20,6 +20,11 @@ ahead. That asymmetry shapes everything here:
|
|
|
20
20
|
- **drift** is one-directional and deterministic: "has the JSON changed since the last
|
|
21
21
|
export?" — a pure-JSON check that needs no tool connection.
|
|
22
22
|
|
|
23
|
+
**Which file.** `docs/design-tokens.json` is the default path. When `harness.config.yml` sets
|
|
24
|
+
`design_tokens.file`, the path it names (relative to the repository root) is the token file:
|
|
25
|
+
read the binding from it and expect the CLI's import and export to act on it, wherever this
|
|
26
|
+
skill says `docs/design-tokens.json`. Never create a second file at the default.
|
|
27
|
+
|
|
23
28
|
You are invoked two ways: by `/sync-design-tokens` (the human's explicit handle, argument
|
|
24
29
|
`import` or `export`), and at DEFINE when the drift check shows an export is pending and the
|
|
25
30
|
current user has the tool connected.
|
|
@@ -70,34 +75,70 @@ variables and the DTCG JSON:
|
|
|
70
75
|
- `name` — the variable's dotted path. Map it to/from the tool's own grouping separator.
|
|
71
76
|
- `value` for a literal; `ref` for an alias (the name of the variable it points at).
|
|
72
77
|
- `modes` — per-theme overrides (the base theme is `value`/`ref`, not repeated).
|
|
78
|
+
- Every `value` and mode value is a **string** — `"1024"`, not `1024` — and every `ref` a
|
|
79
|
+
non-empty one; each variable carries a `value` or a `ref` (a `value` beside a `ref` is
|
|
80
|
+
ignored), and `modes` is an object whose mode names are not blank nor `__proto__`. A name
|
|
81
|
+
is dot-separated segments, none empty, none starting with `$`, none `__proto__`, not just
|
|
82
|
+
a tier (`primitive`, `semantic`, `component`), and no two variables share one. The CLI
|
|
83
|
+
refuses a file that breaks any of these, naming the variable (and the mode, for a mode
|
|
84
|
+
value).
|
|
73
85
|
|
|
74
86
|
The CLI maps this to the 3-tier DTCG model: `ref` → an alias `{path}`, `modes` →
|
|
75
|
-
`$extensions["com.lemony.modes"]`, and the tier follows the path
|
|
76
|
-
|
|
87
|
+
`$extensions["com.lemony.modes"]`, and the tier follows the path. A name without a tier
|
|
88
|
+
lands on the one tier `docs/design-tokens.json` already has it in; otherwise a variable that
|
|
89
|
+
is literal in every mode defaults to `primitive`, and one that is an alias in its base value
|
|
90
|
+
or any mode to `semantic` (a primitive is literal in every mode). A bare `ref` — or a bare
|
|
91
|
+
`{alias}` mode value — resolves to the tiered path its target lands at in the same neutral
|
|
92
|
+
file, or to the one tier the token file has it in.
|
|
77
93
|
|
|
78
94
|
## import — tool → JSON
|
|
79
95
|
|
|
80
96
|
1. **Read** the tool's variables over MCP and write them to a neutral file (a temp path).
|
|
81
97
|
2. **Preview** the diff (writes nothing):
|
|
82
98
|
`lemony design-tokens import --from=<neutral-file>`. It prints, per token, the target tier
|
|
83
|
-
and `new` / `changed` (
|
|
99
|
+
and `new` / `changed` (each thing that changes, old → new: the value, the `$type`, each
|
|
100
|
+
mode) / `unchanged`. When
|
|
101
|
+
`docs/design-tokens.json` already fails validation it adds a `Warning:` saying so: the
|
|
102
|
+
apply will refuse any slice that leaves it failing, so tell the human before curating.
|
|
84
103
|
3. **Present and curate.** Walk the human through it: which new tokens to take, which changes
|
|
85
104
|
to accept, and — where a tool-origin name is ambiguous — whether a literal belongs in
|
|
86
105
|
`primitive` or `semantic`. The human owns the slice.
|
|
87
106
|
4. **Apply** the agreed slice:
|
|
88
107
|
`lemony design-tokens import --from=<neutral-file> --apply --only=<dotted,paths>`.
|
|
89
108
|
This does the additive merge into `docs/design-tokens.json` deterministically (it
|
|
90
|
-
bootstraps the file if it does not exist yet
|
|
91
|
-
|
|
109
|
+
bootstraps the file if it does not exist yet and the slice writes something). A changed
|
|
110
|
+
token keeps what the neutral file does not carry — `$description`, a contrast pairing,
|
|
111
|
+
other extensions — and an unchanged one is left as it is. A token the tool cannot hold
|
|
112
|
+
(see export) is never written over: the preview shows it as a change from
|
|
113
|
+
`(outside the sync: <why>)`, and the apply lists it as not applied. It writes nothing
|
|
114
|
+
when the merged file would fail validation — typically a `semantic` alias whose
|
|
115
|
+
`primitive` target is neither in the file nor in the slice: add the target to the same
|
|
116
|
+
`--only`, or fix the file first. When the neutral file does not carry the target at
|
|
117
|
+
all, the refusal says so: create it in the tool first, or leave out what references
|
|
118
|
+
it. A fault no slice fixes, such as a `primitive` that aliases, is named the same way:
|
|
119
|
+
fix that variable in the tool, or leave it out. An `--only` path no variable lands at
|
|
120
|
+
is refused rather than skipped. A refusal prints the preview again, so the slice can be
|
|
121
|
+
re-curated from the paths it lists. Then run `lemony design-tokens validate` to confirm
|
|
122
|
+
the result is well-formed.
|
|
92
123
|
|
|
93
124
|
## export — JSON → tool
|
|
94
125
|
|
|
95
|
-
1. **Plan.**
|
|
126
|
+
1. **Plan.** `export` refuses (exit 1) a `docs/design-tokens.json` that fails
|
|
127
|
+
`lemony design-tokens validate` — fix the file first. Read the tool's current variables
|
|
128
|
+
over MCP into a neutral file, then:
|
|
96
129
|
`lemony design-tokens export --tool-state=<tool-state> --out=<projection-file>`. It prints
|
|
97
130
|
the additive upsert plan (how many to create, how many to update — **never any deletes**)
|
|
98
131
|
and writes the projection the tool should hold to `<projection-file>`. The `--tool-state`
|
|
99
132
|
is optional; without it every variable is planned as a create (the upsert is idempotent by
|
|
100
|
-
name either way).
|
|
133
|
+
name either way). A tool variable named without its tier (`color.brand`) is matched to the
|
|
134
|
+
path `import` lands it at (`primitive.color.brand`), so when you push, write into that
|
|
135
|
+
existing variable rather than creating a tiered twin. A token a tool variable cannot hold
|
|
136
|
+
is not projected: a composite (a shadow, a typography, a cubic Bézier — an object or
|
|
137
|
+
array `$value`), a token whose modes the neutral format cannot carry (a mode value that
|
|
138
|
+
is not text, a number or a boolean, a blank or `__proto__` mode name, a modes extension
|
|
139
|
+
that is not an object), and any alias to one of those. The plan names each as "not
|
|
140
|
+
projected", with why — say so to the human. `--out` takes a file path (not a pipe, nor
|
|
141
|
+
the token file itself).
|
|
101
142
|
2. **Confirm.** Show the plan; the human approves.
|
|
102
143
|
3. **Push.** Write the projection's variables into the tool over MCP — create new ones, update
|
|
103
144
|
changed ones, leave tool-only variables untouched.
|
|
@@ -75,7 +75,10 @@ the design direction it settles can inform the spec. Before the first question:
|
|
|
75
75
|
[Persisting personas](#persisting-personas-when-absent)) — an opt-in offer on your human-facing
|
|
76
76
|
surface, never an unasked write.
|
|
77
77
|
- **Consume `docs/design-tokens.json` if it exists** — the single source of truth for tokens. The
|
|
78
|
-
handoff §11 points at it; you never inline token values.
|
|
78
|
+
handoff §11 points at it; you never inline token values. That is the default path: when
|
|
79
|
+
`harness.config.yml` sets `design_tokens.file`, the path it names (relative to the repository
|
|
80
|
+
root) is the token file — read, scaffold, bind and point at that path wherever this skill says
|
|
81
|
+
`docs/design-tokens.json`, and never create a second file at the default.
|
|
79
82
|
- **Read `docs/architecture.md` if it exists** — orient against the system's shape so design
|
|
80
83
|
proposals don't contradict existing surfaces. Absent is fine; never push the user to create it.
|
|
81
84
|
|
|
@@ -84,7 +84,8 @@ Every section is **N/A-able** per task: mark a section N/A rather than padding i
|
|
|
84
84
|
|
|
85
85
|
## 11. Token reference
|
|
86
86
|
|
|
87
|
-
Reference `docs/design-tokens.json
|
|
87
|
+
Reference the token file — `docs/design-tokens.json`, or the path `design_tokens.file` names in
|
|
88
|
+
`harness.config.yml` — the single, client-owned source of truth, a 3-tier
|
|
88
89
|
W3C-DTCG model (`primitive` → `semantic` → `component`). **Never inline token values here; point at
|
|
89
90
|
the file.** If the project has no token file yet, the interview offers to scaffold one (derived from
|
|
90
91
|
the answers) or to connect a design tool; if the human declines both, say so and capture it as an
|
|
@@ -71,19 +71,33 @@ Without a script, each probe is yours to run. The mechanics:
|
|
|
71
71
|
as accounted-for, is the ledger contract's rule, not this skill's — the floor asks
|
|
72
72
|
for accounting, never exhaustion. One or two well-aimed probes per logic-bearing
|
|
73
73
|
file usually answer the question.
|
|
74
|
-
- **The round trip is one composite command
|
|
75
|
-
mutation, run the focused test, and restore —
|
|
76
|
-
|
|
77
|
-
|
|
74
|
+
- **The round trip is one composite command whose revert is a trap, installed before
|
|
75
|
+
the mutation.** Apply the mutation, run the focused test, and restore — in one
|
|
76
|
+
shell invocation, never separate edit / test / revert calls, and never a revert
|
|
77
|
+
that a red test or an interrupt can skip. The session can be stopped at any instant
|
|
78
|
+
(Esc stops the running command with SIGTERM), and a mutant left behind reads as
|
|
79
|
+
real work to whoever resumes. The `EXIT` trap restores on every way out — green,
|
|
80
|
+
red, or interrupted:
|
|
78
81
|
|
|
79
82
|
```bash
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
+
( trap 'mv -f src/queue.ts.bak src/queue.ts 2>/dev/null' EXIT; \
|
|
84
|
+
trap 'kill $! 2>/dev/null; exit 130' INT TERM HUP; \
|
|
85
|
+
sed -i.bak '128s/>=/>/' src/queue.ts; \
|
|
86
|
+
npx vitest run src/queue.spec.ts & wait $! )
|
|
83
87
|
```
|
|
84
88
|
|
|
85
89
|
(Line-addressed on purpose — one targeted mutant per trip keeps the kill
|
|
86
|
-
attributable; the `.bak` the in-place edit leaves is the revert.
|
|
90
|
+
attributable; the `.bak` the in-place edit leaves is the revert. The subshell
|
|
91
|
+
scopes the traps to the probe; the `INT TERM HUP` trap stops the runner and turns
|
|
92
|
+
the signal into an ordinary exit so the `EXIT` trap runs. The test runs in the
|
|
93
|
+
background under `wait` because a shell runs no trap while a foreground child is
|
|
94
|
+
still running, and Claude Code follows Esc's SIGTERM with a SIGKILL within a
|
|
95
|
+
second or two: `wait` returns on the signal, so the restore lands first. The
|
|
96
|
+
`kill $!` matters for a Ctrl-C too — a background job in a non-interactive shell
|
|
97
|
+
ignores SIGINT. Only a `SIGKILL` before the trap runs or a machine crash can
|
|
98
|
+
still strand a mutant — `git status` after the
|
|
99
|
+
trip shows the tree exactly as you found it, and a leftover `.bak` is the sign
|
|
100
|
+
one was stranded.)
|
|
87
101
|
|
|
88
102
|
A **killed** mutant (the focused test went red) is the good outcome; a **survivor**
|
|
89
103
|
(still green on broken code) is the finding. Batching buys trips, never
|
|
@@ -209,7 +209,11 @@ close, is always read as the declaration: it is reported if it does not end the
|
|
|
209
209
|
even when a list ends a later line. Keep every other `(R<n>)` mention in detail prose
|
|
210
210
|
**mid-line**. When two lists end continuation lines the validator cannot tell which is
|
|
211
211
|
the declaration and reports the task; on a task that declares no list, the one
|
|
212
|
-
line-ending mention is read as its declaration, and a mid-line one is reported.
|
|
212
|
+
line-ending mention is read as its declaration, and a mid-line one is reported. An
|
|
213
|
+
aside that must open a parenthesis with a requirement id opens it with `per` —
|
|
214
|
+
`(per R42, …)` — because a bare `(R42…` falls under the rules above exactly like a
|
|
215
|
+
declaration, and can be elected over the real list, sometimes silently. A `per` aside
|
|
216
|
+
is never read, so it never stands in for the list: the task still declares its own.
|
|
213
217
|
|
|
214
218
|
The review evidence ledger's validator enumerates each group's review slice from these
|
|
215
219
|
refs; a task it cannot read is reported as `malformed-task-refs` — a spec defect that
|
|
@@ -220,14 +224,32 @@ when the task reaches green, with the task's commit. Write every task `- [ ]`; t
|
|
|
220
224
|
validator reports a task still `[ ]` in a group the loop has passed
|
|
221
225
|
(`unticked-completed-task`), so the file tracks the progress its shape promises.
|
|
222
226
|
|
|
227
|
+
**Line shapes — read by the same script.** A task line is `- [ ]` or `- [x]` and a bare
|
|
228
|
+
`T<n>` id, at the task list's own indentation; a group header is exactly `## Group <n>`,
|
|
229
|
+
numbered from 1, with `Group` in English even when the spec is written in another
|
|
230
|
+
language. A line that looks like one but is not — an in-progress `[-]`, a suffixed
|
|
231
|
+
`T12b` or `T1.1`, a numbered `1. [ ]` item, a `- T6` with no box, a task inside a `>`
|
|
232
|
+
quote, a `T<n>` sub-item indented under another task, `### Group 3`, `## Group 0`, a
|
|
233
|
+
digit-less `## Group P`, a translated `## Grupo 2 — … _(…)_` — is reported
|
|
234
|
+
(`malformed-task-line`, `malformed-group-header`), never skipped; a note bullet about a
|
|
235
|
+
task opens with
|
|
236
|
+
something other than its `T<n>`. Every `T<n>` is used once in the file
|
|
237
|
+
(`duplicate-task-id` otherwise). Detail prose may run to several paragraphs: a blank
|
|
238
|
+
line inside a task is fine while the next line stays indented at least to the task's
|
|
239
|
+
text (two spaces under `- [ ]`); less, and it is no longer the task's. Code fences and
|
|
240
|
+
`>` quotes are never read as tasks, but a task or header shape inside one is reported
|
|
241
|
+
too — a quoted sample looks exactly like a task under a fence left open — so quote
|
|
242
|
+
samples without a `T<n>` id or a group header.
|
|
243
|
+
|
|
223
244
|
Rules: order so the first task is a tracer bullet; never "write all tests" then
|
|
224
245
|
"write all code"; keep each task small enough to verify on its own. Grouping never
|
|
225
246
|
changes task granularity — checkboxes stay atomic and TDD runs per task; only review
|
|
226
247
|
and checkpoint frequency follow the groups (all-at-once mode ignores the headers).
|
|
227
|
-
Tasks added mid-implementation (from a discovery)
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
248
|
+
Tasks added mid-implementation (from a discovery) take the **next free `T<n>`** (never
|
|
249
|
+
a suffixed `T12b`) and default to **their own group** — they are risk by definition,
|
|
250
|
+
and grouping rule 5 applies to them unchanged — but they are added **after** the spec
|
|
251
|
+
gate, and no later gate presents a group header, so their tags get no human reading at
|
|
252
|
+
all. Tag them for the record; do not rely on anyone
|
|
231
253
|
catching a wrong one.
|
|
232
254
|
|
|
233
255
|
### 5. Self-check before handing off
|
|
@@ -37,7 +37,7 @@ Read the repo's `CLAUDE.md` (and `CONTEXT.md` if present). Summarize in ≤ 50 l
|
|
|
37
37
|
code-style rules, comment conventions, naming, architecture constraints. This is what
|
|
38
38
|
the analysis judges "against project convention" by.
|
|
39
39
|
|
|
40
|
-
### 2. Analyze (single sub-agent, the review lenses as a checklist)
|
|
40
|
+
### 2. Analyze (one pass — a single sub-agent, or inline — the review lenses as a checklist)
|
|
41
41
|
|
|
42
42
|
Spawn **one** `Explore` sub-agent with the full diff, the changed-files list, and the
|
|
43
43
|
project context. Its prompt walks the five review lenses as a checklist — **not** five
|
|
@@ -58,6 +58,12 @@ The agent returns a strict JSON array of findings (see [reference.md](reference.
|
|
|
58
58
|
the exact prompt and schema). It returns `[]` when the diff is clean — it must **not**
|
|
59
59
|
manufacture findings to seem thorough.
|
|
60
60
|
|
|
61
|
+
**A Reviewer, or no `Agent` tool?** A Reviewer never spawns, whatever its tools (the
|
|
62
|
+
opening rule of the Reviewer contract, `.claude/agents/reviewer.md`), and an agent at
|
|
63
|
+
Claude Code's nesting depth limit has no `Agent` tool unless it is a fork. Then walk
|
|
64
|
+
the five lenses yourself, in this context, with the same prompt as your checklist, and
|
|
65
|
+
produce the same JSON array; the rest of the flow is unchanged.
|
|
66
|
+
|
|
61
67
|
### 3. Dry-run
|
|
62
68
|
|
|
63
69
|
Print the findings as a numbered table (don't post anything yet):
|
|
@@ -2,8 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
## Analysis agent prompt
|
|
4
4
|
|
|
5
|
-
Pass this to the single `Explore` sub-agent
|
|
6
|
-
|
|
5
|
+
Pass this to the single `Explore` sub-agent — or, as a Reviewer or with no `Agent`
|
|
6
|
+
tool (SKILL.md §2), walk it yourself. The five lenses are a **checklist one agent
|
|
7
|
+
walks**, not a fan-out.
|
|
7
8
|
|
|
8
9
|
```
|
|
9
10
|
You are reviewing a GitHub PR diff. Find real issues worth posting as inline review
|
|
@@ -105,7 +105,8 @@ A task deserves the harness if it is **specifiable**, **verifiable**, or
|
|
|
105
105
|
checks precondition (`.claude/hooks/lib/merge-pr.sh --approve-issue <issue>`)
|
|
106
106
|
and never lands on red, absent, or still-pending checks without asking — nor
|
|
107
107
|
content that no longer matches what the Reviewer's APPROVE reviewed (the
|
|
108
|
-
stale-approve guard; a clean update-branch
|
|
108
|
+
stale-approve guard; a clean update-branch or a task-state write under
|
|
109
|
+
`.claude/state` stays valid, any other content change
|
|
109
110
|
routes back to re-review). The task stays at `in-review`
|
|
110
111
|
until merged.
|
|
111
112
|
10. **Closeout** — `task-closeout`: confirm the merge via `gh`, run the three Architect
|
|
@@ -83,16 +83,25 @@ rollback:
|
|
|
83
83
|
# - test
|
|
84
84
|
# - build
|
|
85
85
|
|
|
86
|
-
# Design tokens (`design-tokens
|
|
86
|
+
# Design tokens (the `design-tokens` verbs, and the token lines of `doctor` / `status`).
|
|
87
87
|
# `scan_extensions`: the anti-hardcode scan inspects a built-in set of UI/style
|
|
88
88
|
# extensions (.css/.scss/.ts/.tsx/.vue/.svelte/.astro/.js/.mdx/.html/…). Add extra
|
|
89
89
|
# suffixes here for a stack the built-ins don't cover — additive, never a replacement.
|
|
90
90
|
# Default none.
|
|
91
|
-
# `verify`: a command line `design-tokens contrast` runs
|
|
91
|
+
# `verify`: a command line `design-tokens contrast` runs alongside its own WCAG checks
|
|
92
92
|
# (through `sh -c`, in the repo root) — your design system's own verifier for the rules
|
|
93
93
|
# only it can know (palette under colour-vision-deficiency simulation, property grammar,
|
|
94
|
-
# CSS scans). Its output is forwarded; a non-zero exit fails the gate.
|
|
94
|
+
# CSS scans). Its output is forwarded; a non-zero exit fails the gate. It runs on every
|
|
95
|
+
# path: with no token file only the WCAG pair check is skipped, and a file
|
|
96
|
+
# that cannot be read or parsed is reported alongside it — never the verifier you
|
|
97
|
+
# declared. Default none.
|
|
98
|
+
# `file`: where the token file lives, relative to the repository root — for a DTCG
|
|
99
|
+
# file kept (or generated) somewhere other than docs/design-tokens.json. Every
|
|
100
|
+
# design-tokens verb, `doctor` and `status` read it from here. An absolute path, a `\`
|
|
101
|
+
# separator, a path that leads out of the repository or names its root, or one ending
|
|
102
|
+
# in `/` or `/.` is a config error. Default docs/design-tokens.json.
|
|
95
103
|
# design_tokens:
|
|
96
104
|
# scan_extensions:
|
|
97
105
|
# - .foo
|
|
98
106
|
# verify: pnpm exec my-design-system verify
|
|
107
|
+
# file: design/tokens.json
|