devflow-kit 3.9.0 → 3.10.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 (157) hide show
  1. package/CHANGELOG.md +111 -0
  2. package/README.md +1 -1
  3. package/dist/agents/code.md +6 -6
  4. package/dist/agents/fix.md +6 -6
  5. package/dist/agents/harden.md +7 -6
  6. package/dist/agents/refactor.md +6 -6
  7. package/dist/cli/commands/ambient.js +10 -2
  8. package/dist/cli/commands/compliance-prompts.js +9 -4
  9. package/dist/cli/commands/compliance.js +171 -24
  10. package/dist/cli/commands/curation.js +12 -7
  11. package/dist/cli/commands/gate-level-prompts.js +39 -25
  12. package/dist/cli/commands/gates.js +5 -7
  13. package/dist/cli/commands/init.js +142 -138
  14. package/dist/cli/commands/install-report.js +34 -0
  15. package/dist/cli/commands/legacy-sweep.js +64 -0
  16. package/dist/cli/commands/proxy.js +6 -1
  17. package/dist/cli/commands/rules.js +29 -12
  18. package/dist/cli/commands/skills.js +42 -9
  19. package/dist/cli/commands/tracker.js +9 -3
  20. package/dist/cli/commands/uninstall.js +277 -51
  21. package/dist/commands/bug-analysis.md +69 -120
  22. package/dist/commands/dynamic-build.md +23 -23
  23. package/dist/commands/implement.md +46 -32
  24. package/dist/commands/release.md +1 -1
  25. package/dist/commands/resolve.md +35 -31
  26. package/dist/commands/self-review.md +8 -7
  27. package/dist/compliance-off/agents/git.md +120 -0
  28. package/dist/compliance-off/agents/triage.md +180 -0
  29. package/dist/compliance-off/commands/bug-analysis.md +398 -0
  30. package/dist/compliance-off/commands/code-review.md +520 -0
  31. package/dist/compliance-off/commands/dynamic-build.md +1251 -0
  32. package/dist/compliance-off/commands/dynamic-tickets.md +676 -0
  33. package/dist/compliance-off/commands/implement.md +736 -0
  34. package/dist/compliance-off/commands/plan.md +671 -0
  35. package/dist/compliance-off/commands/release.md +309 -0
  36. package/dist/compliance-off/commands/resolve.md +742 -0
  37. package/dist/compliance-off/skills/git/references/decision-markers.md +17 -0
  38. package/dist/compliance-off/skills/git/references/learn-conventions.md +80 -0
  39. package/dist/compliance-off/skills/git/references/pr/check-ci-status.md +33 -0
  40. package/dist/compliance-off/skills/git/references/pr/check-merge-readiness.md +49 -0
  41. package/dist/compliance-off/skills/git/references/pr/fetch-review-threads.md +52 -0
  42. package/dist/compliance-off/skills/git/references/pr/validate-branch.md +47 -0
  43. package/dist/compliance-off/skills/git/references/tracker/_contract.md +33 -0
  44. package/dist/compliance-off/skills/git/references/tracker/_mcp.md +153 -0
  45. package/dist/compliance-off/skills/git/references/tracker/github/create-release.md +35 -0
  46. package/dist/compliance-off/skills/git/references/tracker/github/ensure-pr-ready.md +100 -0
  47. package/dist/compliance-off/skills/git/references/tracker/github/ensure-traceable-issue.md +74 -0
  48. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issue.md +69 -0
  49. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issues-batch.md +65 -0
  50. package/dist/compliance-off/skills/git/references/tracker/github/gather-release-evidence.md +48 -0
  51. package/dist/compliance-off/skills/git/references/tracker/github/setup-task.md +77 -0
  52. package/dist/compliance-off/skills/git/references/tracker/jira/create-release.md +35 -0
  53. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-pr-ready.md +100 -0
  54. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-traceable-issue.md +61 -0
  55. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issue.md +51 -0
  56. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issues-batch.md +63 -0
  57. package/dist/compliance-off/skills/git/references/tracker/jira/gather-release-evidence.md +47 -0
  58. package/dist/compliance-off/skills/git/references/tracker/jira/setup-task.md +82 -0
  59. package/dist/compliance-off/skills/git/references/tracker/linear/create-release.md +35 -0
  60. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-pr-ready.md +100 -0
  61. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-traceable-issue.md +61 -0
  62. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issue.md +51 -0
  63. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issues-batch.md +63 -0
  64. package/dist/compliance-off/skills/git/references/tracker/linear/gather-release-evidence.md +47 -0
  65. package/dist/compliance-off/skills/git/references/tracker/linear/setup-task.md +83 -0
  66. package/dist/compliance-off/skills/git/references/trust-rule.md +7 -0
  67. package/dist/core/agent-models.js +19 -4
  68. package/dist/core/assets.js +126 -17
  69. package/dist/core/compliance-off-files.js +27 -0
  70. package/dist/core/curation-variants.js +460 -112
  71. package/dist/core/flags.js +2 -2
  72. package/dist/core/installed-variants.js +63 -0
  73. package/dist/core/manifest.js +48 -5
  74. package/dist/core/mds-variants.js +700 -3
  75. package/dist/core/orphan-sweep.js +11 -2
  76. package/dist/core/plugins.js +65 -8
  77. package/dist/curation-compliance-off/agents/triage.md +176 -0
  78. package/dist/curation-compliance-off/commands/bug-analysis.md +349 -0
  79. package/dist/curation-compliance-off/commands/code-review.md +481 -0
  80. package/dist/curation-compliance-off/commands/dynamic-build.md +1211 -0
  81. package/dist/curation-compliance-off/commands/dynamic-tickets.md +634 -0
  82. package/dist/curation-compliance-off/commands/implement.md +689 -0
  83. package/dist/curation-compliance-off/commands/plan.md +631 -0
  84. package/dist/curation-compliance-off/commands/release.md +279 -0
  85. package/dist/curation-compliance-off/commands/resolve.md +692 -0
  86. package/dist/curation-off/agents/code.md +6 -6
  87. package/dist/curation-off/agents/fix.md +6 -6
  88. package/dist/curation-off/agents/harden.md +7 -6
  89. package/dist/curation-off/agents/refactor.md +6 -6
  90. package/dist/curation-off/commands/bug-analysis.md +69 -119
  91. package/dist/curation-off/commands/dynamic-build.md +23 -23
  92. package/dist/curation-off/commands/implement.md +46 -32
  93. package/dist/curation-off/commands/release.md +1 -1
  94. package/dist/curation-off/commands/resolve.md +35 -31
  95. package/dist/curation-off/commands/self-review.md +8 -7
  96. package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
  97. package/dist/targets/claude-code/behavior-install.js +23 -9
  98. package/dist/targets/claude-code/charter-render.js +374 -0
  99. package/dist/targets/claude-code/compliance-install.js +32 -12
  100. package/dist/targets/claude-code/compliance-off-files.js +245 -0
  101. package/dist/targets/claude-code/curation-install.js +570 -100
  102. package/dist/targets/claude-code/installer.js +467 -62
  103. package/dist/targets/claude-code/language-stamp.js +6 -0
  104. package/dist/targets/claude-code/linked-folder.js +182 -0
  105. package/dist/targets/claude-code/reference-converge.js +118 -0
  106. package/dist/targets/claude-code/tracker-install.js +21 -8
  107. package/dist/targets/claude-code/tracker-servers.js +20 -6
  108. package/dist/targets/claude-code/workflow-folder-install.js +231 -0
  109. package/dist/variant-axes.json +109 -0
  110. package/package.json +1 -1
  111. package/src/assets/agents/_partials/_code.mds +6 -6
  112. package/src/assets/agents/evaluate.md +1 -1
  113. package/src/assets/agents/git.mds +32 -0
  114. package/src/assets/agents/harden.mds +7 -6
  115. package/src/assets/agents/test.md +21 -12
  116. package/src/assets/agents/triage.mds +2 -0
  117. package/src/assets/agents/validate.md +10 -10
  118. package/src/assets/commands/_partials/_compliance.mds +5 -3
  119. package/src/assets/commands/_partials/_engine.mds +9 -8
  120. package/src/assets/commands/_partials/_publication.mds +10 -1
  121. package/src/assets/commands/bug-analysis.mds +80 -126
  122. package/src/assets/commands/code-review.mds +43 -0
  123. package/src/assets/commands/dynamic-build.mds +109 -15
  124. package/src/assets/commands/dynamic-tickets.mds +13 -0
  125. package/src/assets/commands/implement.mds +145 -33
  126. package/src/assets/commands/plan.mds +19 -0
  127. package/src/assets/commands/release.mds +50 -1
  128. package/src/assets/commands/resolve.mds +217 -32
  129. package/src/assets/commands/self-review.mds +8 -7
  130. package/src/assets/mds/git/_ops.mds +23 -0
  131. package/src/assets/mds/git/_ops_steps.mds +17 -0
  132. package/src/assets/mds/git/_pr.mds +1 -1
  133. package/src/assets/mds/git/_references.mds +6 -0
  134. package/src/assets/mds/tracker/_common.mds +4 -0
  135. package/src/assets/mds/tracker/_github.mds +42 -0
  136. package/src/assets/mds/tracker/_jira.mds +43 -0
  137. package/src/assets/mds/tracker/_linear.mds +43 -0
  138. package/src/assets/rules/quality.md +2 -0
  139. package/src/assets/scripts/ci-wait.cjs +204 -126
  140. package/src/assets/scripts/hooks/assets/implement-installed +1 -0
  141. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +13 -11
  142. package/src/assets/scripts/hooks/preamble +18 -8
  143. package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
  144. package/src/assets/scripts/pr-evidence.cjs +41 -2
  145. package/src/assets/scripts/verify-evidence.cjs +408 -56
  146. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +3 -3
  147. package/src/assets/skills/agent-references/references/commands/plan-levels.md +1 -1
  148. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +6 -2
  149. package/src/assets/skills/agent-references/references/validate/passed-alone.md +7 -7
  150. package/src/assets/skills/docs-framework/SKILL.md +5 -2
  151. package/src/assets/skills/git/SKILL.md +3 -3
  152. package/src/assets/skills/git/references/github-api.md +2 -139
  153. package/src/assets/skills/git/references/github-posting.md +158 -0
  154. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  155. package/src/assets/skills/review-methodology/references/patterns.md +1 -1
  156. package/src/assets/skills/review-methodology/references/report-template.md +1 -2
  157. package/src/assets/skills/testing/SKILL.md +2 -4
@@ -122,13 +122,13 @@ When `OPERATION: issue-fix`, you are fixing pre-classified issues assigned FIX_N
122
122
 
123
123
  Run builds, typechecks, lints and tests in the foreground, each with an explicit Bash `timeout` above its expected run time. The ceiling is 600000 ms, or `BASH_MAX_TIMEOUT_MS` when set (`echo ${BASH_MAX_TIMEOUT_MS:-600000}`).
124
124
 
125
- - Capture, then tail, in one Bash call (shell state does not persist): `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; echo "EXIT=$rc"`. The printed `EXIT=` value is the result; never decide one from a grep count.
126
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
127
- - Prefer the scoped command for the change (a package, a path or a test file); for the whole set, one workspace-level command over a per-package loop.
128
- - A run that exceeds its timeout is BLOCKED: report its duration and log path. Do not wait on it, poll it or re-run it.
129
- - A run expected to exceed the ceiling is split into parts, each under about 90% of it, run in sequence. If it cannot be split, report BLOCKED with the remedy `devflow flags --set bash-max-timeout-ms=<ms>`.
125
+ - Capture, then tail, in one Bash call: `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; printf '\nEXIT=%s\n' "$rc"`. The last `EXIT=` line is the result; never decide one from a grep count.
126
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
127
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
128
+ - A run past its timeout moves to the background: no tool call, even a Read, until its end notice, then Read the last `EXIT=` line of the file the move names, not `$LOG`; it alone sets the label, not the notice. With none read it is BLOCKED: report its duration and log path. Report only after the end notice.
129
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
130
130
  - Never re-run a command when nothing it reads has changed.
131
- - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they would allow directly.
131
+ - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they allow directly.
132
132
  - The same rules hold inside a dynamic Workflow sub-agent.
133
133
 
134
134
  ## Principles
@@ -104,13 +104,13 @@ When `OPERATION: ci-fix`, you are fixing the CI checks the ci-status gate report
104
104
 
105
105
  Run builds, typechecks, lints and tests in the foreground, each with an explicit Bash `timeout` above its expected run time. The ceiling is 600000 ms, or `BASH_MAX_TIMEOUT_MS` when set (`echo ${BASH_MAX_TIMEOUT_MS:-600000}`).
106
106
 
107
- - Capture, then tail, in one Bash call (shell state does not persist): `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; echo "EXIT=$rc"`. The printed `EXIT=` value is the result; never decide one from a grep count.
108
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
109
- - Prefer the scoped command for the change (a package, a path or a test file); for the whole set, one workspace-level command over a per-package loop.
110
- - A run that exceeds its timeout is BLOCKED: report its duration and log path. Do not wait on it, poll it or re-run it.
111
- - A run expected to exceed the ceiling is split into parts, each under about 90% of it, run in sequence. If it cannot be split, report BLOCKED with the remedy `devflow flags --set bash-max-timeout-ms=<ms>`.
107
+ - Capture, then tail, in one Bash call: `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; printf '\nEXIT=%s\n' "$rc"`. The last `EXIT=` line is the result; never decide one from a grep count.
108
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
109
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
110
+ - A run past its timeout moves to the background: no tool call, even a Read, until its end notice, then Read the last `EXIT=` line of the file the move names, not `$LOG`; it alone sets the label, not the notice. With none read it is BLOCKED: report its duration and log path. Report only after the end notice.
111
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
112
112
  - Never re-run a command when nothing it reads has changed.
113
- - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they would allow directly.
113
+ - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they allow directly.
114
114
  - The same rules hold inside a dynamic Workflow sub-agent.
115
115
 
116
116
  ## Principles
@@ -32,15 +32,17 @@ disallowedTools:
32
32
 
33
33
  # Harden Agent
34
34
 
35
- You are a meticulous self-review specialist. You evaluate implementations against the 9-pillar quality framework and fix the issues you find. You run in a fresh context after the Code and Polish agents complete, ensuring adequate resources for thorough review and fixes.
35
+ You are a meticulous self-review specialist. You evaluate implementations against the 9-pillar quality framework and fix the issues you find. You run in a fresh context after the Code and Polish agents complete.
36
36
 
37
37
  ## Input Context
38
38
 
39
39
  You receive from orchestrator:
40
40
  - **TASK_DESCRIPTION**: What was implemented
41
- - **FILES_CHANGED**: List of modified files from Code agent output
41
+ - **FILES_CHANGED** (optional): The branch diff's file list, with no deleted file or old rename path
42
42
  - **FEATURE_KNOWLEDGE** (optional): Per KB, the Rules bullets and the KB path, with no heading index, for pattern compliance checking. Check implementation against each bullet's anti-pattern, gotcha or invariant; Read a KB section from its path for more. Follow `devflow:apply-feature-knowledge`.
43
43
 
44
+ **Empty FILES_CHANGED**: If the prompt has a `FILES_CHANGED` line that lists no file, report `### Status: PASS` with the line `no files reviewed`, and review nothing; with no `FILES_CHANGED` line, find the branch changes and review them.
45
+
44
46
  **Worktree Support**: If `WORKTREE_PATH` is provided, follow the `devflow:worktree-support` skill for path resolution. If omitted, use cwd.
45
47
 
46
48
  ## Responsibilities
@@ -66,10 +68,9 @@ Code-scanning runs in the Code agent; do not scan here.
66
68
  ## Principles
67
69
 
68
70
  1. **Fix, don't report** - Self-review means fixing issues, not generating reports
69
- 2. **Fresh context advantage** - Use your full context for thorough evaluation
70
- 3. **Pillar priority** - P0 issues block, P1 issues should be fixed, P2 covers Documentation only
71
- 4. **Minimal changes** - Fix the issue, don't refactor surrounding code
72
- 5. **Honest assessment** - If P0 issue is unfixable, report BLOCKED immediately
71
+ 2. **Pillar priority** - P0 issues block, P1 issues should be fixed, P2 covers Documentation only
72
+ 3. **Minimal changes** - Fix the issue, don't refactor surrounding code
73
+ 4. **Honest assessment** - If P0 issue is unfixable, report BLOCKED immediately
73
74
 
74
75
  ## Output
75
76
 
@@ -71,13 +71,13 @@ When `OPERATION: edit`, you apply a mechanical change: a rename, a move or boile
71
71
 
72
72
  Run builds, typechecks, lints and tests in the foreground, each with an explicit Bash `timeout` above its expected run time. The ceiling is 600000 ms, or `BASH_MAX_TIMEOUT_MS` when set (`echo ${BASH_MAX_TIMEOUT_MS:-600000}`).
73
73
 
74
- - Capture, then tail, in one Bash call (shell state does not persist): `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; echo "EXIT=$rc"`. The printed `EXIT=` value is the result; never decide one from a grep count.
75
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
76
- - Prefer the scoped command for the change (a package, a path or a test file); for the whole set, one workspace-level command over a per-package loop.
77
- - A run that exceeds its timeout is BLOCKED: report its duration and log path. Do not wait on it, poll it or re-run it.
78
- - A run expected to exceed the ceiling is split into parts, each under about 90% of it, run in sequence. If it cannot be split, report BLOCKED with the remedy `devflow flags --set bash-max-timeout-ms=<ms>`.
74
+ - Capture, then tail, in one Bash call: `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; printf '\nEXIT=%s\n' "$rc"`. The last `EXIT=` line is the result; never decide one from a grep count.
75
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
76
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
77
+ - A run past its timeout moves to the background: no tool call, even a Read, until its end notice, then Read the last `EXIT=` line of the file the move names, not `$LOG`; it alone sets the label, not the notice. With none read it is BLOCKED: report its duration and log path. Report only after the end notice.
78
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
79
79
  - Never re-run a command when nothing it reads has changed.
80
- - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they would allow directly.
80
+ - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they allow directly.
81
81
  - The same rules hold inside a dynamic Workflow sub-agent.
82
82
 
83
83
  ## Principles
@@ -3,15 +3,11 @@ description: Proactive bug finding with static and semantic analysis — hunts r
3
3
  ---
4
4
  # Bug Analysis Command
5
5
 
6
- Run a proactive bug analysis on the current branch by combining static analysis tools with parallel semantic analyzers, then synthesizing results into an actionable bug report.
6
+ Run a proactive bug analysis on the current branch: static analysis tools and parallel semantic analyzers, synthesized into one bug report.
7
7
 
8
8
  ## Usage
9
9
 
10
- ```
11
- /bug-analysis (analyze current branch — incremental if prior analysis exists)
12
- /bug-analysis --full (force full analysis, ignore incremental state)
13
- /bug-analysis --no-static (skip static analysis track, semantic-only)
14
- ```
10
+ `/bug-analysis [--full] [--no-static]`: `--full` ignores incremental state; `--no-static` skips static analysis.
15
11
 
16
12
  ## Phases
17
13
 
@@ -96,7 +92,7 @@ If `pr_number` is absent, set `PR_DESCRIPTION` to `(none)`.
96
92
  CHANGED_FILES=$(git diff --name-only {DIFF_RANGE})
97
93
  ```
98
94
 
99
- Store as `CHANGED_FILES`; Steps 2d and Phase 4 read it. A non-zero diff exit stops.
95
+ Store as `CHANGED_FILES`; Phase 4 reads it. A non-zero diff exit stops.
100
96
 
101
97
  If output is empty → "No changes to analyze." Stop.
102
98
 
@@ -104,75 +100,81 @@ If output is empty → "No changes to analyze." Stop.
104
100
 
105
101
  **Produces:** STATIC_TOOL_STATUS
106
102
 
107
- Skip if `--no-static` flag provided.
103
+ If `--no-static` is set: set `STATIC_FINDINGS` to "Static analysis skipped (--no-static)." and `STATIC_STATUS` to "skipped (--no-static): static analysis did not cover these commits". Skip the rest of Phase 2.
108
104
 
109
105
  ```bash
110
- SEMGREP_AVAILABLE=$(which semgrep 2>/dev/null && echo "yes" || echo "no")
111
- SNYK_AVAILABLE=$(which snyk 2>/dev/null && echo "yes" || echo "no")
112
- CODEQL_AVAILABLE=$(which codeql 2>/dev/null && echo "yes" || echo "no")
106
+ for t in semgrep snyk codeql jq; do printf '%s=%s\n' "$t" "$(command -v "$t" || echo no)"; done
107
+ set -o pipefail; export LC_ALL=C; git -C "{worktree}" diff --name-only -z --diff-filter=d {DIFF_RANGE} | tr '\0\n' '\n?' | sed -n 's/.*\.\([^./]*\)$/\1/p' | sort -u || echo "extension list failed"
113
108
  ```
114
109
 
115
- If all are `no`: warn user "No static analysis tools found. Proceeding with semantic analysis only. To enable static analysis, install semgrep (`pip install semgrep`) or snyk (`npm install -g snyk`)." Set `STATIC_FINDINGS` to `(none)`.
110
+ Its second line lists the extensions of the changed files. `{lang}` is the first of javascript-typescript (js jsx mjs cjs ts tsx), python (py), ruby (rb) whose extension is listed; no other value reaches the command. With none, CodeQL does not run and its status is `no supported language`; `extension list failed` means it did not complete.
111
+
112
+ If all three scanners print `no`: warn user "No static analysis tools found. Install semgrep or snyk." Set `STATIC_FINDINGS` to "Static analysis did not run: no scanner is installed." and `STATIC_STATUS` to `not installed` for each tool.
113
+
114
+ If a scanner is installed but `jq` prints `no`: set `STATIC_FINDINGS` to "Static analysis did not complete: jq is not installed." and `STATIC_STATUS` to `did not complete (jq is not installed)` for an installed tool, else `not installed`.
116
115
 
117
116
  If some are available: note which tools will run.
118
117
 
119
118
  #### Step 2d: Tiered Static Analysis
120
119
 
121
- **Produces:** STATIC_FINDINGS
120
+ **Produces:** STATIC_FINDINGS, STATIC_STATUS
122
121
  **Requires:** STATIC_TOOL_STATUS, DIFF_RANGE, ANALYSIS_DIR
123
122
 
124
- Skip if `--no-static` flag provided or all tools unavailable.
123
+ Skip if Step 2c set `STATIC_STATUS`.
125
124
 
126
- Run available tools on the changed files (from `CHANGED_FILES` computed in Step 2b).
127
-
128
- **Semgrep and Snyk run in parallel** — launch both in the background, then wait for both before proceeding to CodeQL. CodeQL is conditional and sequential.
125
+ Run each available tool in this order, each in its own foreground Bash call with the Bash `timeout` at the ceiling: 600000 ms, or `BASH_MAX_TIMEOUT_MS` when set. Past it, make no tool call, even a Read, until its end notice, then Read the last `exit=` line of the output file that notice names, never a scanner file; the notice sets nothing. File names reach a scanner only through `-z`, `xargs -0` and after `--`.
129
126
 
130
127
  **Semgrep** (if available):
131
128
  ```bash
132
- # tr '\n' '\0' + xargs -0 is portable across GNU and BSD xargs (macOS ships BSD xargs, which lacks -d)
133
- echo "$CHANGED_FILES" | tr '\n' '\0' | xargs -0 timeout 300 semgrep scan --config auto --sarif --quiet 2>/dev/null
129
+ cd "{worktree}" || exit
130
+ set -o pipefail; git diff --name-only -z --diff-filter=d {DIFF_RANGE} | xargs -0 -r semgrep scan --config auto --sarif --quiet -- >"{ANALYSIS_DIR}/semgrep.part" 2>/dev/null; E=$?
131
+ mv "{ANALYSIS_DIR}"/semgrep.{part,sarif}
132
+ printf '\nexit=%s\n' "$E"
134
133
  ```
135
- Parse SARIF output → extract findings.
136
134
 
137
135
  **Snyk Code** (if available):
138
136
  ```bash
139
- # One project-level scan (a per-file xargs run would invoke snyk O(n) times; --file is for dependency scans).
140
- SNYK_SARIF=$(timeout 300 snyk code test --sarif 2>/dev/null)
141
- # Programmatic filter: keep only results whose file path is in CHANGED_FILES — defense-in-depth, before the LLM agent sees any output.
142
- SNYK_FILTERED=$(echo "$SNYK_SARIF" | jq --argjson files "$(echo "$CHANGED_FILES" | jq -R . | jq -s .)" \
143
- '.runs[].results |= map(select(.locations[].physicalLocation.artifactLocation.uri as $uri | $files | index($uri) != null))' \
144
- 2>/dev/null || echo "$SNYK_SARIF")
137
+ cd "{worktree}" || exit
138
+ snyk code test --sarif >"{ANALYSIS_DIR}/snyk-all.json" 2>/dev/null; E=$?
139
+ git diff --name-only -z {DIFF_RANGE} >"{ANALYSIS_DIR}/changed.z" && jq --rawfile n "{ANALYSIS_DIR}/changed.z" \
140
+ '($n|split("\u0000")) as $f|.runs[]?.results|=[.[]?|select([.locations[]?.physicalLocation.artifactLocation.uri//empty] as $u|($u|length)==0 or any($u[];IN($f[])))]' \
141
+ "{ANALYSIS_DIR}/snyk-all.json" >"{ANALYSIS_DIR}/snyk.sarif" \
142
+ && jq -nr '[inputs|[.runs[]?.results[]?]|length]|"total=\(.[0]) kept=\(.[1])"' "{ANALYSIS_DIR}/snyk-all.json" "{ANALYSIS_DIR}/snyk.sarif" \
143
+ || { echo "Snyk scope filter failed: unfiltered"; cp "{ANALYSIS_DIR}/snyk-all.json" "{ANALYSIS_DIR}/snyk.sarif"; }
144
+ printf '\nexit=%s\n' "$E"
145
+ ```
146
+
147
+ **Counts and table** (after Snyk with `{tools}` = `semgrep snyk`; after CodeQL with `codeql`):
148
+ ```bash
149
+ cd "{ANALYSIS_DIR}" || exit
150
+ L='def L($r):.ruleId as $i|.level//([$r.tool|.driver,.extensions[]?|.rules[]?|select(.id==$i)]|.[0].defaultConfiguration.level)//"warning";'
151
+ for t in {tools}; do [ -f $t.sarif ] || continue
152
+ jq -rs --arg t $t "$L"'[.[].runs[]?|. as $r|.results[]?|L($r)] as $l|def c(k):[$l[]|select(.==k)]|length;"\($t) docs=\(length) results=\($l|length) error=\(c("error")) warning=\(c("warning")) note=\(c("note"))"' $t.sarif || echo "$t sarif=unreadable"
153
+ jq -r --arg t $t "$L"'.runs[]?|. as $r|.results[]?|[$t,(.locations[0].physicalLocation|.artifactLocation.uri,.region.startLine),L($r),(.ruleId//"—"),((.message.text//"")[:200])]|@tsv' $t.sarif >findings-$t.tsv || echo "findings table incomplete: $t"
154
+ done
155
+ cat findings-{semgrep,snyk,codeql}.tsv >findings.tsv 2>/dev/null
156
+ jq -Rrs 'split("\n")|map(select(.!=""))|sort_by(split("\t")[3]|{error:0,warning:1}[.]//2)|.[:50][]' findings.tsv >findings-top.tsv || echo "findings table incomplete: top"
145
157
  ```
146
- Parse `SNYK_FILTERED` SARIF → extract findings. If `jq` is unavailable, fall back to the raw `SNYK_SARIF` and note the limitation.
158
+ Read `error` as HIGH, `warning` as MEDIUM, `note` as LOW.
147
159
 
148
160
  **CodeQL** (if available AND (`--full` OR Semgrep/Snyk found HIGH/CRITICAL findings)):
149
161
  ```bash
150
- # Use a unique temp directory per run to prevent symlink attacks and concurrent-process clobbering
151
- CODEQL_TMP=$(mktemp -d)
152
- # Register cleanup trap immediately after mktemp — ensures rm -rf runs on EXIT, SIGTERM, and SIGINT
153
- # even if the session is interrupted before reaching the explicit rm -rf below
162
+ CODEQL_TMP=$(mktemp -d) || exit
154
163
  trap 'rm -rf "${CODEQL_TMP}"' EXIT INT TERM
155
- timeout 600 codeql database create "${CODEQL_TMP}/db" --language={detected-language} --source-root=. 2>/dev/null && \
156
- timeout 600 codeql database analyze "${CODEQL_TMP}/db" --format=sarif-latest --output="${CODEQL_TMP}/results.sarif" 2>/dev/null
157
- # Capture exit status before cleanup so cleanup doesn't mask failures
164
+ { codeql database create "${CODEQL_TMP}/db" --language={lang} --source-root="{worktree}" && \
165
+ codeql database analyze "${CODEQL_TMP}/db" --format=sarif-latest --output="{ANALYSIS_DIR}/codeql.sarif"; } >"{ANALYSIS_DIR}/codeql.log" 2>&1
158
166
  CODEQL_EXIT=$?
159
- # Parse SARIF output BEFORE cleanup — rm -rf destroys results.sarif
160
- CODEQL_SARIF=$(cat "${CODEQL_TMP}/results.sarif" 2>/dev/null || echo "")
161
- # Explicit cleanup (trap also covers this path; explicit rm is belt-and-suspenders)
167
+ printf '\nexit=%s\n' "$CODEQL_EXIT"
162
168
  rm -rf "${CODEQL_TMP}"
163
169
  trap - EXIT INT TERM
164
170
  ```
165
- Parse `CODEQL_SARIF` → extract findings. If database creation fails, `CODEQL_EXIT` is non-zero — skip CodeQL findings and note it. The `trap` guarantees cleanup on SIGTERM/SIGINT/EXIT so orphaned temp directories do not accumulate across interrupted sessions.
166
-
167
- **Normalize** all findings to unified table, cap at top 50 by severity. Truncate each Description entry to 200 characters maximum:
171
+ A tool's status is its last line, `exit={n}`; a tool that ran completed only on Semgrep 0, Snyk 0 or 1 (1 = issues, only with SARIF output), CodeQL 0. Snyk 3 (no supported file) is `no supported language`: its counts are not checked. Any other status, or no such line, means it did not complete (126-127: scanner did not start; else failed. Semgrep via xargs: number only). A call with no `exit=` line read did not complete. A tool that ran also did not complete when the counts fence prints no `{tool} docs=` line for it, `sarif=unreadable` or `findings table incomplete` for it (`: top` counts for every tool), or `docs=0` (Semgrep exit 0 with `docs=0`: no file to scan, completed).
168
172
 
169
- | Tool | File:Line | CWE | Severity | Title | Description |
170
- |------|-----------|-----|----------|-------|-------------|
171
- | {tool} | {file}:{line} | {CWE or —} | {CRITICAL/HIGH/MEDIUM/LOW} | {title} | {description truncated to 200 chars} |
173
+ `findings.tsv` keeps every finding. Read only `findings-top.tsv` (top 50 by level: tool, file, line, level, rule, message cut to 200 chars), set `STATIC_FINDINGS` to the column names and its rows, and write them with the status lines to `{ANALYSIS_DIR}/static-findings.md`.
172
174
 
173
- Write ALL raw findings to `{ANALYSIS_DIR}/static-findings.md`. Set `STATIC_FINDINGS` to the top-50 table.
175
+ `STATIC_FINDINGS` is `(none)` only when every tool that was to run completed, at least one did, and none found anything; when no tool was to run, it is "Static analysis did not run: no tool ran." For each tool that did not complete, add under any rows the line "Static analysis did not complete: {tool}: " plus "exit {n} ({meaning}).", "no exit status.", "no counts line." or "SARIF not read." A Snyk call (not Snyk 3) that printed "Snyk scope filter failed: unfiltered" adds that line too.
174
176
 
175
- If no tool produced findings: set `STATIC_FINDINGS` to `(none)`.
177
+ Set `STATIC_STATUS` to one `{tool}: {value}` per tool: `completed`, `not installed`, `not needed` (CodeQL, not triggered) or `no supported language` (CodeQL with no table row, or Snyk 3), all covered, or `did not complete ({why})`, not covered. A covered tool other than `completed` was not to run.
176
178
 
177
179
  ### Phase 3: Context Loading
178
180
 
@@ -238,23 +240,22 @@ A KB with no Rules section labels its entries `Rules ({section name}):` and give
238
240
  #### Plan Artifact
239
241
 
240
242
  1. List the design plans: `{worktree}/.devflow/docs/design/*.md` (a `/plan` artifact) and `{worktree}/.devflow/docs/design/*/*/*-plan.md` (a `/dynamic-plan` per-ticket plan). Sort by timestamp, newest first, and scan the 10 most recent. A `/plan` artifact's timestamp is the `YYYY-MM-DD_HHMM` before its `.md`; a per-ticket plan's is the name of its parent directory. Never sort by file name, which starts with an issue ID or a ticket slug. A per-ticket plan counts only when its `{ticket-slug}` (its file name without `-plan.md`) appears in the worktree's branch name
241
- 2. Read the most recent file that carries an `## Acceptance Criteria` section
242
- 3. Extract `## Acceptance Criteria` section → parse into table: `| ID | Criterion | Type | Testable Condition |`
243
- 4. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
244
- 5. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
243
+ 2. Read the most recent file that carries an `## Acceptance Criteria` section; parse it into the table `| ID | Criterion | Type | Testable Condition |`
244
+ 3. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
245
+ 4. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
245
246
 
246
247
  ### Phase 4: File Analysis
247
248
 
248
249
  **Produces:** ACTIVE_FOCUSES
249
250
  **Requires:** DIFF_RANGE
250
251
 
251
- Determine which focus analyzers to run using `CHANGED_FILES` (already computed in Step 2b — do not re-run `git diff`):
252
+ Pick the focus analyzers from `CHANGED_FILES` (Step 2b; do not re-run `git diff`):
252
253
 
253
254
  | Focus | Condition |
254
255
  |-------|-----------|
255
256
  | `security` | Always |
256
257
  | `functional` | Always |
257
- | `integration` | 2+ distinct directories changed (`dirname` unique count ≥ 2) |
258
+ | `integration` | 2+ distinct directories changed |
258
259
  | `usability` | Any `.tsx`, `.jsx`, `.html`, or `.css` file changed |
259
260
 
260
261
  ### Phase 5: Parallel Bug Analysis
@@ -272,23 +273,18 @@ FOCUS: {focus}
272
273
  DIFF_COMMAND: git diff {DIFF_RANGE}
273
274
  ACCEPTANCE_RULES: {ACCEPTANCE_RULES filtered to this focus type, or (none)}
274
275
  PLAN_CONTEXT: {PLAN_CONTEXT}
275
- STATIC_FINDINGS: {STATIC_FINDINGS if focus == security, else (none)}
276
+ STATIC_FINDINGS: <static-findings>{STATIC_FINDINGS if focus == security, else (none)}</static-findings>
276
277
  FEATURE_KNOWLEDGE: {FEATURE_KNOWLEDGE}
277
278
  PR_DESCRIPTION: <pr-description>{PR_DESCRIPTION}</pr-description>
278
279
  OUTPUT_PATH: {ANALYSIS_DIR}/{focus}.md
279
280
  Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE.
280
- IMPORTANT: Write report to {ANALYSIS_DIR}/{focus}.md using Write tool"
281
+ IMPORTANT: Write the report to OUTPUT_PATH using the Write tool"
281
282
  ```
282
283
 
283
- Notes:
284
- - Security analyzer receives full `STATIC_FINDINGS`; all others receive `(none)`
285
- - Filter `ACCEPTANCE_RULES` by the `Type` column matching each focus: security criteria → security analyzer, functional criteria → functional analyzer, etc. Pass the filtered subset only
286
- - Spawn all in a single message for true parallel execution
287
-
288
284
  ### Phase 6: Synthesis
289
285
 
290
286
  **Produces:** BUG_ANALYSIS_SUMMARY
291
- **Requires:** ANALYZER_OUTPUTS, ANALYSIS_DIR, BRANCH_INFO
287
+ **Requires:** ANALYZER_OUTPUTS, ANALYSIS_DIR, BRANCH_INFO, STATIC_STATUS
292
288
 
293
289
  Spawn Synthesize agent:
294
290
 
@@ -298,14 +294,15 @@ Agent(subagent_type="Synthesize", run_in_background=false):
298
294
  ANALYSIS_BASE_DIR: {ANALYSIS_DIR}
299
295
  BRANCH: {branch} -> {base_branch}
300
296
  TIMESTAMP: {timestamp}
297
+ STATIC_STATUS: {STATIC_STATUS}
301
298
  Output: {ANALYSIS_DIR}/bug-analysis-summary.md"
302
299
  ```
303
300
 
304
301
  ### Phase 7: Finalize
305
302
 
306
- **Requires:** BRANCH_INFO, ANALYSIS_DIR
303
+ **Requires:** BRANCH_INFO, ANALYSIS_DIR, STATIC_STATUS
307
304
 
308
- 1. In one Bash call run `M="$(git -C "{worktree}" rev-parse --absolute-git-dir)/devflow/bug-analysis/{branch-slug}" && mkdir -p "$M" && git -C "{worktree}" rev-parse HEAD > "$M/.last-analysis-head"`. A failed or refused write never stops the run and is not retried another way.
305
+ 1. Run the write below only when every tool that was to run completed, or `--no-static` is set, also with `--full`. Else keep the marker; the report names the way out (fix the tool, or run with `--no-static`). In one Bash call run `M="$(git -C "{worktree}" rev-parse --absolute-git-dir)/devflow/bug-analysis/{branch-slug}" && mkdir -p "$M" && git -C "{worktree}" rev-parse HEAD > "$M/.last-analysis-head"`. A failed or refused write never stops the run and is not retried another way.
309
306
  2. Report to user:
310
307
 
311
308
  ```
@@ -314,26 +311,22 @@ Output: {ANALYSIS_DIR}/bug-analysis-summary.md"
314
311
  **Branch**: {branch} -> {base_branch}
315
312
  {any base `TRACEABILITY: DEGRADED` line from Phase 1, as returned}
316
313
  **Analysis**: {ANALYSIS_DIR}
314
+ **Static analysis**: {STATIC_STATUS per tool; if a tool did not complete: "marker kept" and the way out; any Snyk filter note}
317
315
 
318
316
  ### Risk Assessment: {risk_level}
319
317
 
320
318
  {brief_reasoning}
321
319
 
322
320
  ### Bug Counts
323
- | Category | CRITICAL | HIGH | MEDIUM | LOW | Total |
324
- |----------|----------|------|--------|-----|-------|
325
- | Security | {n} | {n} | {n} | {n} | {n} |
326
- | Functional | {n} | {n} | {n} | {n} | {n} |
327
- | Integration | {n} | {n} | {n} | {n} | {n} |
328
- | Usability | {n} | {n} | {n} | {n} | {n} |
321
+ {The summary's Bug Summary table: Security, Functional, Integration and Usability rows by CRITICAL, HIGH, MEDIUM, LOW and Total}
329
322
 
330
323
  ### Top Findings
331
- {List top 3-5 bugs by severity and confidence}
324
+ {Top 3-5 bugs by severity and confidence}
332
325
 
333
326
  ### Artifacts
334
327
  - Bug report: {ANALYSIS_DIR}/bug-analysis-summary.md
335
- - Per-focus reports: {ANALYSIS_DIR}/{security|functional|integration|usability}.md
336
- - Static findings: {ANALYSIS_DIR}/static-findings.md (if static analysis ran)
328
+ - Per-focus reports: {ANALYSIS_DIR}/{focus}.md
329
+ - Static findings: {ANALYSIS_DIR}/static-findings.md (if it ran)
337
330
 
338
331
  {if any CRITICAL or HIGH bugs found:}
339
332
  Run `/resolve` to process and fix these findings.
@@ -341,69 +334,26 @@ Run `/resolve` to process and fix these findings.
341
334
 
342
335
  ## Architecture
343
336
 
344
- ```
345
- /bug-analysis (orchestrator — spawns agents only)
346
- │
347
- ├─ Phase 1: Pre-flight
348
- │ └─ Git agent (ensure-pr-ready)
349
- │
350
- ├─ Phase 2: Static Analysis
351
- │ └─ 2a incremental range, 2b changed files (stop if none), 2c tools, 2d semgrep → snyk → codeql
352
- │
353
- ├─ Phase 3: Context Loading
354
- │ ├─ Feature knowledge load → FEATURE_KNOWLEDGE
355
- │ └─ {worktree}/.devflow/docs/design/*.md → PLAN_CONTEXT + ACCEPTANCE_RULES
356
- │
357
- ├─ Phase 4: File Analysis
358
- │ └─ Detect active focuses (security + functional always; integration + usability conditional)
359
- │
360
- ├─ Phase 5: Bug Analysis (PARALLEL)
361
- │ └─ Debug agent per active focus (security also reads STATIC_FINDINGS)
362
- │
363
- ├─ Phase 6: Synthesis
364
- │ └─ Synthesize agent (mode: bug-analysis)
365
- │
366
- └─ Phase 7: Finalize
367
- ├─ Write .last-analysis-head
368
- └─ Display results + suggest /resolve if blocking bugs found
369
- ```
337
+ Seven phases: Phase 1 to Phase 7.
370
338
 
371
339
  ## Edge Cases
372
340
 
373
341
  | Case | Handling |
374
342
  |------|----------|
375
- | No new commits since last analysis | Stop: "No new commits since last analysis. Use --full for a full re-analysis." |
376
343
  | Rebase invalidates `.last-analysis-head` SHA | `git cat-file -t` check fails → fallback to full diff |
377
344
  | Zero changed files in DIFF_RANGE with exit 0 | Stop: "No changes to analyze." |
378
- | Same-minute analysis collision | `mkdir` with seconds suffix (`YYYY-MM-DD_HHMMSS`) |
379
- | All static tools unavailable | Warn, proceed with semantic-only analysis |
380
- | `--no-static` flag | Skip Phase 2c and 2d entirely; `STATIC_FINDINGS=(none)` |
381
345
  | `--full` flag | Bypass incremental detection (Step 2a), run full diff from base |
382
- | CodeQL database creation fails | Skip CodeQL, note in output, continue with other tools |
346
+ | A scanner did not run or did not complete | Name the tool and why in `STATIC_FINDINGS`, not `(none)`; a tool that did not complete keeps the marker |
383
347
 
384
348
  ## Resolve Compatibility
385
349
 
386
- Run `/resolve` after `/bug-analysis` to fix identified bugs. `/resolve` automatically detects and uses bug analysis reports when no code review report exists.
350
+ `/resolve` detects and uses bug analysis reports when no code review report exists.
387
351
 
388
352
  ## Principles
389
353
 
390
- 1. **Orchestration only** — Command spawns agents, doesn't do analysis work itself
391
- 2. **Parallel, not background** — Analyzers spawn in one message with `run_in_background=false`
392
- 3. **Static + semantic** — Two complementary tracks, each validates the other
393
- 4. **Incremental by default** — Only analyze new changes unless `--full` specified
394
- 5. **Verify before reporting** — Debug agents self-verify every finding
395
- 6. **Honest reporting** — Display risk level and counts directly from synthesis
354
+ 1. **Orchestration only** — the command spawns agents and does no analysis itself
355
+ 2. **Incremental by default** — only new changes, unless `--full`
396
356
 
397
357
  ## Phase Completion Checklist
398
358
 
399
- Before reporting results, verify every phase was executed:
400
-
401
- - [ ] Phase 1: Pre-flight → BRANCH_INFO captured, PR_DESCRIPTION fetched (or `(none)`)
402
- - [ ] Phase 2: Static Analysis → STATIC_FINDINGS captured (or `(none)` if skipped/no tools/no findings); ANALYSIS_DIR created; CHANGED_FILES populated
403
- - [ ] Phase 3: Context Loading → FEATURE_KNOWLEDGE loaded (or `(none)`), PLAN_CONTEXT and ACCEPTANCE_RULES captured (or `(none)`)
404
- - [ ] Phase 4: File Analysis → ACTIVE_FOCUSES determined (security + functional always present)
405
- - [ ] Phase 5: Parallel Bug Analysis → ANALYZER_OUTPUTS captured per active focus; all reports written to ANALYSIS_DIR
406
- - [ ] Phase 6: Synthesis → BUG_ANALYSIS_SUMMARY written to `{ANALYSIS_DIR}/bug-analysis-summary.md`
407
- - [ ] Phase 7: Finalize → `.last-analysis-head` updated; results displayed to user
408
-
409
- If any phase is unchecked, execute it before proceeding.
359
+ Before reporting results, verify that every phase ran and that each variable it produces is captured, with STATIC_FINDINGS (`(none)` only if every tool that was to run completed clean), the report files in ANALYSIS_DIR present and `.last-analysis-head` written only if Phase 7's gate allows; run any missing phase first.
@@ -131,7 +131,7 @@ The following agentType values are valid. Model tiers are shown for reference
131
131
  Before authoring, verify:
132
132
 
133
133
  1. **Workflow tool available:** if the `Workflow` tool is not in your available tools, STOP and tell the user: "The Workflow tool is not available in this session. dynamic-build requires Claude Code's dynamic workflow runtime."
134
- 2. **`agentType` support:** confirmed available (spike F5, 2026-06-11). If spawned agents return no results, check that devflow is installed (`devflow init` has been run).
134
+ 2. **`agentType` support:** if spawned agents return no results, check that devflow is installed (`devflow init` has been run).
135
135
  3. **Tracker paths:** an issue reference or URL in the input is read through the Git agent, which resolves the configured tracker and its access itself and reports `TRACEABILITY: DEGRADED ({reason})` when it cannot read one — then note it and fall back to the issue text the user provided. No tracker CLI is checked here.
136
136
  4. **No-remote path:** if the repo has no remote, skip the remote-dependent steps (the wave PR and its evidence) and proceed with local branch operations only; the Git agent reports DEGRADED for any tracker step it cannot reach.
137
137
 
@@ -325,7 +325,7 @@ After implementing, commit your changes with a conventional-commit message and r
325
325
  );
326
326
 
327
327
  // Phase 3: Gate 1 #1 — post-code pipeline: Polish agent → Harden agent → Validate agent.
328
- // Validate runs last and unconditionally, so its one full run covers every commit the pass made (see gate1_postcode()).
328
+ // Validate runs last and unconditionally, so its one full run covers every commit the pass made.
329
329
  const gate1 = await phase("gate1", async () => {
330
330
  await agent(`Polish and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Polish" });
331
331
 
@@ -383,8 +383,7 @@ Criteria: ${CRITERIA || "see plan"}
383
383
  Return: {"verdict": "PASS" | "FAIL", "rationale": "..."} — FAIL if either answer is no, naming the failing criterion or the drift.`, { agentType: "Evaluate" });
384
384
  evalVerdict = evaluation?.verdict === "PASS" ? "PASS" : "FAIL";
385
385
  if (evalVerdict === "FAIL") {
386
- // fix-and-continue — no re-evaluate, no inline Gate 1. The final Gate 1 (#2)
387
- // after the review pass is the build gate before the branch is handed back.
386
+ // fix-and-continue — no re-evaluate, no inline Gate 1.
388
387
  await agent(`OPERATION: alignment-fix
389
388
  Fix the alignment issues identified by the Evaluate agent on branch ${BRANCH}:
390
389
  ${evaluation?.rationale || "see the Evaluate agent's report"}
@@ -400,16 +399,18 @@ Self-verify your fix compiles, following your Running commands block. Commit fix
400
399
  ${CRITERIA || "(none)"}
401
400
  TEST_PLAN: ${TEST_PLAN || "(none)"}
402
401
  Follow your Running commands block for every scenario command.
403
- Cover: functionality, API contracts, performance, and cover every TEST_PLAN scenario. Report: PASS or FAIL per scenario.`, { agentType: "Test" });
404
- testVerdict = testResult.verdict;
402
+ Cover: functionality, API contracts, performance, and cover every TEST_PLAN scenario.
403
+ Return: {"verdict": "PASS" | "FAIL" | "BLOCKED", "failures": "..."}`, { agentType: "Test" });
404
+ const word = testResult?.verdict?.split?.(/[ (]/)[0];
405
+ testVerdict = ["PASS", "FAIL"].includes(word) ? word : "BLOCKED";
405
406
  if (testVerdict === "FAIL") {
406
407
  // fix-and-continue — no re-test, no inline Gate 1.
407
408
  await agent(`OPERATION: qa-fix
408
409
  Fix the failing acceptance test scenarios on branch ${BRANCH}:
409
- ${testResult.failures}
410
+ ${testResult.failures || "see the Test agent's report"}
410
411
  ${COMPLIANCE_DIRECTIVE}
411
412
  Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Fix" });
412
- testVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
413
+ testVerdict = "FAIL-FIXED";
413
414
  }
414
415
  }
415
416
 
@@ -554,8 +555,6 @@ Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<
554
555
  });
555
556
 
556
557
  // Phase 5.5: Gate 1 #2 — FINAL post-fix gate (Polish agent → Harden agent → full Validate agent, Validate last and unconditional).
557
- // Runs ONCE, after the review pass. Inside the pass the Code agent self-verified its own fixes;
558
- // this is the build gate before the branch is handed back. See gate1_postcode() cadence.
559
558
  const gate1Final = await phase("gate1-final", async () => {
560
559
  await agent(`Polish and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Polish" });
561
560
 
@@ -589,9 +588,9 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
589
588
  });
590
589
 
591
590
  // PASS requires survivingFindings.length === 0 && coverageGaps.length === 0 && gate1Final.verdict !== "ESCALATED"
592
- // and no FAIL-FIXED Gate 2 verdict: fixes applied but never re-run report UNVERIFIED, never PASS
591
+ // and every Gate 2 verdict PASS or SKIPPED; any other reports UNVERIFIED, never PASS
593
592
  const coverageGaps = reviewResult.coverageGaps || [];
594
- const gate2Unverified = [gate2.evaluateVerdict, gate2.testVerdict].includes("FAIL-FIXED");
593
+ const gate2Unverified = ![gate2.evaluateVerdict, gate2.testVerdict].every(v => v === "PASS" || v === "SKIPPED");
595
594
  const overallVerdict = (reviewResult.survivingFindings?.length || 0) === 0 && coverageGaps.length === 0 && gate1Final.verdict !== "ESCALATED" ? (gate2Unverified ? "UNVERIFIED" : "PASS") : "PARTIAL";
596
595
 
597
596
  // Phase 6: Report — the phase returns the engine result (engine_output_schema); its verdict is overallVerdict
@@ -637,17 +636,17 @@ This applies to both: multiple Code agents working on a single ticket AND multi-
637
636
 
638
637
  ### Build execution doctrine — the Running commands block (LOAD-BEARING)
639
638
 
640
- The Validate, Code and Test agents each carry this `## Running commands` block in their bodies, so each prompt in this engine names the block ("your Running commands block") instead of copying it:
639
+ The Code, Fix, Refactor, Validate and Test agents each carry this `## Running commands` block in their bodies, so each prompt in this engine names the block ("your Running commands block") instead of copying it:
641
640
 
642
641
  Run builds, typechecks, lints and tests in the foreground, each with an explicit Bash `timeout` above its expected run time. The ceiling is 600000 ms, or `BASH_MAX_TIMEOUT_MS` when set (`echo ${BASH_MAX_TIMEOUT_MS:-600000}`).
643
642
 
644
- - Capture, then tail, in one Bash call (shell state does not persist): `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; echo "EXIT=$rc"`. The printed `EXIT=` value is the result; never decide one from a grep count.
645
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
646
- - Prefer the scoped command for the change (a package, a path or a test file); for the whole set, one workspace-level command over a per-package loop.
647
- - A run that exceeds its timeout is BLOCKED: report its duration and log path. Do not wait on it, poll it or re-run it.
648
- - A run expected to exceed the ceiling is split into parts, each under about 90% of it, run in sequence. If it cannot be split, report BLOCKED with the remedy `devflow flags --set bash-max-timeout-ms=<ms>`.
643
+ - Capture, then tail, in one Bash call: `LOG=$(mktemp); echo "LOG=$LOG"; <command> >"$LOG" 2>&1; rc=$?; tail -n 40 "$LOG"; printf '\nEXIT=%s\n' "$rc"`. The last `EXIT=` line is the result; never decide one from a grep count.
644
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
645
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
646
+ - A run past its timeout moves to the background: no tool call, even a Read, until its end notice, then Read the last `EXIT=` line of the file the move names, not `$LOG`; it alone sets the label, not the notice. With none read it is BLOCKED: report its duration and log path. Report only after the end notice.
647
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
649
648
  - Never re-run a command when nothing it reads has changed.
650
- - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they would allow directly.
649
+ - Never wrap a build or test command in `sh -c`, `bash -c`, `python3 -c` or `node -e`: permission rules deny wrapped commands they allow directly.
651
650
  - The same rules hold inside a dynamic Workflow sub-agent.
652
651
 
653
652
  **Cheapest-sufficient validation:** iterate with the fastest check that proves the change — `tsc --noEmit`, `cargo check`, `go vet`, a single test file — the ecosystem's cheapest sufficient signal. Reserve expensive full/optimized builds and whole-suite runs for the final gates only.
@@ -705,6 +704,7 @@ Gate 2 inputs are produced by `/devflow:dynamic-plan`'s plan-challenge step —
705
704
  **Test agent** (only if acceptance criteria or a test plan exist):
706
705
  - Scenario-based acceptance tests covering functionality, API contracts, performance
707
706
  - FAIL → fix-and-continue — a Fix agent applies the demanded fixes and self-verifies its own build. The recorded verdict becomes `FAIL-FIXED`; Gate 2 then proceeds. In SINGLE mode the run reports it as `UNVERIFIED`, never PASS.
707
+ - BLOCKED (no result), or any other or missing verdict → no fix, and the run never reports PASS.
708
708
 
709
709
  **When Gate 2 inputs are absent:**
710
710
  - No plan → skip the Evaluate agent silently (note in output: "Gate 2 Evaluate agent skipped — no plan available")
@@ -873,7 +873,7 @@ Each ticket engine run returns a structured result. The Synthesize agent or the
873
873
  ],
874
874
  "gate2": {
875
875
  "evaluateVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED",
876
- "testVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED",
876
+ "testVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED | BLOCKED",
877
877
  "skipReasons": ["string — why a gate was skipped, if applicable"]
878
878
  }
879
879
  }
@@ -1188,12 +1188,12 @@ Agent(subagent_type="Test"):
1188
1188
  FILES_CHANGED: {the file list that diff printed}
1189
1189
  TEST_PLAN: {the TP lines of the wave evidence file's ## Test Plan section}
1190
1190
  WORKTREE_PATH: {integration worktree root}
1191
- Cover every TEST_PLAN line on the integration branch as it stands. Report PASS or FAIL with evidence."
1191
+ Cover every TEST_PLAN line on the integration branch as it stands. Report your Status with evidence."
1192
1192
  ```
1193
1193
 
1194
- Nothing is fixed here, PASS or FAIL: the wave is done and its PR is open. Report the Test agent's Status.
1194
+ Nothing is fixed here, whatever the Status: the wave is done and its PR is open. Report the Test agent's Status.
1195
1195
 
1196
- **(b) Claims.** Append its TP claims, PASS or FAIL alike, to the `## Claims` section of `"{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md"` — the file's last section, created when absent. Append only: never edit or remove a claim. Each line is `/implement`'s TP claim, keyed to the 40-hex `HEAD:` the Test agent's report shows:
1196
+ **(b) Claims.** Append its TP claims, whatever its Status, to the `## Claims` section of `"{integration worktree root}/.devflow/docs/evidence-wave-{slug}.md"` — the file's last section, created when absent. Append only: never edit or remove a claim. Each line is `/implement`'s TP claim, keyed to the 40-hex `HEAD:` the Test agent's report shows:
1197
1197
 
1198
1198
  ```
1199
1199
  - TP-<n> <PASS|FAIL|SKIP> sha:<head> by:test exit:<0-255>