devflow-kit 3.9.0 → 3.11.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 (168) hide show
  1. package/CHANGELOG.md +142 -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/backup-session.js +131 -0
  9. package/dist/cli/commands/compliance-prompts.js +9 -4
  10. package/dist/cli/commands/compliance.js +184 -24
  11. package/dist/cli/commands/curation.js +23 -10
  12. package/dist/cli/commands/gate-level-prompts.js +39 -25
  13. package/dist/cli/commands/gates.js +5 -7
  14. package/dist/cli/commands/init.js +169 -184
  15. package/dist/cli/commands/install-report.js +129 -0
  16. package/dist/cli/commands/legacy-sweep.js +159 -0
  17. package/dist/cli/commands/proxy.js +6 -1
  18. package/dist/cli/commands/rules.js +75 -21
  19. package/dist/cli/commands/skills.js +42 -9
  20. package/dist/cli/commands/tracker.js +9 -3
  21. package/dist/cli/commands/uninstall.js +339 -75
  22. package/dist/commands/bug-analysis.md +69 -120
  23. package/dist/commands/dynamic-build.md +23 -23
  24. package/dist/commands/implement.md +46 -32
  25. package/dist/commands/release.md +1 -1
  26. package/dist/commands/resolve.md +35 -31
  27. package/dist/commands/self-review.md +8 -7
  28. package/dist/compliance-off/agents/git.md +120 -0
  29. package/dist/compliance-off/agents/triage.md +180 -0
  30. package/dist/compliance-off/commands/bug-analysis.md +398 -0
  31. package/dist/compliance-off/commands/code-review.md +520 -0
  32. package/dist/compliance-off/commands/dynamic-build.md +1251 -0
  33. package/dist/compliance-off/commands/dynamic-tickets.md +676 -0
  34. package/dist/compliance-off/commands/implement.md +736 -0
  35. package/dist/compliance-off/commands/plan.md +671 -0
  36. package/dist/compliance-off/commands/release.md +309 -0
  37. package/dist/compliance-off/commands/resolve.md +742 -0
  38. package/dist/compliance-off/skills/git/references/decision-markers.md +17 -0
  39. package/dist/compliance-off/skills/git/references/learn-conventions.md +80 -0
  40. package/dist/compliance-off/skills/git/references/pr/check-ci-status.md +33 -0
  41. package/dist/compliance-off/skills/git/references/pr/check-merge-readiness.md +49 -0
  42. package/dist/compliance-off/skills/git/references/pr/fetch-review-threads.md +52 -0
  43. package/dist/compliance-off/skills/git/references/pr/validate-branch.md +47 -0
  44. package/dist/compliance-off/skills/git/references/tracker/_contract.md +33 -0
  45. package/dist/compliance-off/skills/git/references/tracker/_mcp.md +153 -0
  46. package/dist/compliance-off/skills/git/references/tracker/github/create-release.md +35 -0
  47. package/dist/compliance-off/skills/git/references/tracker/github/ensure-pr-ready.md +100 -0
  48. package/dist/compliance-off/skills/git/references/tracker/github/ensure-traceable-issue.md +74 -0
  49. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issue.md +69 -0
  50. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issues-batch.md +65 -0
  51. package/dist/compliance-off/skills/git/references/tracker/github/gather-release-evidence.md +48 -0
  52. package/dist/compliance-off/skills/git/references/tracker/github/setup-task.md +77 -0
  53. package/dist/compliance-off/skills/git/references/tracker/jira/create-release.md +35 -0
  54. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-pr-ready.md +100 -0
  55. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-traceable-issue.md +61 -0
  56. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issue.md +51 -0
  57. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issues-batch.md +63 -0
  58. package/dist/compliance-off/skills/git/references/tracker/jira/gather-release-evidence.md +47 -0
  59. package/dist/compliance-off/skills/git/references/tracker/jira/setup-task.md +82 -0
  60. package/dist/compliance-off/skills/git/references/tracker/linear/create-release.md +35 -0
  61. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-pr-ready.md +100 -0
  62. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-traceable-issue.md +61 -0
  63. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issue.md +51 -0
  64. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issues-batch.md +63 -0
  65. package/dist/compliance-off/skills/git/references/tracker/linear/gather-release-evidence.md +47 -0
  66. package/dist/compliance-off/skills/git/references/tracker/linear/setup-task.md +83 -0
  67. package/dist/compliance-off/skills/git/references/trust-rule.md +7 -0
  68. package/dist/core/agent-models.js +19 -4
  69. package/dist/core/assets.js +126 -17
  70. package/dist/core/compliance-off-files.js +27 -0
  71. package/dist/core/compliance.js +14 -0
  72. package/dist/core/curation-variants.js +460 -112
  73. package/dist/core/flags.js +2 -2
  74. package/dist/core/fs-atomic.js +5 -1
  75. package/dist/core/installed-variants.js +63 -0
  76. package/dist/core/manifest.js +48 -5
  77. package/dist/core/mds-variants.js +700 -3
  78. package/dist/core/orphan-sweep.js +29 -2
  79. package/dist/core/path-set.js +15 -0
  80. package/dist/core/plugins.js +68 -10
  81. package/dist/core/reference-sweep.js +42 -25
  82. package/dist/core/retired-shipped-names.js +278 -0
  83. package/dist/curation-compliance-off/agents/triage.md +176 -0
  84. package/dist/curation-compliance-off/commands/bug-analysis.md +349 -0
  85. package/dist/curation-compliance-off/commands/code-review.md +481 -0
  86. package/dist/curation-compliance-off/commands/dynamic-build.md +1211 -0
  87. package/dist/curation-compliance-off/commands/dynamic-tickets.md +634 -0
  88. package/dist/curation-compliance-off/commands/implement.md +689 -0
  89. package/dist/curation-compliance-off/commands/plan.md +631 -0
  90. package/dist/curation-compliance-off/commands/release.md +279 -0
  91. package/dist/curation-compliance-off/commands/resolve.md +692 -0
  92. package/dist/curation-off/agents/code.md +6 -6
  93. package/dist/curation-off/agents/fix.md +6 -6
  94. package/dist/curation-off/agents/harden.md +7 -6
  95. package/dist/curation-off/agents/refactor.md +6 -6
  96. package/dist/curation-off/commands/bug-analysis.md +69 -119
  97. package/dist/curation-off/commands/dynamic-build.md +23 -23
  98. package/dist/curation-off/commands/implement.md +46 -32
  99. package/dist/curation-off/commands/release.md +1 -1
  100. package/dist/curation-off/commands/resolve.md +35 -31
  101. package/dist/curation-off/commands/self-review.md +8 -7
  102. package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
  103. package/dist/targets/claude-code/behavior-install.js +23 -9
  104. package/dist/targets/claude-code/charter-render.js +374 -0
  105. package/dist/targets/claude-code/compliance-install.js +62 -17
  106. package/dist/targets/claude-code/compliance-off-files.js +249 -0
  107. package/dist/targets/claude-code/curation-install.js +632 -117
  108. package/dist/targets/claude-code/folder-swap.js +142 -0
  109. package/dist/targets/claude-code/install-backup.js +780 -0
  110. package/dist/targets/claude-code/installer.js +795 -199
  111. package/dist/targets/claude-code/language-stamp.js +6 -0
  112. package/dist/targets/claude-code/legacy-skill-hashes.js +170 -0
  113. package/dist/targets/claude-code/linked-folder.js +176 -0
  114. package/dist/targets/claude-code/reference-converge.js +131 -0
  115. package/dist/targets/claude-code/rules-folder.js +34 -0
  116. package/dist/targets/claude-code/shipped-set.js +160 -0
  117. package/dist/targets/claude-code/tracker-install.js +21 -8
  118. package/dist/targets/claude-code/tracker-servers.js +20 -6
  119. package/dist/targets/claude-code/workflow-folder-install.js +251 -0
  120. package/dist/variant-axes.json +109 -0
  121. package/package.json +1 -1
  122. package/src/assets/agents/_partials/_code.mds +6 -6
  123. package/src/assets/agents/evaluate.md +1 -1
  124. package/src/assets/agents/git.mds +32 -0
  125. package/src/assets/agents/harden.mds +7 -6
  126. package/src/assets/agents/test.md +21 -12
  127. package/src/assets/agents/triage.mds +2 -0
  128. package/src/assets/agents/validate.md +10 -10
  129. package/src/assets/commands/_partials/_compliance.mds +5 -3
  130. package/src/assets/commands/_partials/_engine.mds +9 -8
  131. package/src/assets/commands/_partials/_publication.mds +10 -1
  132. package/src/assets/commands/bug-analysis.mds +80 -126
  133. package/src/assets/commands/code-review.mds +43 -0
  134. package/src/assets/commands/dynamic-build.mds +109 -15
  135. package/src/assets/commands/dynamic-tickets.mds +13 -0
  136. package/src/assets/commands/implement.mds +145 -33
  137. package/src/assets/commands/plan.mds +19 -0
  138. package/src/assets/commands/release.mds +50 -1
  139. package/src/assets/commands/resolve.mds +217 -32
  140. package/src/assets/commands/self-review.mds +8 -7
  141. package/src/assets/mds/git/_ops.mds +23 -0
  142. package/src/assets/mds/git/_ops_steps.mds +17 -0
  143. package/src/assets/mds/git/_pr.mds +1 -1
  144. package/src/assets/mds/git/_references.mds +6 -0
  145. package/src/assets/mds/tracker/_common.mds +4 -0
  146. package/src/assets/mds/tracker/_github.mds +42 -0
  147. package/src/assets/mds/tracker/_jira.mds +43 -0
  148. package/src/assets/mds/tracker/_linear.mds +43 -0
  149. package/src/assets/rules/quality.md +2 -0
  150. package/src/assets/scripts/ci-wait.cjs +204 -126
  151. package/src/assets/scripts/hooks/assets/implement-installed +1 -0
  152. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +13 -11
  153. package/src/assets/scripts/hooks/preamble +18 -8
  154. package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
  155. package/src/assets/scripts/pr-evidence.cjs +41 -2
  156. package/src/assets/scripts/verify-evidence.cjs +408 -56
  157. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +3 -3
  158. package/src/assets/skills/agent-references/references/commands/plan-levels.md +1 -1
  159. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +6 -2
  160. package/src/assets/skills/agent-references/references/validate/passed-alone.md +7 -7
  161. package/src/assets/skills/docs-framework/SKILL.md +5 -2
  162. package/src/assets/skills/git/SKILL.md +3 -3
  163. package/src/assets/skills/git/references/github-api.md +2 -139
  164. package/src/assets/skills/git/references/github-posting.md +158 -0
  165. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  166. package/src/assets/skills/review-methodology/references/patterns.md +1 -1
  167. package/src/assets/skills/review-methodology/references/report-template.md +1 -2
  168. package/src/assets/skills/testing/SKILL.md +2 -4
@@ -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
 
@@ -285,23 +287,22 @@ A KB with no Rules section labels its entries `Rules ({section name}):` and give
285
287
  #### Plan Artifact
286
288
 
287
289
  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
288
- 2. Read the most recent file that carries an `## Acceptance Criteria` section
289
- 3. Extract `## Acceptance Criteria` section → parse into table: `| ID | Criterion | Type | Testable Condition |`
290
- 4. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
291
- 5. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
290
+ 2. Read the most recent file that carries an `## Acceptance Criteria` section; parse it into the table `| ID | Criterion | Type | Testable Condition |`
291
+ 3. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
292
+ 4. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
292
293
 
293
294
  ### Phase 4: File Analysis
294
295
 
295
296
  **Produces:** ACTIVE_FOCUSES
296
297
  **Requires:** DIFF_RANGE
297
298
 
298
- Determine which focus analyzers to run using `CHANGED_FILES` (already computed in Step 2b — do not re-run `git diff`):
299
+ Pick the focus analyzers from `CHANGED_FILES` (Step 2b; do not re-run `git diff`):
299
300
 
300
301
  | Focus | Condition |
301
302
  |-------|-----------|
302
303
  | `security` | Always |
303
304
  | `functional` | Always |
304
- | `integration` | 2+ distinct directories changed (`dirname` unique count ≥ 2) |
305
+ | `integration` | 2+ distinct directories changed |
305
306
  | `usability` | Any `.tsx`, `.jsx`, `.html`, or `.css` file changed |
306
307
 
307
308
  ### Phase 5: Parallel Bug Analysis
@@ -319,25 +320,20 @@ FOCUS: {focus}
319
320
  DIFF_COMMAND: git diff {DIFF_RANGE}
320
321
  ACCEPTANCE_RULES: {ACCEPTANCE_RULES filtered to this focus type, or (none)}
321
322
  PLAN_CONTEXT: {PLAN_CONTEXT}
322
- STATIC_FINDINGS: {STATIC_FINDINGS if focus == security, else (none)}
323
+ STATIC_FINDINGS: <static-findings>{STATIC_FINDINGS if focus == security, else (none)}</static-findings>
323
324
  DECISIONS_CONTEXT: {DECISIONS_CONTEXT}
324
325
  FEATURE_KNOWLEDGE: {FEATURE_KNOWLEDGE}
325
326
  PR_DESCRIPTION: <pr-description>{PR_DESCRIPTION}</pr-description>
326
327
  OUTPUT_PATH: {ANALYSIS_DIR}/{focus}.md
327
328
  Follow devflow:apply-decisions to Read full ADR/PF bodies on demand.
328
329
  Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE.
329
- IMPORTANT: Write report to {ANALYSIS_DIR}/{focus}.md using Write tool"
330
+ IMPORTANT: Write the report to OUTPUT_PATH using the Write tool"
330
331
  ```
331
332
 
332
- Notes:
333
- - Security analyzer receives full `STATIC_FINDINGS`; all others receive `(none)`
334
- - Filter `ACCEPTANCE_RULES` by the `Type` column matching each focus: security criteria → security analyzer, functional criteria → functional analyzer, etc. Pass the filtered subset only
335
- - Spawn all in a single message for true parallel execution
336
-
337
333
  ### Phase 6: Synthesis
338
334
 
339
335
  **Produces:** BUG_ANALYSIS_SUMMARY
340
- **Requires:** ANALYZER_OUTPUTS, ANALYSIS_DIR, BRANCH_INFO
336
+ **Requires:** ANALYZER_OUTPUTS, ANALYSIS_DIR, BRANCH_INFO, STATIC_STATUS
341
337
 
342
338
  Spawn Synthesize agent:
343
339
 
@@ -347,14 +343,15 @@ Agent(subagent_type="Synthesize", run_in_background=false):
347
343
  ANALYSIS_BASE_DIR: {ANALYSIS_DIR}
348
344
  BRANCH: {branch} -> {base_branch}
349
345
  TIMESTAMP: {timestamp}
346
+ STATIC_STATUS: {STATIC_STATUS}
350
347
  Output: {ANALYSIS_DIR}/bug-analysis-summary.md"
351
348
  ```
352
349
 
353
350
  ### Phase 7: Finalize
354
351
 
355
- **Requires:** BRANCH_INFO, ANALYSIS_DIR
352
+ **Requires:** BRANCH_INFO, ANALYSIS_DIR, STATIC_STATUS
356
353
 
357
- 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.
354
+ 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.
358
355
  2. Report to user:
359
356
 
360
357
  ```
@@ -363,26 +360,22 @@ Output: {ANALYSIS_DIR}/bug-analysis-summary.md"
363
360
  **Branch**: {branch} -> {base_branch}
364
361
  {any base `TRACEABILITY: DEGRADED` line from Phase 1, as returned}
365
362
  **Analysis**: {ANALYSIS_DIR}
363
+ **Static analysis**: {STATIC_STATUS per tool; if a tool did not complete: "marker kept" and the way out; any Snyk filter note}
366
364
 
367
365
  ### Risk Assessment: {risk_level}
368
366
 
369
367
  {brief_reasoning}
370
368
 
371
369
  ### Bug Counts
372
- | Category | CRITICAL | HIGH | MEDIUM | LOW | Total |
373
- |----------|----------|------|--------|-----|-------|
374
- | Security | {n} | {n} | {n} | {n} | {n} |
375
- | Functional | {n} | {n} | {n} | {n} | {n} |
376
- | Integration | {n} | {n} | {n} | {n} | {n} |
377
- | Usability | {n} | {n} | {n} | {n} | {n} |
370
+ {The summary's Bug Summary table: Security, Functional, Integration and Usability rows by CRITICAL, HIGH, MEDIUM, LOW and Total}
378
371
 
379
372
  ### Top Findings
380
- {List top 3-5 bugs by severity and confidence}
373
+ {Top 3-5 bugs by severity and confidence}
381
374
 
382
375
  ### Artifacts
383
376
  - Bug report: {ANALYSIS_DIR}/bug-analysis-summary.md
384
- - Per-focus reports: {ANALYSIS_DIR}/{security|functional|integration|usability}.md
385
- - Static findings: {ANALYSIS_DIR}/static-findings.md (if static analysis ran)
377
+ - Per-focus reports: {ANALYSIS_DIR}/{focus}.md
378
+ - Static findings: {ANALYSIS_DIR}/static-findings.md (if it ran)
386
379
 
387
380
  {if any CRITICAL or HIGH bugs found:}
388
381
  Run `/resolve` to process and fix these findings.
@@ -390,70 +383,26 @@ Run `/resolve` to process and fix these findings.
390
383
 
391
384
  ## Architecture
392
385
 
393
- ```
394
- /bug-analysis (orchestrator — spawns agents only)
395
- │
396
- ├─ Phase 1: Pre-flight
397
- │ └─ Git agent (ensure-pr-ready)
398
- │
399
- ├─ Phase 2: Static Analysis
400
- │ └─ 2a incremental range, 2b changed files (stop if none), 2c tools, 2d semgrep → snyk → codeql
401
- │
402
- ├─ Phase 3: Context Loading
403
- │ ├─ index.md (pre-rendered) → DECISIONS_CONTEXT
404
- │ ├─ Feature knowledge load → FEATURE_KNOWLEDGE
405
- │ └─ {worktree}/.devflow/docs/design/*.md → PLAN_CONTEXT + ACCEPTANCE_RULES
406
- │
407
- ├─ Phase 4: File Analysis
408
- │ └─ Detect active focuses (security + functional always; integration + usability conditional)
409
- │
410
- ├─ Phase 5: Bug Analysis (PARALLEL)
411
- │ └─ Debug agent per active focus (security also reads STATIC_FINDINGS)
412
- │
413
- ├─ Phase 6: Synthesis
414
- │ └─ Synthesize agent (mode: bug-analysis)
415
- │
416
- └─ Phase 7: Finalize
417
- ├─ Write .last-analysis-head
418
- └─ Display results + suggest /resolve if blocking bugs found
419
- ```
386
+ Seven phases: Phase 1 to Phase 7.
420
387
 
421
388
  ## Edge Cases
422
389
 
423
390
  | Case | Handling |
424
391
  |------|----------|
425
- | No new commits since last analysis | Stop: "No new commits since last analysis. Use --full for a full re-analysis." |
426
392
  | Rebase invalidates `.last-analysis-head` SHA | `git cat-file -t` check fails → fallback to full diff |
427
393
  | Zero changed files in DIFF_RANGE with exit 0 | Stop: "No changes to analyze." |
428
- | Same-minute analysis collision | `mkdir` with seconds suffix (`YYYY-MM-DD_HHMMSS`) |
429
- | All static tools unavailable | Warn, proceed with semantic-only analysis |
430
- | `--no-static` flag | Skip Phase 2c and 2d entirely; `STATIC_FINDINGS=(none)` |
431
394
  | `--full` flag | Bypass incremental detection (Step 2a), run full diff from base |
432
- | CodeQL database creation fails | Skip CodeQL, note in output, continue with other tools |
395
+ | 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 |
433
396
 
434
397
  ## Resolve Compatibility
435
398
 
436
- Run `/resolve` after `/bug-analysis` to fix identified bugs. `/resolve` automatically detects and uses bug analysis reports when no code review report exists.
399
+ `/resolve` detects and uses bug analysis reports when no code review report exists.
437
400
 
438
401
  ## Principles
439
402
 
440
- 1. **Orchestration only** — Command spawns agents, doesn't do analysis work itself
441
- 2. **Parallel, not background** — Analyzers spawn in one message with `run_in_background=false`
442
- 3. **Static + semantic** — Two complementary tracks, each validates the other
443
- 4. **Incremental by default** — Only analyze new changes unless `--full` specified
444
- 5. **Verify before reporting** — Debug agents self-verify every finding
445
- 6. **Honest reporting** — Display risk level and counts directly from synthesis
403
+ 1. **Orchestration only** — the command spawns agents and does no analysis itself
404
+ 2. **Incremental by default** — only new changes, unless `--full`
446
405
 
447
406
  ## Phase Completion Checklist
448
407
 
449
- Before reporting results, verify every phase was executed:
450
-
451
- - [ ] Phase 1: Pre-flight → BRANCH_INFO captured, PR_DESCRIPTION fetched (or `(none)`)
452
- - [ ] Phase 2: Static Analysis → STATIC_FINDINGS captured (or `(none)` if skipped/no tools/no findings); ANALYSIS_DIR created; CHANGED_FILES populated
453
- - [ ] Phase 3: Context Loading → DECISIONS_CONTEXT captured, FEATURE_KNOWLEDGE loaded (or `(none)`), PLAN_CONTEXT and ACCEPTANCE_RULES captured (or `(none)`)
454
- - [ ] Phase 4: File Analysis → ACTIVE_FOCUSES determined (security + functional always present)
455
- - [ ] Phase 5: Parallel Bug Analysis → ANALYZER_OUTPUTS captured per active focus; all reports written to ANALYSIS_DIR
456
- - [ ] Phase 6: Synthesis → BUG_ANALYSIS_SUMMARY written to `{ANALYSIS_DIR}/bug-analysis-summary.md`
457
- - [ ] Phase 7: Finalize → `.last-analysis-head` updated; results displayed to user
458
-
459
- If any phase is unchecked, execute it before proceeding.
408
+ 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.
@@ -155,7 +155,7 @@ The following agentType values are valid. Model tiers are shown for reference
155
155
  Before authoring, verify:
156
156
 
157
157
  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."
158
- 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).
158
+ 2. **`agentType` support:** if spawned agents return no results, check that devflow is installed (`devflow init` has been run).
159
159
  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.
160
160
  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.
161
161
 
@@ -357,7 +357,7 @@ After implementing, commit your changes with a conventional-commit message and r
357
357
  );
358
358
 
359
359
  // Phase 3: Gate 1 #1 — post-code pipeline: Polish agent → Harden agent → Validate agent.
360
- // Validate runs last and unconditionally, so its one full run covers every commit the pass made (see gate1_postcode()).
360
+ // Validate runs last and unconditionally, so its one full run covers every commit the pass made.
361
361
  const gate1 = await phase("gate1", async () => {
362
362
  await agent(`Polish and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Polish" });
363
363
 
@@ -416,8 +416,7 @@ Criteria: ${CRITERIA || "see plan"}
416
416
  Return: {"verdict": "PASS" | "FAIL", "rationale": "..."} — FAIL if either answer is no, naming the failing criterion or the drift.`, { agentType: "Evaluate" });
417
417
  evalVerdict = evaluation?.verdict === "PASS" ? "PASS" : "FAIL";
418
418
  if (evalVerdict === "FAIL") {
419
- // fix-and-continue — no re-evaluate, no inline Gate 1. The final Gate 1 (#2)
420
- // after the review pass is the build gate before the branch is handed back.
419
+ // fix-and-continue — no re-evaluate, no inline Gate 1.
421
420
  await agent(`OPERATION: alignment-fix
422
421
  Fix the alignment issues identified by the Evaluate agent on branch ${BRANCH}:
423
422
  ${evaluation?.rationale || "see the Evaluate agent's report"}
@@ -434,17 +433,19 @@ Self-verify your fix compiles, following your Running commands block. Commit fix
434
433
  ${CRITERIA || "(none)"}
435
434
  TEST_PLAN: ${TEST_PLAN || "(none)"}
436
435
  Follow your Running commands block for every scenario command.
437
- Cover: functionality, API contracts, performance, and cover every TEST_PLAN scenario. Report: PASS or FAIL per scenario.`, { agentType: "Test" });
438
- testVerdict = testResult.verdict;
436
+ Cover: functionality, API contracts, performance, and cover every TEST_PLAN scenario.
437
+ Return: {"verdict": "PASS" | "FAIL" | "BLOCKED", "failures": "..."}`, { agentType: "Test" });
438
+ const word = testResult?.verdict?.split?.(/[ (]/)[0];
439
+ testVerdict = ["PASS", "FAIL"].includes(word) ? word : "BLOCKED";
439
440
  if (testVerdict === "FAIL") {
440
441
  // fix-and-continue — no re-test, no inline Gate 1.
441
442
  await agent(`OPERATION: qa-fix
442
443
  Fix the failing acceptance test scenarios on branch ${BRANCH}:
443
- ${testResult.failures}
444
+ ${testResult.failures || "see the Test agent's report"}
444
445
  DECISIONS_CONTEXT: ${DECISIONS_CONTEXT}
445
446
  ${COMPLIANCE_DIRECTIVE}
446
447
  Self-verify your fix compiles and the scenarios pass, following your Running commands block. Commit fixes.`, { agentType: "Fix" });
447
- testVerdict = "FAIL-FIXED"; // issues found, fixes applied, not re-evaluated by design
448
+ testVerdict = "FAIL-FIXED";
448
449
  }
449
450
  }
450
451
 
@@ -590,8 +591,6 @@ Return: {"status": "fixed"|"blocked", "commitShas": ["<sha>"], "unresolved": ["<
590
591
  });
591
592
 
592
593
  // Phase 5.5: Gate 1 #2 — FINAL post-fix gate (Polish agent → Harden agent → full Validate agent, Validate last and unconditional).
593
- // Runs ONCE, after the review pass. Inside the pass the Code agent self-verified its own fixes;
594
- // this is the build gate before the branch is handed back. See gate1_postcode() cadence.
595
594
  const gate1Final = await phase("gate1-final", async () => {
596
595
  await agent(`Polish and reduce complexity of recent changes on branch ${BRANCH}. Commit any improvements.`, { agentType: "Polish" });
597
596
 
@@ -626,9 +625,9 @@ Return: {"verdict": "PASS" | "FAIL", "details": "..."}`, { agentType: "Validate"
626
625
  });
627
626
 
628
627
  // PASS requires survivingFindings.length === 0 && coverageGaps.length === 0 && gate1Final.verdict !== "ESCALATED"
629
- // and no FAIL-FIXED Gate 2 verdict: fixes applied but never re-run report UNVERIFIED, never PASS
628
+ // and every Gate 2 verdict PASS or SKIPPED; any other reports UNVERIFIED, never PASS
630
629
  const coverageGaps = reviewResult.coverageGaps || [];
631
- const gate2Unverified = [gate2.evaluateVerdict, gate2.testVerdict].includes("FAIL-FIXED");
630
+ const gate2Unverified = ![gate2.evaluateVerdict, gate2.testVerdict].every(v => v === "PASS" || v === "SKIPPED");
632
631
  const overallVerdict = (reviewResult.survivingFindings?.length || 0) === 0 && coverageGaps.length === 0 && gate1Final.verdict !== "ESCALATED" ? (gate2Unverified ? "UNVERIFIED" : "PASS") : "PARTIAL";
633
632
 
634
633
  // Phase 6: Report — the phase returns the engine result (engine_output_schema); its verdict is overallVerdict
@@ -674,17 +673,17 @@ This applies to both: multiple Code agents working on a single ticket AND multi-
674
673
 
675
674
  ### Build execution doctrine — the Running commands block (LOAD-BEARING)
676
675
 
677
- 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:
676
+ 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:
678
677
 
679
678
  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}`).
680
679
 
681
- - 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.
682
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
683
- - 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.
684
- - 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.
685
- - 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>`.
680
+ - 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.
681
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
682
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
683
+ - 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.
684
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
686
685
  - Never re-run a command when nothing it reads has changed.
687
- - 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.
686
+ - 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.
688
687
  - The same rules hold inside a dynamic Workflow sub-agent.
689
688
 
690
689
  **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.
@@ -742,6 +741,7 @@ Gate 2 inputs are produced by `/devflow:dynamic-plan`'s plan-challenge step —
742
741
  **Test agent** (only if acceptance criteria or a test plan exist):
743
742
  - Scenario-based acceptance tests covering functionality, API contracts, performance
744
743
  - 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.
744
+ - BLOCKED (no result), or any other or missing verdict → no fix, and the run never reports PASS.
745
745
 
746
746
  **When Gate 2 inputs are absent:**
747
747
  - No plan → skip the Evaluate agent silently (note in output: "Gate 2 Evaluate agent skipped — no plan available")
@@ -910,7 +910,7 @@ Each ticket engine run returns a structured result. The Synthesize agent or the
910
910
  ],
911
911
  "gate2": {
912
912
  "evaluateVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED",
913
- "testVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED",
913
+ "testVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED | BLOCKED",
914
914
  "skipReasons": ["string — why a gate was skipped, if applicable"]
915
915
  }
916
916
  }
@@ -1228,12 +1228,12 @@ Agent(subagent_type="Test"):
1228
1228
  FILES_CHANGED: {the file list that diff printed}
1229
1229
  TEST_PLAN: {the TP lines of the wave evidence file's ## Test Plan section}
1230
1230
  WORKTREE_PATH: {integration worktree root}
1231
- Cover every TEST_PLAN line on the integration branch as it stands. Report PASS or FAIL with evidence."
1231
+ Cover every TEST_PLAN line on the integration branch as it stands. Report your Status with evidence."
1232
1232
  ```
1233
1233
 
1234
- Nothing is fixed here, PASS or FAIL: the wave is done and its PR is open. Report the Test agent's Status.
1234
+ Nothing is fixed here, whatever the Status: the wave is done and its PR is open. Report the Test agent's Status.
1235
1235
 
1236
- **(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:
1236
+ **(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:
1237
1237
 
1238
1238
  ```
1239
1239
  - TP-<n> <PASS|FAIL|SKIP> sha:<head> by:test exit:<0-255>