devflow-kit 3.6.0 → 3.7.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 (80) hide show
  1. package/CHANGELOG.md +80 -1
  2. package/README.md +6 -2
  3. package/dist/agents/code.md +7 -11
  4. package/dist/agents/fix.md +5 -6
  5. package/dist/agents/refactor.md +5 -6
  6. package/dist/agents/review.md +2 -0
  7. package/dist/cli/commands/gates.js +276 -0
  8. package/dist/cli/commands/init-seed.js +9 -1
  9. package/dist/cli/commands/init.js +31 -8
  10. package/dist/cli/commands/memory.js +18 -0
  11. package/dist/cli.js +2 -0
  12. package/dist/commands/bug-analysis.md +2 -2
  13. package/dist/commands/code-review.md +52 -44
  14. package/dist/commands/debug.md +2 -2
  15. package/dist/commands/dynamic-build.md +53 -61
  16. package/dist/commands/dynamic-plan.md +2 -2
  17. package/dist/commands/dynamic-profile.md +2 -2
  18. package/dist/commands/dynamic-tickets.md +2 -2
  19. package/dist/commands/explore.md +2 -2
  20. package/dist/commands/implement.md +26 -31
  21. package/dist/commands/plan.md +15 -25
  22. package/dist/commands/release.md +2 -2
  23. package/dist/commands/research.md +2 -2
  24. package/dist/commands/resolve.md +2 -2
  25. package/dist/commands/self-review.md +2 -2
  26. package/dist/core/echo-value.js +42 -0
  27. package/dist/core/evidence-policy.js +17 -5
  28. package/dist/core/gate-level.js +213 -0
  29. package/dist/core/manifest.js +26 -10
  30. package/dist/core/model-discovery.js +7 -1
  31. package/dist/core/tracker.js +5 -33
  32. package/dist/learning-off/agents/code.md +7 -11
  33. package/dist/learning-off/agents/fix.md +5 -6
  34. package/dist/learning-off/agents/refactor.md +5 -6
  35. package/dist/learning-off/agents/review.md +2 -0
  36. package/dist/learning-off/commands/code-review.md +52 -44
  37. package/dist/learning-off/commands/debug.md +2 -2
  38. package/dist/learning-off/commands/dynamic-build.md +53 -61
  39. package/dist/learning-off/commands/explore.md +2 -2
  40. package/dist/learning-off/commands/implement.md +26 -31
  41. package/dist/learning-off/commands/plan.md +15 -25
  42. package/dist/learning-off/commands/resolve.md +2 -2
  43. package/dist/learning-off/commands/self-review.md +2 -2
  44. package/dist/skills/git/references/tracker/github/ensure-pr-ready.md +7 -9
  45. package/dist/skills/git/references/tracker/jira/ensure-pr-ready.md +7 -15
  46. package/dist/skills/git/references/tracker/linear/ensure-pr-ready.md +7 -15
  47. package/dist/targets/claude-code/learning-install.js +15 -3
  48. package/package.json +1 -1
  49. package/src/assets/agents/_partials/_code.mds +5 -6
  50. package/src/assets/agents/code.mds +2 -5
  51. package/src/assets/agents/curate.md +4 -6
  52. package/src/assets/agents/review.mds +2 -0
  53. package/src/assets/agents/synthesize.md +2 -2
  54. package/src/assets/commands/_partials/_settings.mds +2 -2
  55. package/src/assets/commands/_partials/_wave.mds +10 -17
  56. package/src/assets/commands/code-review.mds +50 -42
  57. package/src/assets/commands/dynamic-build.mds +41 -42
  58. package/src/assets/commands/implement.mds +24 -29
  59. package/src/assets/commands/plan.mds +14 -24
  60. package/src/assets/mds/git/_ops_steps.mds +3 -3
  61. package/src/assets/mds/tracker/_common.mds +23 -12
  62. package/src/assets/mds/tracker/_github.mds +2 -9
  63. package/src/assets/mds/tracker/_jira.mds +2 -15
  64. package/src/assets/mds/tracker/_linear.mds +2 -15
  65. package/src/assets/scripts/ci-wait.cjs +22 -4
  66. package/src/assets/scripts/claude-md-audit.cjs +66 -51
  67. package/src/assets/scripts/hooks/background-memory-update +404 -95
  68. package/src/assets/scripts/hooks/json-helper.cjs +11 -0
  69. package/src/assets/scripts/hooks/json-parse +18 -3
  70. package/src/assets/scripts/hooks/memory-permission +358 -0
  71. package/src/assets/scripts/hooks/session-start-context +2 -2
  72. package/src/assets/scripts/hooks/session-start-memory +26 -5
  73. package/src/assets/scripts/lib/project-config.cjs +19 -4
  74. package/src/assets/scripts/resolve-settings.cjs +200 -32
  75. package/src/assets/skills/agent-references/SKILL.md +6 -2
  76. package/src/assets/skills/agent-references/references/commands/code-review-levels.md +19 -0
  77. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +2 -2
  78. package/src/assets/skills/agent-references/references/commands/plan-levels.md +40 -0
  79. package/src/assets/skills/agent-references/references/synthesize/planning.md +9 -22
  80. package/src/assets/skills/agent-references/references/synthesize/review.md +11 -11
@@ -35,17 +35,17 @@ Run a comprehensive code review of the current branch by spawning parallel revie
35
35
  - **Exactly one listed:** load neither the skill nor the reference — that worktree is the only candidate, and Step 0c's Git agent validates its branch
36
36
  2. **If `--path` flag provided:** use only that worktree, skip discovery
37
37
  **`--path` validation**: Before proceeding, verify the path exists as a directory and appears in `git worktree list` output. If not: report error and stop.
38
- 3. **If only 1 reviewable worktree** (the common case): proceed as single-worktree flow — zero behavior change
38
+ 3. **If only 1 reviewable worktree**: proceed as single-worktree flow — zero behavior change
39
39
  4. **If multiple reviewable worktrees:** report "Found N worktrees with reviewable branches: {list with paths and branches}" and proceed with multi-worktree flow
40
40
 
41
- #### Step 0b: Resolve the compliance lens
41
+ #### Step 0b: Resolve the compliance lens and gate level
42
42
 
43
- **Produces:** COMPLIANCE_ACTIVE, COMPLIANCE_FRAMEWORKS
43
+ **Produces:** COMPLIANCE_ACTIVE, COMPLIANCE_FRAMEWORKS, GATE_LEVEL
44
44
 
45
45
  {{settings.settings_resolve()}}
46
46
 
47
47
  {{compliance_gate()}}
48
- Keep each worktree's values for every downstream phase of that worktree.
48
+ Keep each worktree's values for every downstream phase.
49
49
 
50
50
  #### Step 0b-ii: Resolve the evidence policy
51
51
 
@@ -87,7 +87,7 @@ Validate branch, commit if needed, push, create PR if needed.
87
87
  Return: branch, base_branch, branch-slug, PR#"
88
88
  ```
89
89
 
90
- In multi-worktree mode, spawn all pre-flight agents **in a single message** (parallel).
90
+ In multi-worktree mode, spawn all pre-flight agents **in a single message**.
91
91
 
92
92
  **If BLOCKED:** In single-worktree mode, stop and report. In multi-worktree mode, report the failure but continue with other worktrees.
93
93
 
@@ -127,7 +127,7 @@ For each worktree, perform a single pass over timestamped directories:
127
127
  2. Iterate once: accumulate CYCLE_NUMBER count for each directory containing `resolution-summary.md`; capture the first (most-recent) such directory as PRIOR_DIR.
128
128
  3. If CYCLE_NUMBER = 0: set PRIOR_RESOLUTIONS=(none), CYCLE_NUMBER=1, proceed.
129
129
  4. Otherwise: set CYCLE_NUMBER = count + 1. Read `{PRIOR_DIR}/resolution-summary.md` as PRIOR_RESOLUTIONS.
130
- 5. If `--full`: still load PRIOR_RESOLUTIONS (valuable for Review agent cross-cycle awareness).
130
+ 5. If `--full`: still load PRIOR_RESOLUTIONS.
131
131
 
132
132
  #### Step 0e-ii: Convergence Assessment
133
133
 
@@ -169,7 +169,7 @@ For each reviewable worktree, call:
169
169
  ### Phase 1: Analyze Changed Files
170
170
 
171
171
  **Produces:** REVIEW_FOCUS_LIST, DIFF_CLASS, DIFF_FILES
172
- **Requires:** DIFF_RANGE, REVIEW_DIR, COMPLIANCE_ACTIVE (from Step 0b)
172
+ **Requires:** DIFF_RANGE, REVIEW_DIR, COMPLIANCE_ACTIVE, GATE_LEVEL (from Step 0b)
173
173
 
174
174
  Per worktree, in order. Each `git` command here writes to a file and prints nothing.
175
175
 
@@ -185,7 +185,7 @@ git -C "{WORKTREE_PATH}" diff --name-only --no-renames {DIFF_RANGE} > "{REVIEW_D
185
185
  wc -l < "{REVIEW_DIR}/paths.txt"
186
186
  ```
187
187
 
188
- Read `paths.txt` once when the count is about 40 or fewer, and in ranges of about 40 lines (Read with offset and limit) when it is more. `--no-renames` lists a moved file under its old path and its new one, so a move cannot take a file out of its own class.
188
+ Read `paths.txt` once when the count is about 40 or fewer, and in ranges of about 40 lines (Read with offset and limit) when it is more.
189
189
 
190
190
  Set `DIFF_CLASS` by these rules, in order; the first that decides wins:
191
191
 
@@ -202,7 +202,7 @@ Each reduced class runs only its focus set, every member reading `diff.patch`:
202
202
  - `tests-only`: testing, reliability, consistency, security
203
203
  - `lockfile-only`: dependencies, security
204
204
 
205
- A mixed non-code diff runs the union of the sets of the classes it holds. Language and conditional triggers inside a reduced class are suppressed: they add no focus, and Step 1.2 applies to the `code` class only.
205
+ A mixed non-code diff runs the union of the sets of the classes it holds. Language and conditional triggers inside a reduced class are suppressed.
206
206
 
207
207
  #### Step 1.2: Choose the focuses
208
208
 
@@ -226,13 +226,27 @@ When `DIFF_CLASS` is `code`, read the file types from the paths in `paths.txt` t
226
226
  | Docs or significant code | documentation |
227
227
  | COMPLIANCE_ACTIVE AND diff touches regulated surface | compliance |
228
228
 
229
- If `COMPLIANCE_ACTIVE` AND the diff touches regulated surface (data models, auth flows, logging/observability, payments, IaC, retention): add `compliance` to REVIEW_FOCUS_LIST for this worktree.
229
+ If `COMPLIANCE_ACTIVE` AND the diff touches regulated surface (data models, auth flows, logging/observability, payments, IaC, retention): on a `code` diff, add `compliance`.
230
230
 
231
231
  **Language focus stamp.** The eight language focuses — `typescript`, `react`, `accessibility`, `ui-design`, `go`, `java`, `python`, `rust` — ship with optional plugins, and the installer stamps the ones installed on this machine onto this line, rewriting it on every install and on `uninstall --plugin`:
232
232
 
233
233
  Installed language focuses: (none)
234
234
 
235
- A language focus is spawned only when its file-type condition above fires AND its name appears in that stamped line. A focus missing from the line is never spawned, even when its trigger fires. The eight core focuses are unconditional and are never stamp-gated.
235
+ A language focus is spawned only when its file-type condition above fires AND its name appears in that stamped line. A focus missing from the line is never spawned, even when its trigger fires. On a `code` diff, REVIEW_FOCUS_LIST starts with the eight core focuses. Step 1.2b may narrow them; they are never stamp-gated.
236
+
237
+ #### Step 1.2b: Narrow by gate level
238
+
239
+ **Produces:** REVIEW_FOCUS_LIST, GATE_LEVEL
240
+ **Requires:** REVIEW_FOCUS_LIST, DIFF_CLASS, CHANGED_PATHS, GATE_LEVEL
241
+
242
+ **Gate level.** Narrow REVIEW_FOCUS_LIST, for every class, by this worktree's `GATE_LEVEL`. `full` keeps it and reads nothing.
243
+
244
+ At `standard` or `lean`, once per run, run `d="${CLAUDE_CONFIG_DIR:-}"; case "$d" in /*) ;; *) d="$HOME/.claude" ;; esac; printf '%s\n' "$d"` in Bash and Read `<that directory>/skills/devflow:agent-references/references/commands/code-review-levels.md` with the Read tool.
245
+
246
+ - Apply each class's row to that class's own set, then take the union of the results. A row only removes focuses.
247
+ - If the Read fails, or a row is missing or malformed, set `GATE_LEVEL` to `full` for every worktree of this run and say `level reference unreadable: full sets applied`.
248
+ - Security runs at every level and in every class.
249
+ - Compliance keeps the trigger above at every level.
236
250
 
237
251
  #### Step 1.3: Write the diff files
238
252
 
@@ -297,9 +311,9 @@ Pass `FEATURE_KNOWLEDGE` to all Review agents.
297
311
  **Requires:** DIFF_RANGE, REVIEW_DIR, TIMESTAMP, FEATURE_KNOWLEDGE, PR_DESCRIPTION, PRIOR_RESOLUTIONS, REVIEW_FOCUS_LIST, DIFF_CLASS, DIFF_FILES
298
312
  <!-- learning:end -->
299
313
 
300
- Spawn Review agents **in a single message**, for the focus set `DIFF_CLASS` selects (max 20 per worktree):
314
+ Spawn Review agents **in a single message**, one per focus in REVIEW_FOCUS_LIST (max 20 per worktree):
301
315
 
302
- - `code`: the 8 core reviews on the full `diff.patch`, plus the language and conditional focuses Phase 1 added.
316
+ - `code`: the 8 core reviews on the full `diff.patch` (lean keeps 3), plus the language and conditional focuses Phase 1 added.
303
317
  - `docs-only`, `tests-only`, `lockfile-only`: only the reduced set Phase 1 names, each reading `diff.patch`; a mixed non-code diff runs the union. No language or conditional focus is spawned, whatever its trigger.
304
318
 
305
319
  | Focus | Runs | Pattern Skill | DIFF_FILE |
@@ -325,7 +339,7 @@ Spawn Review agents **in a single message**, for the focus set `DIFF_CLASS` sele
325
339
  | documentation | conditional | devflow:documentation | diff.patch |
326
340
  | compliance | diff-driven | devflow:compliance | diff.patch |
327
341
 
328
- Review agent count: `code` runs 8 core, +1-12 added by Phase 1 (max 20 total per worktree); a reduced class or a mix runs 2-6.
342
+ Review agent count: at `full`, `code` runs 8 core +1-12; `standard` runs up to 2 fewer; lean `code` runs 3-4; a reduced class or a mix runs 2-6.
329
343
 
330
344
  {{compliance_focus_inject()}}
331
345
 
@@ -353,16 +367,16 @@ Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE — feature-specifi
353
367
  IMPORTANT: Write report to {worktree_path}/.devflow/docs/reviews/{branch-slug}/{timestamp}/{focus}.md using Write tool"
354
368
  ```
355
369
 
356
- In multi-worktree mode, process worktrees **sequentially** (one worktree at a time). Complete Phases 1-4 for each worktree before starting the next. This prevents agent overload — spawning 8-20 Review agents per worktree across multiple worktrees simultaneously overwhelms the system.
370
+ In multi-worktree mode, process worktrees **sequentially**: complete Phases 1-4 for one worktree before starting the next, so Review spawns never pile up.
357
371
 
358
372
  ### Phase 3: Synthesis → Review Comment (Sequential)
359
373
 
360
374
  **Produces:** REVIEW_SUMMARY, PR_COMMENT
361
- **Requires:** REVIEW_FOCUS_OUTPUTS, REVIEW_DIR, PR_INFO
375
+ **Requires:** REVIEW_FOCUS_OUTPUTS, REVIEW_DIR, PR_INFO, GATE_LEVEL, REVIEW_FOCUS_LIST
362
376
 
363
377
  **WAIT** for Phase 2. For each worktree, run steps 3a and 3b in sequence:
364
378
 
365
- **Step 3a — Synthesize agent** per worktree (wait for completion before 3b):
379
+ **Step 3a — Synthesize agent** per worktree:
366
380
  ```
367
381
  Agent(subagent_type="Synthesize", run_in_background=false):
368
382
  "Mode: review
@@ -370,6 +384,8 @@ WORKTREE_PATH: {worktree_path} (omit if cwd)
370
384
  REVIEW_BASE_DIR: {worktree_path}/.devflow/docs/reviews/{branch-slug}/{timestamp}
371
385
  TIMESTAMP: {timestamp}
372
386
  CYCLE_NUMBER: {cycle_number}
387
+ GATE_LEVEL: {GATE_LEVEL}
388
+ FOCUSES: {REVIEW_FOCUS_LIST}
373
389
  PRIOR_RESOLUTIONS: <prior-resolution-summary>{prior_resolutions}</prior-resolution-summary>
374
390
  Include Convergence Status section in review-summary.md.
375
391
  Aggregate findings, determine merge recommendation.
@@ -402,8 +418,6 @@ Per worktree, after successful completion:
402
418
  - Publication status: FULL (private repo) | FULL (config override) | STUB (public repository) | OFF (publication disabled by config) | STUB (visibility undeterminable) | STUB (evidence policy) (from Git agent)
403
419
  - Artifact paths
404
420
 
405
- In multi-worktree mode, report results per worktree.
406
-
407
421
  ## Architecture
408
422
 
409
423
  ```
@@ -411,29 +425,27 @@ In multi-worktree mode, report results per worktree.
411
425
  │
412
426
  ├─ Phase 0: Worktree Discovery & Pre-flight
413
427
  │ ├─ Step 0a: git worktree list → filter reviewable
414
- │ ├─ Step 0b: Resolve the compliance lens
428
+ │ ├─ Step 0b: Resolve the compliance lens and gate level
415
429
  │ ├─ Step 0c: Git agent (ensure-pr-ready) per worktree [parallel]
416
430
  │ ├─ Step 0d: Incremental detection + timestamp setup per worktree
417
431
  │ ├─ Step 0e-i: Load prior resolution-summary.md
418
- │ ├─ Step 0e-ii: Convergence assessment (warn if FP ratio > 70%)
432
+ │ ├─ Step 0e-ii: Convergence assessment (warn only)
419
433
  │ └─ Step 0f: Resolve REVIEW_PUBLICATION per worktree (publication gate)
420
434
  │
421
435
  ├─ Per worktree (SEQUENTIAL — one worktree at a time):
422
436
  │ │
423
437
  │ ├─ Phase 1: Analyze changed files
424
438
  │ │ ├─ Classify the diff (paths.txt → DIFF_CLASS)
425
- │ │ ├─ Choose the focuses (code class: file types + the language stamp)
439
+ │ │ ├─ Choose the focuses (code: file types + the language stamp; all: the gate level)
426
440
  │ │ └─ Write diff.patch and diff-{focus}.patch, by redirect
427
441
  │ │
428
- │ ├─ Phase 2: Reviews (PARALLEL within worktree)
429
- │ │ ├─ code: Review agents security, architecture, performance, complexity,
430
- │ │ │ consistency, regression, testing, reliability (diff.patch)
431
- │ │ ├─ code: + language (diff-{focus}.patch) and conditional Review agents
442
+ │ ├─ Phase 2: Reviews (PARALLEL within worktree), one per REVIEW_FOCUS_LIST focus
443
+ │ │ ├─ code: core (diff.patch), language (diff-{focus}.patch), conditional
432
444
  │ │ └─ docs-only / tests-only / lockfile-only: the reduced set (diff.patch)
433
445
  │ │
434
446
  │ ├─ Phase 3: Synthesis → Review Comment (SEQUENTIAL within worktree)
435
447
  │ │ ├─ Step 3a: Synthesize agent (mode: review, writes review-summary.md)
436
- │ │ └─ Step 3b: Git agent (post-review-summary, D7 marker-dedup, REVIEW_PUBLICATION)
448
+ │ │ └─ Step 3b: Git agent (post-review-summary)
437
449
  │ │
438
450
  │ └─ Phase 4: Write .last-review-head + display results
439
451
  ```
@@ -442,31 +454,27 @@ In multi-worktree mode, report results per worktree.
442
454
 
443
455
  | Case | Handling |
444
456
  |------|----------|
445
- | `--full` in multi-worktree mode | Applies to all worktrees (global modifier) |
457
+ | `--full` in multi-worktree mode | Applies to all worktrees (global modifier). It sets the diff range, never the gate level |
446
458
  | Review comment already posted | Git agent matches its own marker on the `{cycle, timestamp}` pair → skip silently (D7); a re-review in the same cycle (different timestamp) posts its own comment. The marker's format belongs to the operation; this caller passes the pair and never restates the literal |
447
- | Concurrent sessions | Advisory only, each session computes independently |
448
- | Docs-only diff | `docs-only`: documentation, consistency, security on `diff.patch`; no language or conditional focus |
449
- | Tests-only diff | `tests-only`: testing, reliability, consistency, security on `diff.patch`; no language or conditional focus |
450
- | Lockfile-only diff | `lockfile-only`: dependencies, security on `diff.patch` |
451
- | Mixed non-code diff (docs with tests, tests with a lockfile) | The union of the matching sets, on `diff.patch` |
452
- | A path matching no class pattern, or an empty path list | `code` (fail-safe): the 8 core focuses on the full diff |
453
- | A file moved across classes | `--no-renames` lists the old and the new path, so a code path makes the diff `code` |
454
- | Path listing above about 40 lines | The count is printed; `paths.txt` is read in ranges of about 40 lines |
459
+ | Docs-only diff | Its Step 1.1 set, narrowed by level |
460
+ | Tests-only diff | Its Step 1.1 set, narrowed by level |
461
+ | Lockfile-only diff | Its Step 1.1 set, narrowed by level |
462
+ | Mixed non-code diff | The union of the matching sets |
463
+ | A path matching no class pattern, or an empty path list | `code` (fail-safe) |
464
+ | A file moved across classes | `--no-renames` lists both paths, so a code path makes the diff `code` |
465
+ | Path listing above about 40 lines | Read in ranges (Step 1.1) |
455
466
  | Language files changed, plugin not installed | The focus is absent from the stamped line: not spawned |
456
467
  | Public repo (`auto` mode) | Git agent probes visibility, posts counts-only STUB comment; full report stays local |
457
468
  | `reviewPublication: off` | Git agent skips posting the review comment (OFF) — except that, only when `EVIDENCE_POLICY` is `required`, Step 0f passes `stub` and a counts-only STUB comment posts |
458
469
 
459
470
  ## Backwards Compatibility
460
471
 
461
- - **Single worktree**: Auto-discovery finds only one worktree → proceeds exactly as before. Zero behavior change.
462
- - **Legacy flat layout**: If `{worktree}/.devflow/docs/reviews/{branch-slug}/` contains flat `*.md` files (no timestamped subdirectories), new runs create timestamped subdirectories. Old flat files remain untouched.
472
+ - **Gate level**: `full` runs the sets named here; the default `standard` and `lean` narrow them. `devflow gates --set full` keeps them.
463
473
 
464
474
  ## Principles
465
475
 
466
476
  1. **Orchestration only** - Command spawns agents, doesn't do git/review work itself. The exceptions are the read-only Phase 0 probes (`git worktree list`, `git -C ... branch --show-current`, `git -C ... cat-file -t`), the bounded Phase 1 path listing (a redirect into `paths.txt`, a `wc -l` count and ranged reads) and the redirect-only patch writes (`diff.patch`, `diff-{focus}.patch`). Redirect-only writes print nothing, so no diff content reaches this thread
467
477
  2. **Parallel, not background** - Multiple agents in one message, but `run_in_background=false` so phases complete before proceeding
468
478
  3. **Git agent for git work** - All git operations go through Git agent, except the exceptions named in Principle 1; redirect-only writes print nothing
469
- 4. **Clear ownership** - Each agent owns its output completely
470
- 5. **Honest reporting** - Display agent outputs directly
471
- 6. **Incremental by default** - Only review new changes unless `--full` specified
472
- 7. **Auto-discover worktrees** - One command handles all reviewable branches
479
+ 4. **Honest reporting** - Display agent outputs directly
480
+ 5. **Incremental by default** - Only review new changes unless `--full` specified
@@ -575,22 +575,49 @@ When WAVE mode is detected, author a workflow that wraps the single-ticket engin
575
575
 
576
576
  **Wave workflow structure (author after the SINGLE engine blocks above):**
577
577
 
578
- The wave workflow uses the same phases as SINGLE but wraps them in a wave loop. The integration branch is `wave/<initiative>` — the initiative slug, referenced below as `{slug}` — (or the user's current branch). Each ticket works on the branch setup-task created. The Git agent manages worktrees for parallel-eligible tickets. After every merge the workflow spawns the Validate agent (build + test) and undoes a red merge. Escalations accumulate in a list; the final report lists all of them.
578
+ The wave workflow uses the same phases as SINGLE but wraps them in a wave loop. The integration branch is `wave/<initiative>` — the initiative slug, referenced below as `{slug}` — (or the user's current branch).
579
579
 
580
580
  Wave skeleton — compact reference (see `wave_loop()` doctrine for full semantics):
581
581
 
582
582
  ```js
583
- // Wave round loop: Design agent reader → per-ticket try/catch → cascade quarantine via next reader
584
583
  // runSingleTicketEngine(args) is the SINGLE skeleton above as a function of its OWN args: the wave's
585
584
  // args.issueNumber (the tracking issue, Pre-authoring step 5) is never in its scope.
586
585
  const WAVE_TICKETS = [...remainingTickets]; // the pre-fetch's refs, in input order — the wave block's row order
587
- const results = {}; // ticketId → its row of the workflow's return; a ticket with no entry never ran
586
+ const results = {}; // ticketId → its row of the workflow's return
588
587
  // A row from an engine result: the fields step 3 after the workflow renders
589
588
  const rowOf = (ticketId, r, merged, treeEqual = null) => ({ ticket: ticketId, ran: true, verdict: r?.verdict || r?.overallVerdict || null, merged, treeEqual, issuePrLink: r?.issuePrLink || "(none)", evaluateVerdict: r?.gate2?.evaluateVerdict, testVerdict: r?.gate2?.testVerdict, surviving: r?.survivingFindings?.length, coverageComplete: r?.reviewCoverage?.complete });
590
589
  const MAX_ROUNDS = Math.max(10, remainingTickets.length * 2 + 5); // heuristic; always finite
591
- const MERGE_SHA = /^[0-9a-f]{40}$/; // a merge commit as git prints it: the only Git-reported value that reaches a later prompt
590
+ const MERGE_SHA = /^[0-9a-f]{40}$/; // a SHA as git prints it; with the checked branch, the only Git-reported values that reach a merge, Validate or undo prompt
591
+ const BRANCH_NAME = /^(?![-#]|(?:@|(?:FETCH_|ORIG_|MERGE_)?HEAD)$)(?!.*\.\.)[A-Za-z0-9._/+@,#-]{1,200}$/; // implement's ref allowlist; no leading - or #, no revision name, no ..: the name enters git lines
592
+ // One ticket's merge, post-merge Validate and undo (D-WAVE-MERGE-VALIDATE: Git validates nothing).
593
+ const mergeValidateUndo = async (ticketId, engineResult) => {
594
+ if (typeof engineResult.branch !== "string" || !BRANCH_NAME.test(engineResult.branch)) return { kept: false, reason: "ticket branch name refused: not a plain git ref name", halted: null, treeEqual: null };
595
+ const merge = await agent(`Merge ${engineResult.branch} to ${INTEGRATION_BRANCH} locally. Include ticket ID ${ticketId} in the merge commit message. Do not push, and run no build or test.
596
+ Check out ${INTEGRATION_BRANCH}, run git rev-parse ${INTEGRATION_BRANCH} as baseSha, run git rev-parse --verify --quiet refs/heads/${engineResult.branch} (on a non-zero exit return merged false, reason "no such local branch"), then run git merge --no-ff refs/heads/${engineResult.branch}. If the merge stops after it began (git rev-parse -q --verify MERGE_HEAD succeeds), run git merge --abort; if that abort fails, return merged true with no mergeSha. A merge that did not begin returns merged false with the reason.
597
+ Return: {"merged": true, "mergeSha": "<40-hex merge commit>", "treeEqual": <true when mergeSha^{tree} equals the tree of ${engineResult.branch}'s head>, "baseSha": "<40-hex integration HEAD before the merge>"} — or {"merged": false, "reason": "<why>"} when no merge is kept.`, { agentType: "Git" });
598
+ const treeEqual = merge?.merged === true && typeof merge.treeEqual === "boolean" ? merge.treeEqual : null;
599
+ const done = (kept, reason, halted = null) => ({ kept, reason, halted, treeEqual });
600
+ if (merge?.merged !== true) return done(false, merge?.reason || "merge failed");
601
+ const badSha = ![merge.mergeSha, merge.baseSha].every(v => MERGE_SHA.test(String(v)));
602
+ if (badSha || merge.mergeSha === merge.baseSha) {
603
+ // No usable SHA, or no commit added: stop taking merges. Fixed reasons; no rejected value is copied.
604
+ const reason = badSha ? "merge reported without a 40-hex mergeSha or baseSha; the integration branch may hold an unvalidated merge" : "merge added no commit, so the wave stopped: an unchanged HEAD cannot be checked. Check that the ticket branch holds its commits, then run the wave again.";
605
+ return done(false, reason, { ticket: ticketId, mergeSha: null, baseSha: null, reason, kind: badSha ? "bad-sha" : "unchanged" });
606
+ }
607
+ const post = await agent(`Run build, typecheck, lint, and tests on ${INTEGRATION_BRANCH} after merging ${engineResult.branch} (merge commit ${merge.mergeSha}).
608
+ Follow your Running commands block for every build and test command.
609
+ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
610
+ if (post?.verdict === "PASS") {
611
+ return done(true, "");
612
+ }
613
+ const undo = await agent(`Undo the merge ${merge.mergeSha} on ${INTEGRATION_BRANCH}, only when no remote branch contains ${merge.mergeSha}. Run these read-only checks in order; a non-zero exit or a different value means not undone: git symbolic-ref --short HEAD must print ${INTEGRATION_BRANCH}; git rev-parse refs/heads/${INTEGRATION_BRANCH} must print ${merge.mergeSha}; git rev-parse ${merge.mergeSha}^1 must print ${merge.baseSha}. Then run exactly this one command and no other that changes a branch or a remote: git reset --keep ${merge.baseSha}
614
+ Return: {"undone": true, "head": "<40-hex from git rev-parse refs/heads/${INTEGRATION_BRANCH}>"} — or {"undone": false, "reason": "<why>"} when you did not undo it.`, { agentType: "Git" });
615
+ if (undo?.undone === true && undo.head === merge.baseSha) return done(false, "post-merge build red; merge undone");
616
+ const reason = `post-merge build red; merge not undone (${undo?.undone === true ? "undo reported a head other than the recorded pre-merge SHA" : undo?.reason || "no reason given"})`;
617
+ return done(false, reason, { ticket: ticketId, mergeSha: merge.mergeSha, baseSha: merge.baseSha, reason, kind: "red" });
618
+ };
592
619
  const waveState = { quarantined: [], round: 0, halted: null };
593
- let reAskedThisDeadlock = false; // re-ask guard: re-ask once on empty ready-set, then escalate
620
+ let reAskedThisDeadlock = false;
594
621
  while (remainingTickets.length > 0 && waveState.round < MAX_ROUNDS && waveState.halted === null) {
595
622
  waveState.round++;
596
623
 
@@ -614,56 +641,28 @@ Return: {"ready": [...ticket-ids], "blocked": [{"ticket": "id", "namedBlocker":
614
641
  reAskedThisDeadlock = true; // re-ask once with vacuous-truth rule quoted verbatim
615
642
  continue;
616
643
  }
617
- reAskedThisDeadlock = false; // reset on a non-empty ready set
644
+ reAskedThisDeadlock = false;
618
645
 
619
646
  for (const ticketId of ready) {
620
647
  try {
621
- // ticketId is the ISSUE_REF the pre-fetch heading printed — this ticket's OWN reference: the engine's `ticket` (its TICKET)
622
- // and its setup-task ISSUE_INPUT; the engine returns the branch that setup-task created, merged below; plans[ticketId] is its own plan, criteria and checked testPlan (Pre-authoring step 4)
648
+ // ticketId is this ticket's OWN ISSUE_REF (pre-fetch heading): the engine's `ticket` and setup-task ISSUE_INPUT; the engine returns the branch setup-task created, merged below; plans[ticketId] is its own plan (Pre-authoring step 4)
623
649
  <!-- learning:on -->
624
650
  const engineResult = await runSingleTicketEngine({ ticket: ticketId, baseBranch: INTEGRATION_BRANCH, ...(plans[ticketId] || {}), decisionsContext: DECISIONS_CONTEXT, issueRequired: ISSUE_REQUIRED, applyConventions: APPLY_CONVENTIONS, complianceFrameworks: COMPLIANCE_FRAMEWORKS, issueInput: ticketId });
625
651
  <!-- learning:off -->
626
652
  const engineResult = await runSingleTicketEngine({ ticket: ticketId, baseBranch: INTEGRATION_BRANCH, ...(plans[ticketId] || {}), issueRequired: ISSUE_REQUIRED, applyConventions: APPLY_CONVENTIONS, complianceFrameworks: COMPLIANCE_FRAMEWORKS, issueInput: ticketId });
627
653
  <!-- learning:end -->
628
- // Check both verdict (engine_output_schema) and overallVerdict (SINGLE skeleton alias).
629
- // PASS and UNVERIFIED merge; PARTIAL, FAIL, ESCALATED (the ticket-link and branch stops included) or no verdict quarantine.
654
+ // PASS and UNVERIFIED merge (verdict, or overallVerdict as the SINGLE alias); PARTIAL, FAIL, ESCALATED (the ticket-link and branch stops included) or no verdict quarantine.
630
655
  if (["PASS", "UNVERIFIED"].includes(engineResult.verdict || engineResult.overallVerdict)) {
631
- // Git merges locally and validates nothing: the workflow owns the post-merge Validate (D-WAVE-MERGE-VALIDATE).
632
- const merge = await agent(`Merge ${engineResult.branch} to ${INTEGRATION_BRANCH} locally. Include ticket ID ${ticketId} in the merge commit message. Do not push, and run no build or test.
633
- Return: {"merged": true, "mergeSha": "<40-hex merge commit>", "treeEqual": <true when mergeSha^{tree} equals the tree of ${engineResult.branch}'s head>} — or {"merged": false, "reason": "<why>"} when no merge is kept.`, { agentType: "Git" });
634
- let kept = false;
635
- let reason = merge?.reason || "merge failed";
636
- if (merge?.merged === true && !MERGE_SHA.test(String(merge.mergeSha))) {
637
- // A merge may sit on the integration branch with no SHA to validate or undo it by: stop taking merges.
638
- waveState.halted = { ticket: ticketId, mergeSha: null, reason: "merge reported without a 40-hex mergeSha; the integration branch may hold an unvalidated merge" };
639
- reason = waveState.halted.reason;
640
- } else if (merge?.merged === true) {
641
- // A merge counts as kept only when this Validate returns PASS. It always runs: treeEqual is recorded, never a reason to skip.
642
- const post = await agent(`Run build, typecheck, lint, and tests on ${INTEGRATION_BRANCH} after merging ${engineResult.branch} (merge commit ${merge.mergeSha}).
643
- Follow your Running commands block for every build and test command.
644
- Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate" });
645
- if (post?.verdict === "PASS") {
646
- kept = true;
647
- } else {
648
- const undo = await agent(`Undo the merge ${merge.mergeSha} on ${INTEGRATION_BRANCH}, only when no remote branch contains ${merge.mergeSha} and the HEAD of ${INTEGRATION_BRANCH} still equals ${merge.mergeSha}. On the integration branch run exactly this one command and no other that changes a branch or a remote: git reset --keep ${merge.mergeSha}^1
649
- Return: {"undone": true} — or {"undone": false, "reason": "<why>"} when you did not undo it.`, { agentType: "Git" });
650
- if (undo?.undone === true) {
651
- reason = "post-merge build red; merge undone";
652
- } else {
653
- // A merge that stays red would poison every later ticket: stop taking merges and rounds, and name the red HEAD.
654
- reason = `post-merge build red; merge not undone (${undo?.reason || "no reason given"})`;
655
- waveState.halted = { ticket: ticketId, mergeSha: merge.mergeSha, reason };
656
- }
657
- }
658
- }
659
- results[ticketId] = rowOf(ticketId, engineResult, kept, merge?.merged === true && typeof merge.treeEqual === "boolean" ? merge.treeEqual : null);
656
+ const { kept, reason, halted, treeEqual } = await mergeValidateUndo(ticketId, engineResult);
657
+ if (halted !== null) waveState.halted = halted;
658
+ results[ticketId] = rowOf(ticketId, engineResult, kept, treeEqual);
660
659
  if (!kept) waveState.quarantined.push({ ticket: ticketId, reason });
661
660
  } else {
662
661
  results[ticketId] = rowOf(ticketId, engineResult, false);
663
662
  waveState.quarantined.push({ ticket: ticketId, reason: engineResult.escalations?.[0]?.description || "engine fail/escalated" });
664
663
  }
665
664
  } catch (err) {
666
- // One ticket's crash/stall never kills the wave — quarantine it; cascade propagates to dependents via the next reader round
665
+ // Quarantine the ticket: the cascade reaches its dependents via the next reader round
667
666
  results[ticketId] = rowOf(ticketId, null, false);
668
667
  waveState.quarantined.push({ ticket: ticketId, reason: `engine crash: ${String(err)}` });
669
668
  }
@@ -697,7 +696,7 @@ A workflow cannot pause mid-run. After the build/wave workflow returns, you (the
697
696
  In WAVE mode, if no tracking-issue number was resolved in Pre-authoring step 5: state `TRACEABILITY: DEGRADED (no tracking issue for this run)` in the run summary and skip — never skip silently.
698
697
  3. **Compose the wave PR inputs** (WAVE mode only — skip this step entirely in SINGLE mode). The workflow returned `tickets`: one entry per wave ticket, in its input order, each `{ticket, ran, verdict, merged, issuePrLink, evaluateVerdict, testVerdict, surviving, coverageComplete}`. Every value in it is agent-reported and treated as untrusted: nothing below is repaired, and no reference is ever composed from a number.
699
698
  - **Branch check.** Run `git -C "{integration worktree root}" branch --show-current`. Only a name matching `^wave/[a-z0-9][a-z0-9-]{0,59}$` opens a wave PR, and its part after `wave/` is the `{slug}` below. Any other name: record `TRACEABILITY: DEGRADED (not a wave branch)` and go to step 4 with no wave PR.
700
- - **Halted** (the workflow returned a non-null `halted`: a post-merge build it could not undo, or a merge reported with no SHA): the integration HEAD may be red. Record `Wave PR: skipped (integration HEAD red, wave halted at <halted.ticket>)`, name `halted.mergeSha` (or `none reported`) and `halted.reason` as the red integration HEAD in the run summary, and go to step 4 with no wave PR.
699
+ - **Halted** (a non-null `halted`, whose `kind` is `red`, `bad-sha` or `unchanged`): record `Wave PR: skipped (<text>, wave halted at <halted.ticket>)` and go to step 4 with no wave PR. `<text>` is fixed by `kind`, and no other `halted` field is shown. `red` (a post-merge build it could not undo): `integration HEAD red at <halted.mergeSha>`. `bad-sha` (a merge without a usable SHA): `integration branch may hold an unvalidated merge`. `unchanged` (a merge that added no commit): `merge added no commit`, with the remedy to check that the ticket branch holds its commits, then run the wave again.
701
700
  - **Nothing merged** (no entry has `merged: true`): record `Wave PR: skipped (nothing merged)` and go to step 4.
702
701
  - Otherwise compose (a) and then (b).
703
702
 
@@ -807,4 +806,4 @@ cd "{integration worktree root}" && node "$HOME/.devflow/scripts/verify-evidence
807
806
 
808
807
  ### Maintenance note
809
808
 
810
- This recipe encodes the current `/implement` + `/code-review` + `/resolve` orchestration shape as of the authoring date (2026-06-12). When those base commands change their orchestration, update this recipe to match. No tooling detects drift — by design (the LLM-vs-plumbing Iron Rule). The reminder lives in the design doc §16.
809
+ This recipe mirrors the `/implement` + `/code-review` + `/resolve` orchestration as of 2026-06-12. When those commands change, update it. No tooling detects drift (the LLM-vs-plumbing Iron Rule).
@@ -46,7 +46,7 @@ $ARGUMENTS
46
46
 
47
47
  ### Re-validation Continuation Path
48
48
 
49
- When the user explicitly asks to re-validate, re-check, or re-run quality gates after making their own changes, resolve `${CLAUDE_CONFIG_DIR:-$HOME/.claude}` in Bash, Read `<that directory>/skills/devflow:agent-references/references/commands/implement-revalidate.md` with the Read tool and follow it. Otherwise proceed with the full pipeline below.
49
+ When the user explicitly asks to re-validate, re-check, or re-run quality gates after making their own changes, run `d="${CLAUDE_CONFIG_DIR:-}"; case "$d" in /*) ;; *) d="$HOME/.claude" ;; esac; printf '%s\n' "$d"` in Bash, Read `<that directory>/skills/devflow:agent-references/references/commands/implement-revalidate.md` with the Read tool and follow it. Otherwise proceed with the full pipeline below.
50
50
 
51
51
  ### Phase 1: Setup
52
52
 
@@ -56,7 +56,7 @@ When the user explicitly asks to re-validate, re-check, or re-run quality gates
56
56
  **Produces:** TASK_ID, BASE_BRANCH, EXECUTION_PLAN, FEATURE_KNOWLEDGE, FEATURE_KNOWLEDGE_RULES, PR_DESCRIPTION_GUIDANCE, ISSUE_NUMBER, ISSUE_PR_LINK, EVIDENCE_POLICY, ISSUE_REQUIRED, APPLY_CONVENTIONS, REQUIRE_NON_AUTHOR_APPROVAL, PR_EXCEPTIONS, TEST_PLAN, EVIDENCE_FILE, PR_TEST_PLAN_BLOCK, REVIEW_PUBLICATION
57
57
  <!-- learning:end -->
58
58
 
59
- Record the current branch name as `BASE_BRANCH` - this will be the PR target.
59
+ Record the current branch name as `BASE_BRANCH`. Stop the run unless it matches `^[A-Za-z0-9._/+@,#-]{1,200}$`, telling the user it allows only ASCII letters, digits and `._/+@,#-`, at most 200.
60
60
 
61
61
  {{docs_root()}}
62
62
 
@@ -114,11 +114,6 @@ The two tests this command does make are its own under every provider:
114
114
  `setup-task` fetches whatever it is handed — so the command would derive a
115
115
  branch from a failed lookup and drop the request on the floor.
116
116
 
117
- The cost of the count gate is a one-word task description (`/implement refactor`)
118
- reaching `setup-task` as an issue reference, where it fails the lookup and is
119
- reported. That is the direction the failure has to fall: an unfetched issue is
120
- visible, a silently discarded request is not.
121
-
122
117
  **Capture from Git agent output** (used throughout flow):
123
118
  - `TASK_ID`: The branch name created by Git agent (use as TASK_ID for rest of flow)
124
119
  - `BASE_BRANCH`: Branch this feature was created from (for PR target)
@@ -127,7 +122,7 @@ visible, a silently discarded request is not.
127
122
 
128
123
  {{issue_capture_contract()}}
129
124
 
130
- **Ticket link, only when `ISSUE_REQUIRED` is `true`:** when the capture above holds no `ISSUE_PR_LINK` (absent, or `(none)`), resolve `${CLAUDE_CONFIG_DIR:-$HOME/.claude}` in Bash, Read `<that directory>/skills/devflow:agent-references/references/commands/implement-evidence-asks.md` with the Read tool (once per run), then run its ticket link ask and **Record the exception at once** as it directs, before any Code spawn. `PR_EXCEPTIONS` is `(none)` until an exception is recorded.
125
+ **Ticket link, only when `ISSUE_REQUIRED` is `true`:** when the capture above holds no `ISSUE_PR_LINK` (absent, or `(none)`), run `d="${CLAUDE_CONFIG_DIR:-}"; case "$d" in /*) ;; *) d="$HOME/.claude" ;; esac; printf '%s\n' "$d"` in Bash, Read `<that directory>/skills/devflow:agent-references/references/commands/implement-evidence-asks.md` with the Read tool (once per run), then run its ticket link ask and **Record the exception at once** as it directs, before any Code spawn. `PR_EXCEPTIONS` is `(none)` until an exception is recorded.
131
126
 
132
127
  **Test plan.** Before any Code spawn, give the task a test plan in the evidence file `{worktree}/.devflow/docs/evidence-{branch_slug}.md` (`EVIDENCE_FILE`) — unlike the handoff file, it stays after the PR exists. It holds up to three sections, in this order and nothing else: `## Test Plan`; `## Evidence Exceptions`, a byte copy of `PR_EXCEPTIONS` present only while that is not `(none)`; and `## Claims`, always last, so every claim is appended at the end of the file. Create the file if absent; if it exists, replace its `## Test Plan` and `## Evidence Exceptions` sections and keep `## Claims` byte-identical.
133
128
 
@@ -181,14 +176,14 @@ Pass `DECISIONS_CONTEXT` to Code agent (Phase 2) and Harden agent (Phase 4).
181
176
  Based on Setup context (plan document, issue body, or conversation context), use the three-strategy framework:
182
177
 
183
178
  **Strategy Selection**:
184
- - If plan document provided: use `execution-strategy` from frontmatter (default: SINGLE_CODE_AGENT if absent)
185
- - Otherwise: default to SINGLE_CODE_AGENT unless task description signals high complexity
179
+ - If plan document provided: use `execution-strategy` from frontmatter (if absent, use the table below)
180
+ - Otherwise: SINGLE_CODE_AGENT, because there is no Subtask Breakdown to split
186
181
 
187
- | Strategy | When | Frequency |
188
- |----------|------|-----------|
189
- | **SINGLE_CODE_AGENT** | Default. Coherent A→Z implementation | ~80% |
190
- | **SEQUENTIAL_CODE_AGENTS** | Context overflow risk, layered dependencies | ~15% |
191
- | **PARALLEL_CODE_AGENTS** | True artifact independence (rare) | ~5% |
182
+ | Strategy | When |
183
+ |----------|------|
184
+ | **SINGLE_CODE_AGENT** | Default. Coherent A→Z implementation |
185
+ | **SEQUENTIAL_CODE_AGENTS** | The plan writes >= 10 files or has >= 7 steps |
186
+ | **PARALLEL_CODE_AGENTS** | True artifact independence (rare) |
192
187
 
193
188
  ---
194
189
 
@@ -211,7 +206,7 @@ DECISIONS_CONTEXT: {decisions_context}
211
206
 
212
207
  ---
213
208
 
214
- **SEQUENTIAL_CODE_AGENTS** (for HIGH/CRITICAL context risk):
209
+ **SEQUENTIAL_CODE_AGENTS** (>= 10 files or >= 7 steps):
215
210
 
216
211
  Spawn Code agents one at a time. Each appends its own phase section to the handoff file, and the next reads only the section before it:
217
212
 
@@ -298,15 +293,15 @@ DECISIONS_CONTEXT: {decisions_context}
298
293
  ### Phase 3: Polish
299
294
 
300
295
  **Produces:** POLISH_OUTPUT
301
- **Requires:** FILES_CHANGED
296
+ **Requires:** BASE_BRANCH
302
297
 
303
- After the Code agent completes, spawn Polish agent to polish the code. The Phase 6 Validate covers its commits.
298
+ After the Code agent completes, spawn Polish agent to polish the code. The Phase 6 Validate covers its commits. `{branch_diff}` is the output of `git diff --name-only --diff-filter=d {BASE_BRANCH}...HEAD`, run again at each spawn.
304
299
 
305
300
  ```
306
301
  Agent(subagent_type="Polish"):
307
302
  "Polish recently implemented code
308
303
  Task: {task description}
309
- FILES_CHANGED: {list of files from Code agent output}
304
+ FILES_CHANGED: {branch_diff}
310
305
  Focus on code modified by Code agent, apply project standards, enhance clarity
311
306
  Commit any improvements with a conventional-commit message."
312
307
  ```
@@ -314,14 +309,14 @@ Commit any improvements with a conventional-commit message."
314
309
  ### Phase 4: Self-Review
315
310
 
316
311
  **Produces:** HARDEN_OUTPUT
317
- **Requires:** FILES_CHANGED
312
+ **Requires:** BASE_BRANCH
318
313
 
319
314
  After Polish agent completes, spawn Harden agent as the quality gate:
320
315
 
321
316
  ```
322
317
  Agent(subagent_type="Harden"):
323
318
  "TASK_DESCRIPTION: {task description}
324
- FILES_CHANGED: {list of files from Code agent output}
319
+ FILES_CHANGED: {branch_diff}
325
320
  <!-- learning:on -->
326
321
  DECISIONS_CONTEXT: {decisions_context}
327
322
  <!-- learning:end -->
@@ -334,7 +329,7 @@ Harden agent reports `### Status: PASS | FIXED | BLOCKED`. **If BLOCKED:** repor
334
329
  ### Phase 5: Alignment Check
335
330
 
336
331
  **Produces:** ALIGNMENT_RESULT
337
- **Requires:** FILES_CHANGED, EXECUTION_PLAN
332
+ **Requires:** BASE_BRANCH, EXECUTION_PLAN
338
333
 
339
334
  After Harden agent passes, spawn Evaluate agent to validate alignment. Evaluate agent receives `FEATURE_KNOWLEDGE_RULES` as acceptance context only; pattern and anti-pattern judgments belong to Harden agent:
340
335
 
@@ -342,7 +337,7 @@ After Harden agent passes, spawn Evaluate agent to validate alignment. Evaluate
342
337
  Agent(subagent_type="Evaluate"):
343
338
  "ORIGINAL_REQUEST: {task description or issue content}
344
339
  EXECUTION_PLAN: {execution plan from Phase 1}
345
- FILES_CHANGED: {list of files from Code agent output}
340
+ FILES_CHANGED: {branch_diff}
346
341
  ACCEPTANCE_CRITERIA: {extracted criteria if available}
347
342
  FEATURE_KNOWLEDGE: {feature_knowledge_rules}
348
343
  Validate alignment with request and plan. Report ALIGNED or MISALIGNED with details."
@@ -375,11 +370,11 @@ Validate alignment with request and plan. Report ALIGNED or MISALIGNED with deta
375
370
  **Produces:** VALIDATION_RESULT, VALIDATED_HEAD
376
371
  **Requires:** BASE_BRANCH
377
372
 
378
- Validate takes the one full-suite slot, after every commit it must cover: Code, Polish, Harden and the alignment fixes. After Evaluate agent passes, spawn Validate agent. `FILES_CHANGED` is the branch diff, because the Code agent's own list misses the Polish, Harden and fix commits (`{base}` is `BASE_BRANCH`):
373
+ Validate takes the one full-suite slot, after every commit it must cover: Code, Polish, Harden and the alignment fixes. After Evaluate agent passes, spawn Validate agent. `FILES_CHANGED` is the branch diff:
379
374
 
380
375
  ```
381
376
  Agent(subagent_type="Validate"):
382
- "FILES_CHANGED: {output of `git diff --name-only {base}...HEAD`}
377
+ "FILES_CHANGED: {branch_diff}
383
378
  VALIDATION_SCOPE: full
384
379
  Run build, typecheck, lint, test. Report pass/fail with failure details."
385
380
  ```
@@ -420,7 +415,7 @@ Run build, typecheck, lint, test. Report pass/fail with failure details."
420
415
  ### Phase 7: QA Testing
421
416
 
422
417
  **Produces:** QA_RESULT
423
- **Requires:** FILES_CHANGED, EXECUTION_PLAN, TEST_PLAN, VALIDATED_HEAD
418
+ **Requires:** BASE_BRANCH, EXECUTION_PLAN, TEST_PLAN, VALIDATED_HEAD
424
419
 
425
420
  After Phase 6 passes, spawn Test agent for scenario-based acceptance testing:
426
421
 
@@ -428,7 +423,7 @@ After Phase 6 passes, spawn Test agent for scenario-based acceptance testing:
428
423
  Agent(subagent_type="Test"):
429
424
  "ORIGINAL_REQUEST: {task description or issue content}
430
425
  EXECUTION_PLAN: {execution plan from Phase 1}
431
- FILES_CHANGED: {list of files from Code agent output}
426
+ FILES_CHANGED: {branch_diff}
432
427
  ACCEPTANCE_CRITERIA: {extracted criteria if available}
433
428
  TEST_PLAN: {the TP lines of the evidence file's ## Test Plan section, or (none)}
434
429
  Design and execute scenario-based acceptance tests. Report PASS or FAIL with evidence."
@@ -467,7 +462,7 @@ Run this phase only if a qa-fix committed (`git rev-parse HEAD` differs from `VA
467
462
 
468
463
  ```
469
464
  Agent(subagent_type="Validate"):
470
- "FILES_CHANGED: {output of `git diff --name-only VALIDATED_HEAD..HEAD`}
465
+ "FILES_CHANGED: {output of `git diff --name-only --diff-filter=d VALIDATED_HEAD..HEAD`}
471
466
  VALIDATION_SCOPE: changed-only
472
467
  Verify the qa-fix commits did not break anything."
473
468
  ```
@@ -575,7 +570,7 @@ If Phase 1 recorded an evidence exception, show its `## Evidence Exceptions` lin
575
570
  /implement (orchestrator - spawns agents only)
576
571
  │
577
572
  ├─ Re-validation Path (when user says "re-validate"/"re-check"/"re-run gates")
578
- │ └─ Branch safety → skip Phase 2 → detect FILES_CHANGED → Phases 3-8 → Phase 9, 11, 12
573
+ │ └─ Branch safety → skip Phase 2 → record BASE_BRANCH → Phases 3-8 → Phase 9, 11, 12
579
574
  │
580
575
  ├─ Phase 1: Setup
581
576
  ├─ Phase 2: Implement (3-strategy framework)