wei-wuwei 0.0.0-stage → 0.2.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.
Files changed (36) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +21 -0
  3. package/README.md +241 -2
  4. package/agents/_template/agent.md +28 -0
  5. package/agents/wuwei-audit-composition/agent.md +85 -0
  6. package/agents/wuwei-audit-contract/agent.md +97 -0
  7. package/agents/wuwei-audit-coverage/agent.md +93 -0
  8. package/agents/wuwei-implementer/agent.md +34 -0
  9. package/agents/wuwei-pre-implementation-doubt/agent.md +158 -0
  10. package/agents/wuwei-review-claims/agent.md +187 -0
  11. package/agents/wuwei-review-code/agent.md +219 -0
  12. package/agents/wuwei-review-defects/agent.md +180 -0
  13. package/agents/wuwei-review-design/agent.md +177 -0
  14. package/agents/wuwei-review-drift/agent.md +184 -0
  15. package/agents/wuwei-review-structure/agent.md +86 -0
  16. package/agents/wuwei-security-auditor/agent.md +229 -0
  17. package/bin/wuwei.js +2832 -0
  18. package/package.json +44 -4
  19. package/skills/_template/skill.md +34 -0
  20. package/skills/wuwei-brainstorm/skill.md +69 -0
  21. package/skills/wuwei-commit-hygiene/skill.md +38 -0
  22. package/skills/wuwei-debug-root-cause/skill.md +48 -0
  23. package/skills/wuwei-execute-phase/skill.md +624 -0
  24. package/skills/wuwei-grill/skill.md +67 -0
  25. package/skills/wuwei-legible-output/skill.md +27 -0
  26. package/skills/wuwei-map-codebase/skill.md +171 -0
  27. package/skills/wuwei-plan-phases/skill.md +326 -0
  28. package/skills/wuwei-review/references/lens-rules.md +41 -0
  29. package/skills/wuwei-review/references/prep.md +35 -0
  30. package/skills/wuwei-review/references/scopes.md +56 -0
  31. package/skills/wuwei-review/references/stream.md +94 -0
  32. package/skills/wuwei-review/skill.md +495 -0
  33. package/skills/wuwei-tdd/skill.md +47 -0
  34. package/templates/code-review.md +137 -0
  35. package/templates/pr.md +35 -0
  36. package/wuwei.config.json +51 -0
@@ -0,0 +1,624 @@
1
+ ---
2
+ name: wuwei-execute-phase
3
+ description: >-
4
+ Use to execute a planned phase or continue a stream — the user says
5
+ "continue the stream / 接手 / 繼續做", a stream has a signed acceptance
6
+ contract and an eligible phase (dependencies merged, unclaimed or
7
+ stale-claimed), or a merge just unlocked a dependent phase. Runs the fixed
8
+ pipeline: claim → implement → e2e → three or more rounds of
9
+ simplify/review/fix → PR — one phase, or a range on request.
10
+ version: 0.2.0
11
+ mode: triggered
12
+ model: sonnet
13
+ effort: xhigh
14
+ targets: [claude-code, codex]
15
+ tags: [execution, workflow]
16
+ ---
17
+
18
+ # Execute phase
19
+
20
+ ## When to use
21
+
22
+ - A signed stream has an eligible phase and this session should take it.
23
+ - The caller gives a range instead of a phase (`## Run scope`). Nothing
24
+ parses the shorthand — this file defines it, so a mistyped token gets asked
25
+ about.
26
+ - Resuming: `git pull`, read `STATE.md`, continue from recorded state. That
27
+ is the whole takeover mechanism.
28
+
29
+ ## When NOT to use
30
+
31
+ - No signed contract → `wuwei-plan-phases` first. An unsigned plan means a
32
+ self-defined finish line.
33
+ - Hotfix outside any stream → `wuwei-debug-root-cause` + `wuwei-tdd` +
34
+ `wuwei-commit-hygiene`.
35
+
36
+ ## Invariants
37
+
38
+ Hold everywhere; the steps do not restate them.
39
+
40
+ - **Evidence, not narration.** Nothing is done without its recorded green e2e.
41
+ - **A fresh subagent implements, a fresh subagent reviews.** Inherited
42
+ context is not review. Inline only where no subagents exist (Codex) — a
43
+ fallback, not an equal option.
44
+ - **Anything changing what was signed off stops and escalates.** `manual`
45
+ asks now, `auto` records in the phase file and pauses. No scope or `mode`
46
+ pre-approves it.
47
+ - **Transition only what this session observed.** Work merged after handover
48
+ belongs to the next claim's reconcile, or to the merger.
49
+ - **Graph first, Grep as fallback, always record which.** A reconstructed
50
+ edge never carries the confidence of a queried one.
51
+
52
+ ## Base sync
53
+
54
+ Steps 1 and 5 both do this.
55
+
56
+ - Merge the **stream branch named in `## Sign-off`**. Read it there, never
57
+ rebuild it from the phase branch name. Named nowhere → stop and say so;
58
+ planning owes it (its step 8).
59
+ - Compare against the integration branch's **tip**, never the sha beside it.
60
+ Syncing to that succeeds, changes nothing, and reports nothing.
61
+ - Sync the **stream branch**, never one phase branch. It is the shared base,
62
+ so one sync serves every later phase; per-phase syncing gives each its own
63
+ merge of main, and those collide.
64
+ - Let codegraph settle afterwards — its watcher reconciles changed paths, so
65
+ a merge is an incremental sync.
66
+ - **Record the ref synced to.** Every graph answer is only true as of it.
67
+
68
+ Then take every diff with **three dots**. Two dots looks right — non-empty,
69
+ plenty to say — and is about the wrong code:
70
+
71
+ ```
72
+ git diff <base>...HEAD # correct — this phase only
73
+ git diff <base>..HEAD # wrong — the merged base counts as yours
74
+ ```
75
+
76
+ **The one two-dot diff in this pipeline is step 5's delta round**
77
+ (`wuwei-review` §6): two shas on this same branch with no base movement
78
+ between them, so there is no merged base there to fold in — and a sync that
79
+ moved the base is exactly what voids the delta and sends that round back to
80
+ full scope.
81
+
82
+ ## Run scope
83
+
84
+ Steps 1–8 cover **one** phase. How many times you go round is settled once
85
+ per invocation.
86
+
87
+ - Default: `manual` → one phase. `auto` → the whole stream, **never
88
+ re-asked**, because sign-off authorised it.
89
+ - Shorthand: `--through <NN>` inclusive, `--through end`, `--all`, or the
90
+ same in words.
91
+ - Given a range, print it and go. Given none, ask.
92
+
93
+ Print before claiming anything: the scope line over `wuwei claim
94
+ <stream>`'s list, which already marks `review: human` and anything waiting or
95
+ blocked — `run scope: 02 → 04 · 3 phases · mode: manual`, then where it
96
+ will stop and which phase starts.
97
+
98
+ Stopping early is normal — say where, why, and what resumes:
99
+
100
+ ```
101
+ stopped at 03-appcheck-wiring · bound e2e red (auth/refresh.spec.ts)
102
+ 02 merged. 03 claimed, branch pushed, not merged.
103
+ next invocation resumes at 03: /wuwei-execute-phase
104
+ ```
105
+
106
+ Name the invocation: this skill's sonnet / xhigh holds for the turn it runs
107
+ in, and a reply in chat continues on the session's model.
108
+
109
+ **Scope is distance, never permission.** `mode` is the stream's standing
110
+ authorisation from sign-off; scope is only how far one invocation tries to
111
+ get.
112
+
113
+ - `review: human` phases were circled at sign-off. The run **always stops**
114
+ at each, and **no token skips it** — carrying through takes words, naming
115
+ the reason it was circled (auth / payments / migrations / irreversible).
116
+ - There is deliberately **no "skip the gates" token**: an endpoint already
117
+ implies not pausing, so a second one adds **no reach at all** — only a name
118
+ that reads as permission to override `mode`.
119
+ - A red bound e2e, a SPEC-changing discovery, a dependency conflict, and
120
+ entering main stop regardless of scope.
121
+
122
+ ## Procedure
123
+
124
+ 1. **Claim.** Pull; pick a ready phase from `wuwei claim <stream>`, or decide
125
+ a stale claim is abandoned (`--take-over` records it in the phase file).
126
+
127
+ **Catch the stream branch up before cutting anything**: base sync, then
128
+ re-run the stream's already-green e2e on it. Red → `manual` stops and
129
+ reports, `auto` surfaces a stuck stream; **never cut a phase branch off a
130
+ stream branch whose evidence is red**. The sync moves the base under any
131
+ in-flight phase — they sync again at their step 5, so no coordination is
132
+ needed, but note it in the phase file.
133
+
134
+ Then `wuwei claim <stream> <NN>` claims it and cuts `wuwei/<stream>/NN-<name>`
135
+ **from the stream branch** — not main, which would put the whole base
136
+ delta in this phase's PR.
137
+
138
+ **Tracker-linked?** Read each phase's issue from its own `tracker-issue`
139
+ frontmatter, never `STATE.md` — `done/total` is an aggregate and cannot
140
+ say *which* phase merged. Compare this phase and every earlier one against
141
+ the state its phase file implies, then apply the single write rule:
142
+
143
+ > **Only move an issue whose current state is one of the three names
144
+ > recorded at sign-off.**
145
+
146
+ Those three are ordered (in-progress → in-review → done), so "behind" is
147
+ decidable inside the set, and catching an earlier phase up is a write wuwei
148
+ owed. **That is the only write that is yours — forward, to catch up.**
149
+ Never backwards: an issue a human moved to `done` is not reopened because
150
+ a phase file still reads in-review. Anything outside the set — `Blocked`, `Won't Do`, `In QA`, anything
151
+ nobody mapped — is a human acting on the board: report and leave it,
152
+ **including when it is the phase you are claiming**. Under `single`
153
+ granularity there is no per-phase issue — see step 7.
154
+
155
+ 2. **Implement** the task checklist, one fresh subagent per task (SDD,
156
+ borrowed from superpowers). **Dispatch with four things:** SPEC, the phase
157
+ context, **the ladder below**, and **step 4's floor** — and nothing else.
158
+ The ladder is a question the subagent writing the helper has to ask; an
159
+ orchestrator holding it while the subagent writes is the same as not having
160
+ it. Dispatch `wuwei-implementer` with **no `model` or `effort` on the
161
+ call** — its file pins sonnet / xhigh, and a value on the call beats it.
162
+ Inline instead → watch for
163
+ drift, keep the phase small, and note that the model policy has nothing to
164
+ attach to.
165
+
166
+ **A task waits for every task in its `depends-on`** (`wuwei-plan-phases`
167
+ sets them) — dispatched only once those have landed. Tasks with no
168
+ `depends-on` are independent and may go in parallel. **A missing field
169
+ means independent, so a mis-planned one runs too early and its subagent
170
+ writes against an interface that is not there yet**; that is a planning
171
+ defect, reported, not worked around here.
172
+
173
+ `wuwei-tdd` governs each task; tick as they land; one task ≈ one commit
174
+ (`wuwei-commit-hygiene`). Library choice and internal structure are yours
175
+ — don't ask.
176
+
177
+ **Each commit stands where it is written.** One-per-task is a floor, not a
178
+ cap: a task holding two real concerns commits twice, in dependency order;
179
+ what is never allowed is one commit holding two.
180
+
181
+ - **No forward dependency.** Building on what an earlier commit landed is
182
+ the `depends-on` sequence working — task 3 using task 2's type is
183
+ correct. Referencing a helper, field, export or migration that a **later**
184
+ commit adds is not: that commit is half a change with a message, and it
185
+ breaks `git bisect` at the exact commit someone reaches for. Needed and
186
+ not there yet → **its task lands first**, never "I'll wire it up after".
187
+ - **The design is readable from the commit alone.** Subject names the
188
+ concern; the body says **why this shape** — and where the choice was not
189
+ obvious, the rung it stopped at and what it climbed past. Someone holding
190
+ only `git show` should not need the phase file to see the intent.
191
+ - The one commit allowed to be red is a `wuwei-tdd` test committed ahead of
192
+ its implementation, and only with that implementation as the **very next**
193
+ commit. Anything committed in between leaves a red commit that reads as a
194
+ broken tree.
195
+
196
+ **Climb before writing**, stopping at the first rung that holds. Skipping a
197
+ rung is not a different kind of mistake from step 4's — it is the same
198
+ finding, arriving later, so the rungs and step 4's tags are one vocabulary:
199
+
200
+ | | Ask | Skipped → step 4 reports |
201
+ |---|---|---|
202
+ | 1 | does this need to exist at all? | `yagni:` / `delete:` |
203
+ | 2 | **is it already in this codebase?** — a helper, type or pattern a few files over | `reuse:` |
204
+ | 3 | does the stdlib do it? | `stdlib:` |
205
+ | 4 | does a native platform feature cover it? | `native:` |
206
+ | 5 | does an **already-installed** dependency? Never add a new one for what a few lines do | `reuse:` |
207
+ | 6 | can it be one line? | `shrink:` |
208
+ | 7 | only then: the minimum code that works | — |
209
+
210
+ Rung 2 is the one that has to be caught here rather than later: `stdlib:`
211
+ and `native:` are visible to anyone reading the diff, while a helper three
212
+ files over is invisible unless you already looked. `review:defects` looks
213
+ too — **both sides gate** — and **a `reuse:` finding from either is a rung
214
+ that was skipped**, so it counts in the round's convergence like any other.
215
+
216
+ **The ladder shortens the solution, never the reading.** Trace every file
217
+ the change touches and the flow it really takes first, then climb — the
218
+ smallest change in the wrong place is not lazy, it is a second bug.
219
+
220
+ **A fix goes at the proven cause, and when the cause is in a shared
221
+ function, one guard there beats one per caller** — smaller diff, and the
222
+ siblings stop being broken too. Find the call sites the way `## Invariants`
223
+ says: graph first, Grep as fallback, record which. This holds wherever a fix
224
+ is written — here, step 6, and `wuwei-debug-root-cause` — and it never
225
+ licenses guarding the shared function *before* the cause is proven, which is
226
+ the guess that hides the real one.
227
+
228
+ 3. **e2e.** Run the phase's bound items. Red → `wuwei-debug-root-cause`.
229
+ Never proceed past a red bound item. Then `wuwei verify --phase NN`: a
230
+ page bound here and missing from `.wuwei/verify.json` is added first,
231
+ fixtures from the API's schema; each flag is fixed or answered.
232
+
233
+ 4. **Simplify.** **Steps 4–6 are one round, and a phase runs at least three.**
234
+ A round cuts, gets reviewed, fixes what came back, verifies, and closes as
235
+ a single commit — so the phase reads as rounds of work rather than a pile
236
+ of fixes.
237
+
238
+ Reread the whole diff and cut. One line per cut, tagged:
239
+
240
+ | Tag | What it marks |
241
+ |---|---|
242
+ | `delete:` | dead code, unused flexibility, speculative feature — nothing replaces it |
243
+ | `reuse:` | something this project already has — a helper, type or pattern in the repo, or an already-installed dependency. Name the file or the package |
244
+ | `stdlib:` | hand-rolled thing the standard library already ships. Name the function |
245
+ | `native:` | a dependency or code doing what the platform already does. Name the feature |
246
+ | `yagni:` | an abstraction with one implementation, config nobody sets, a layer with one caller |
247
+ | `shrink:` | same logic, fewer lines. Show the shorter form |
248
+
249
+ Format: `<file>:L<line>: <tag> <what>. <replacement>.` End with the only
250
+ number that matters — `net: -<N> lines` — or `nothing to cut` and stop.
251
+ Both go in the phase file.
252
+
253
+ **The cut report is one line per cut — no paragraph justifying it.** Every
254
+ paragraph defending a simplification is complexity smuggled back in as
255
+ prose.
256
+
257
+ This governs the cut report and nothing else. Prose that is **required**
258
+ stays in full: `wuwei:` markers (they explain code that is deliberately not
259
+ there, so they are always longer than it), commit bodies (`why`, not what),
260
+ comments stating an invariant, `## Notes`, digests, walkthroughs, phase
261
+ notes.
262
+
263
+ ```
264
+ ❌ this validator class might be more complex than it needs to be, and
265
+ it's worth considering whether all these rules are needed yet
266
+ ✅ auth/email.ts:L12-38: stdlib: 27-line validator class. "@" in the
267
+ address, 1 line; real validation is the confirmation mail.
268
+ ✅ api/repo.py:L88: yagni: AbstractRepository, one implementation. Inline
269
+ it until a second exists.
270
+ ✅ jobs/sync.go:L52-71: delete: retry wrapper around an idempotent local
271
+ call. Nothing replaces it.
272
+ ```
273
+
274
+ `<replacement>` is required, and for `delete:` "nothing" is the answer. The
275
+ ❌ is not too polite, it is **unactionable**: no location, no cut, no
276
+ replacement, and a question where a finding belongs.
277
+
278
+ **Never cut a test to make a number better.** A single smoke test or an
279
+ `assert`-based self-check is the floor, not bloat — cutting coverage cuts
280
+ the phase's only evidence. **Tests are outside this step's reach entirely**;
281
+ `review:defects` owns whether they are the right ones.
282
+
283
+ **Never simplify away** input validation at a trust boundary, error handling
284
+ that prevents data loss, a security measure, **anything this project's own
285
+ linters or checks would flag if it were absent** (a11y rules among them,
286
+ when the project has them configured), or anything the contract explicitly
287
+ asked for. A `yagni:` on "config nobody sets" is exactly how one of these
288
+ leaves.
289
+
290
+ The linter clause is deliberate: wuwei does not carry its own opinion on
291
+ what a project must check for — `review:code` already runs whatever the
292
+ project configured. A category with no configured check and no name in
293
+ `## Sign-off`'s threats is not this step's floor to enforce alone.
294
+
295
+ **Two ways the same size → the one that is correct on edge cases.** Fewer
296
+ lines, never a flimsier algorithm.
297
+
298
+ Skipping is allowed only for trivial diffs, and **the skip is recorded the
299
+ same way** — a step whose output is "simplified" is indistinguishable from
300
+ one that was skipped.
301
+
302
+ **A cut you decide not to make gets a marker where it would have gone**,
303
+ not a line in a document nobody will open:
304
+
305
+ ```
306
+ # wuwei: <the ceiling this accepts>, <what should trigger revisiting it>
307
+ ```
308
+
309
+ ```python
310
+ # wuwei: no retry here — the caller retries the whole batch.
311
+ # add one the first time this is called outside that path.
312
+ ```
313
+
314
+ Two fields, both required. **A marker with no trigger is the one that
315
+ rots**, and step 8 does not pass with one.
316
+
317
+ **Commit the cuts here, before step 5** — step 5's diff is three-dot, so
318
+ cuts left in the working tree are reviewed by nobody while every lens reads
319
+ code that no longer exists. **The bound e2e does not run here**; it runs at
320
+ the round's close, covering this round's cuts and fixes together.
321
+
322
+ 5. **Review panel** — run the panel(s) `wuwei-review` §1 classifies this
323
+ phase's diff into: the code panel always, **and the document panel over any
324
+ prose the phase changed** — a phase that ships an endpoint and its docs has
325
+ both, and §1 calls that normal rather than an edge case. `wuwei-review` owns
326
+ the lenses,
327
+ the tool precedence, the three-dot diff, and the collation rules; this
328
+ step supplies the phase context and decides what happens to the result.
329
+
330
+ **Base sync first.** Every graph question the panel asks is about the tree
331
+ that exists *after* this merges — unsynced, "every call site updated" can
332
+ be true here and false the moment it lands.
333
+
334
+ **Then `wuwei review-prep <base>`** (`wuwei-review` §1) — the dispatcher
335
+ checks and the scan bundle; name the bundle's path in the dispatch — one
336
+ crawl for the whole panel, rebuilt whenever the reviewed sha moves.
337
+
338
+ Then dispatch with five things: the phase file, the path to
339
+ `ACCEPTANCE.md`, the **base ref just synced to**, the **tool facts**
340
+ (`wuwei-review` §1, §5), and **the scope** (`wuwei-review` §6) — plus, for the
341
+ document panel, **the intended reader and the facts this phase
342
+ establishes** (`wuwei-review` §3). Models: as `wuwei claim` printed them
343
+ (`wuwei-review` §2).
344
+
345
+ **Round 1 is full scope; every round after it is delta** — dispatched with
346
+ the sha the previous round's panel was reviewed at, that round's still-open
347
+ and `contested` findings, **its `unread:` scopes**, and the phase's full
348
+ range as something a lens may widen to. The unread scopes have to be passed
349
+ explicitly and **read before the delta**: they are unchanged code by
350
+ definition, so nothing else in a delta round would bring them back. Rounds 2 and 3 otherwise pay four lenses to re-read code four
351
+ lenses already read.
352
+
353
+ **Full scope comes back whenever the delta cannot be trusted**, and
354
+ `wuwei-review` §6 lists when: the sync above actually moved the base, or the
355
+ recorded sha no longer resolves. Both are one command to check and neither
356
+ is a judgement call — **not checking is how a delta silently reviews the
357
+ wrong range.**
358
+
359
+ **The dispatch asks for the same thing every round** — the delta and the
360
+ carried findings. A standing order for fresh probes voids the convergence
361
+ count; widening is chosen once and recorded in the digest header
362
+ (`wuwei-review` §6).
363
+
364
+ **A panel whose delta holds nothing for it is skipped, and the skip is
365
+ recorded** — in the digest header, which already asks for any lens that did
366
+ not run and why; same as step 4, an unrecorded skip is indistinguishable
367
+ from a step that was forgotten. Concretely: on a delta round the document
368
+ panel does not dispatch when the delta contains no prose, **and** the
369
+ previous digest carries no open document findings, **and it declared no
370
+ `unread:` scope** — a blind spot the panel located itself is the last thing
371
+ a skip may swallow. A lens shown nothing can
372
+ find nothing, so nothing is given up — except one seam, named with the
373
+ skip: a code-only fix can falsify prose it never touched, and until the
374
+ stream audit only `review:defects`' prose-inside-code rule watches it.
375
+ Round 1 is untouched by this — it runs full scope, where §1's
376
+ classification already decides which panels exist at all. Inside a phase the panel has two obligations it does not
377
+ have standalone:
378
+
379
+ - **Diff the architecture delta against the phase's `## Intended
380
+ design`** and state the verdict — built as designed, or diverged with
381
+ the reason. Divergence is allowed but never silent; divergence that
382
+ changes what was *signed off* rather than how it was built is an
383
+ escalation, not a footnote.
384
+ - **Reconcile `ACCEPTANCE.md`'s `## Threats`** — no evidence means still
385
+ open.
386
+
387
+ Two dimensions the panel weighs **every round**, because they are what the
388
+ rounds exist for:
389
+
390
+ - **logical necessity** — does this need to exist at all? Not "is it
391
+ correct" but "is it load-bearing". A correct thing nobody needs is next
392
+ round's `yagni:`, not a finding to close.
393
+ - **human readability** — can a person read this without reconstructing
394
+ it? Naming that lies, a function that needs its own diagram to follow,
395
+ an abstraction whose only reader is its author.
396
+
397
+ Append the digest to the phase file — it is what a human reads instead of
398
+ the raw diff. Each round appends its own; the sequence is the convergence
399
+ record step 6 reads.
400
+
401
+ 6. **Close the round.** Fix what step 5 found — **verify it first, then fix
402
+ at the cause, not where it was reported.** Every finding carries its own
403
+ recipe (`Fails when`): run it before touching anything — or, for a finding
404
+ with nothing to run (a `reuse:`, a naming claim), check the claimed fact
405
+ against the tree. It fails as described → real, go find the cause. It does
406
+ not → the finding closes as `false-positive`, recorded in the round record
407
+ with **what was run, what was observed, and the premise the closure
408
+ assumed** — evidence, not argument, since
409
+ prose against prose is how appeasement comes back — and it counts as a
410
+ closure in the convergence table below. The next round's panel reads the
411
+ round record and can re-raise; that is the check on closing too eagerly.
412
+
413
+ A real finding is then fixed at the cause: a lens read the diff, so its
414
+ coordinate is where the defect became visible; the cause can sit in a file
415
+ it never opened. Locate it first, and where it is not already proven that
416
+ is `wuwei-debug-root-cause` — the skill is not only for red tests, and a
417
+ finding whose cause is a guess is the same situation.
418
+
419
+ **The finding disappearing is not the test; the cause being gone is.** One
420
+ closed by widening a type, wrapping the call in a `try`, deleting the
421
+ assertion that caught it, or a local linter-disable is hidden, not fixed —
422
+ and the next round's panel reads the suppression as intended code.
423
+
424
+ **When the proven cause is this phase's own earlier work — a task commit,
425
+ or an earlier round — the fix goes into that code**, in this round's fix
426
+ commit (never amended into a task commit; see below). Nothing has merged yet, so
427
+ this is the cheapest it will ever be, while a compensating layer on top is a
428
+ second floor on a foundation the panel just called wrong — usually more
429
+ lines than the change it avoids. Layer on top only when the cause sits
430
+ outside this phase, and say so in the digest. A cause inside what was signed
431
+ off is an escalation (`## Invariants`), not a local workaround.
432
+
433
+ Then **run the bound e2e** — red → `wuwei-debug-root-cause` — and **commit
434
+ the fixes, one per concern.** Step 2's rules govern these exactly as they
435
+ govern task commits: one concern each, no forward dependency, and a body
436
+ saying why this shape. Step 4's cuts are already their own commit.
437
+ **Nothing is squashed at the round, and nothing is amended into a task
438
+ commit** — a phase reads as the sequence that produced it, which is worth
439
+ reading only because every commit in it is atomic.
440
+
441
+ **Close the round with its record, appended to the phase file** next to
442
+ step 5's digest:
443
+
444
+ ```
445
+ round <n> · scope: <full | delta from <sha>> · reviewed at <sha>
446
+
447
+ - cut: <what step 4 removed>. net: -<N> lines
448
+ - found: <N> findings — <N> HIGH / <N> MED / <N> LOW
449
+ - fixed: <one line per concern — the cause, where it differs from where
450
+ the finding was reported> · premise: <what the closure assumed>
451
+ - e2e: <bound items> green @ <sha>
452
+ - verify: <pages> @ <sha> · <N> flags, each fixed or answered
453
+ ```
454
+
455
+ **The record goes in the phase file, not only in a commit body.** The phase
456
+ file merges whichever way the phase does — and where sign-off chose squash,
457
+ step 7 makes any round commit body unreadable the moment the phase lands.
458
+
459
+ **Findings must converge.** From round 2 on, compare `HIGH + MED` against
460
+ the previous round's digest: strictly fewer, or the round did not converge.
461
+ A new LOW is not non-convergence — a fresh lens turning up something small
462
+ is what it is for.
463
+
464
+ **Under delta scope the count is carried + new**, never only what this
465
+ round's dispatch happened to be shown. A carried HIGH leaves the count when
466
+ **its fix, its recorded falsification, or its supersession was observed**
467
+ (`wuwei-review` §6), not when a
468
+ round that was never shown it stayed quiet — otherwise the number falls because the scope shrank, and every rule
469
+ in this table starts measuring the dispatch instead of the code.
470
+
471
+ | after round | state | do |
472
+ |---|---|---|
473
+ | 1 | **every lens that ran found nothing, any severity** | **exit the loop** — nothing to converge means the diff was trivial, and the panel said so itself rather than being told |
474
+ | 1 or 2 | otherwise | next round |
475
+ | ≥ 3 | no HIGH, no MED | **exit the loop** |
476
+ | ≥ 3 | converging, HIGH or MED still open | next round |
477
+ | ≥ 2 | **`HIGH + MED` did not fall** | **stop and escalate**, recorded in the phase file |
478
+
479
+ The round-1 exit is the only way out before three — **every lens that ran**,
480
+ zero findings, not a size guess about the diff, and a mixed phase runs two
481
+ panels so it needs both clean. A phase author cannot declare its own diff
482
+ trivial; the panel can, because it is the one thing here that is not
483
+ grading its own work.
484
+
485
+ **Non-convergence is information, never something to fix by finding less.**
486
+ A phase that cannot converge is mis-scoped, and grinding another round is
487
+ how a panel learns to report less.
488
+
489
+ **A contested finding is not a failure to converge.** Two lenses reaching
490
+ different severities on the same subject — `review:claims` calling a
491
+ documented pattern HIGH where `audit:security` finds no reachable sink — is
492
+ a disagreement, and rounds cannot settle it: neither lens is wrong within
493
+ its own mandate, so grinding produces the same two rows every time. Mark it
494
+ `contested`, **leave it out of the `HIGH + MED` convergence count**, and
495
+ **escalate it by name** — it is a human's call, and recording it as
496
+ "mis-scoped phase" hides which question is actually open.
497
+
498
+ A finding is `contested` only when **reporters rated the same subject
499
+ differently** — one row whose Sev cell records each (`wuwei-review` §7). One
500
+ lens with a finding nobody disputed is not contested; it is open.
501
+
502
+ On exit from the loop:
503
+
504
+ - **Run the repo's own suite once** — every e2e run so far measured only
505
+ this phase's bound items, so nothing has yet asked whether it broke a
506
+ test that was already there. Use the command the repo already uses
507
+ (`package.json` scripts, `Makefile`, the CI workflow); red →
508
+ `wuwei-debug-root-cause`. **No discoverable command → record "suite not
509
+ run" in the phase file.**
510
+ - **Phase evidence** in the phase file, as the phase's last commit, naming
511
+ the number of rounds and the convergence sequence. Push.
512
+
513
+ **`STATE.md` is not touched from a phase branch** — one global file, one
514
+ row per stream, and `phases done/total` is a single cell two parallel
515
+ phases would conflict on. The aggregate is **derived on the stream branch
516
+ by whoever merges the phase PR**, in its own commit.
517
+
518
+ 7. **PR** against the stream branch from `## Sign-off`, **ready, not draft**
519
+ (draft means "still changing" — use it only when opening early to run CI).
520
+ **Say which delivery strategy this phase sits under**, so a reviewer does
521
+ not judge an `mvp-first` intermediate phase as if it were finished.
522
+
523
+ **Phase PRs merge into the stream branch unsquashed by default — a merge
524
+ commit — and the stream PR merges into main unsquashed too.** The phase is
525
+ still the rollback unit: `git revert -m 1 <merge-sha>` takes one back in a
526
+ single command, and the commits that produced it stay readable underneath.
527
+ **Squashing is the human's call at sign-off, never this step's** — it
528
+ trades every atomic commit for one, which is what a repo demanding linear
529
+ history buys with.
530
+
531
+ **Take the merge method from `## Sign-off`, never from what the button
532
+ offers** — sign-off recorded which method this stream uses and what the
533
+ repo allows. Using a fallback is fine; using it silently is not, so name it
534
+ in the PR.
535
+
536
+ **Squash chosen → the squash commit body lists the commit subjects, in
537
+ order**, because those commits go unreachable and `wuwei-tdd` settles "did
538
+ the test exist before the implementation" from history order. Merged
539
+ unsquashed, the history answers that itself and no listing is owed.
540
+
541
+ - `review: auto` + evidence green, in auto mode → merge into the stream
542
+ branch, by the method in `## Sign-off`.
543
+ - `review: human` → hand the digest over and wait. **The wait ends on the
544
+ last review's body, not on `reviewDecision`** — an approve can carry a
545
+ condition ("docs first, then merge"), and reviews live at
546
+ `/pulls/:n/reviews` and `/pulls/:n/comments`, not with the issue
547
+ comments; every review's findings enter the round record like the
548
+ panel's own. **A commit after the approve gets `wuwei-review` §6's
549
+ post-approve scope before the merge — dispatched to a fresh
550
+ `review:defects`**, never run by this session, which wrote the code.
551
+ - Whole stream done → **base sync once more, re-run the full e2e**, then
552
+ **hand over to `wuwei-review`'s stream panel**, then open the stream PR
553
+ to main.
554
+
555
+ **Say the audit is available, every time, including in auto mode.** It is
556
+ the only gate positioned to ask whether the delivery matches the contract
557
+ that authorised it (`wuwei-review` §4, `references/stream.md`, says what each of its lenses sees
558
+ that no phase review can). **A gate nobody knows about is not a gate.**
559
+
560
+ ```
561
+ stream pwa-redesign · 5/5 phases merged · e2e green
562
+
563
+ before the PR to main, run the stream audit:
564
+ /wuwei-review --stream # contract · coverage · composed security · composition
565
+ ```
566
+
567
+ The stream PR is ready, carries the accumulated digests, the audit, and the
568
+ UAT checklist, **names the ref synced to** (the UAT is evidence about that
569
+ tree and no other), and states whether the composed result still matches
570
+ `## Target architecture`.
571
+
572
+ **Tracker**, subject to step 1's write rule:
573
+
574
+ | granularity | on PR open | on merge |
575
+ |---|---|---|
576
+ | `per-phase` | phase issue → review state | → done state |
577
+ | `single` | tick that phase's box in the stream issue | **never transition the issue** — closing the stream's only ticket because phase 01 of 5 landed is the destructive version of this feature |
578
+
579
+ A `review: human` phase, and the stream PR into main, merge after this
580
+ session hands over — **say at sign-off who owns those final moves**, or the
581
+ board stops one step short with nobody owning it.
582
+
583
+ 8. **Verify.** **At least three rounds, or exactly one if it exited on the
584
+ all-zero rule**, each closed by a green bound e2e, and the convergence
585
+ sequence recorded — a round with no digest is a round that did not happen;
586
+ **every `contested` finding named and escalated**, never counted as
587
+ non-convergence; **the repo's suite run once after the last code change, or
588
+ "suite not run" recorded with the reason**;
589
+ **every round's digest records the scope it ran at and the sha it reviewed,
590
+ round 1 ran full, and any panel skipped on an empty delta is recorded with
591
+ its reason**; **every `unread:` scope a digest declared was read by a later
592
+ round or carried into the phase evidence as still unread**; `STATE.md`'s row for this stream matches the phase
593
+ files, updated on the stream branch; **every `wuwei:` marker this phase
594
+ added names both a ceiling and a trigger**; **no commit in this phase
595
+ depends on one that lands after it**; every round's fixes name a
596
+ located cause rather than a suppression; every tracker transition
597
+ attempted recorded with its outcome, failures in the digest — done against
598
+ a board that never moved must not pass; no red branch left behind
599
+ silently.
600
+
601
+ ## Notes
602
+
603
+ - Idea credit: superpowers subagent-driven development + the owner's
604
+ pipeline. Step 4's tag vocabulary and net-lines metric: ponytail's
605
+ `ponytail-review`; step 2's ladder and step 4's floor: ponytail's `ponytail`.
606
+ Rewritten for wuwei.
607
+ - The `review: human` asymmetry is the design: the safe thing costs two
608
+ tokens, the risky thing costs a sentence.
609
+ - SDD's value is freshness — each task starts clean instead of inheriting the
610
+ debris of every task before it, which is what makes late tasks in a long
611
+ phase drift.
612
+ - A `wuwei:` marker is the only decision a project keeps that outlives its
613
+ stream: SPEC and phase digests close when the stream does, the reason a
614
+ function has no retry does not.
615
+ - **One command rolls a phase back, and which command is the merge method's
616
+ doing**: `revert -m 1` for a merge commit (the default), plain `revert` for
617
+ a squash, and rebase gives neither. None of them makes a phase
618
+ *independent* — that is cut at planning time (`wuwei-plan-phases` step 4),
619
+ and recorded in `## Sign-off` because the person reverting is holding an
620
+ incident, not reading the plan.
621
+ - Tracker writes are outward-facing and can fail. A failure never blocks the
622
+ phase, but it goes in the digest and step 8 gates on it.
623
+ - Entering main is always a human decision; phase → stream-branch merges ride
624
+ the sign-off authorisation.