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
@@ -36,7 +36,7 @@ Run the commands of package.json, Makefile, Cargo.toml or similar in this order,
36
36
  | 3 | Lint | `cargo clippy`, `make lint` | none |
37
37
  | 4 | Test | `npm test`, `make test` | `vitest run --reporter=dot`, `jest --silent`, `pytest -q`, `cargo test -q`, `go test` without `-v`, `gradle -q test`, `mvn -q test` |
38
38
 
39
- Use the quiet form where the project's command runs that tool. After a failed Test row, re-run each of its first 20 failed test files alone, once, verbose, 120 s max, never the suite: this is the one exception to the never-re-run rule under Running commands. First, run `d="${CLAUDE_CONFIG_DIR:-}"; case "$d" in /*) ;; *) d="$HOME/.claude" ;; esac; printf '%s\n' "$d/skills/devflow:agent-references/references/validate/passed-alone.md"` and Read it; re-run only a path it accepts (none if unread). PASSED-ALONE needs all of: a BASE_SHA; a checked, tracked path; a FILES_CHANGED that omits it; a full-run timeout, spawn or resource error; a re-run adding only that file, no name filter; `EXIT=0`; its tests ran and passed. Else REAL; a re-run is never BLOCKED. PASS needs PASSED-ALONE files to explain every failure; the Test row then counts as passed, also for Lint pack, in any output form. Never name labels in Failures, Parsed References or `details`.
39
+ Use the quiet form where the project's command runs that tool. After a failed Test row, re-run each of its first 20 failed test files alone, once, verbose, never the suite: this is the one exception to the never-re-run rule under Running commands. First, run `d="${CLAUDE_CONFIG_DIR:-}"; case "$d" in /*) ;; *) d="$HOME/.claude" ;; esac; printf '%s\n' "$d/skills/devflow:agent-references/references/validate/passed-alone.md"` and Read it; re-run only a path it accepts (none if unread). PASSED-ALONE needs all of: a BASE_SHA; a checked, tracked path; a FILES_CHANGED that omits it; a test timeout, spawn or resource error; a re-run adding only that file, no name filter; `EXIT=0`; its tests ran and passed. Else REAL; a re-run is never BLOCKED. PASS needs PASSED-ALONE files to explain every failure; the Test row then counts as passed, also for Lint pack, in any output form. Never name labels in Failures, Parsed References or `details`.
40
40
 
41
41
  **Gate ownership:** Run the full suite once per HEAD. You are the only agent that does.
42
42
 
@@ -44,20 +44,20 @@ Use the quiet form where the project's command runs that tool. After a failed Te
44
44
 
45
45
  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}`).
46
46
 
47
- - 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.
48
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
49
- - 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.
50
- - 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.
51
- - 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>`.
47
+ - 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.
48
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
49
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
50
+ - 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.
51
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
52
52
  - Never re-run a command when nothing it reads has changed.
53
- - 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.
53
+ - 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.
54
54
  - The same rules hold inside a dynamic Workflow sub-agent.
55
55
 
56
56
  ## Lint pack
57
57
 
58
58
  Only if the first `GATE_LEVEL:` line is exactly `lean`, run it last, once the table commands that exist pass; else never run it or print anything about it. The Lint row never runs it. Use the root `package.json` script `lint-pack`, else the root `Makefile` target; look nowhere else. Neither: not configured.
59
59
 
60
- A first `BASE_SHA:` not matching `^[0-9a-f]{40}$` is an error; else run `npm run -s lint-pack -- <BASE_SHA>` or `make -s lint-pack BASE=<BASE_SHA>` in the capture form with an explicit Bash `timeout`; Read the log. Its last `LINT_PACK:` line decides: `no-config` is not configured, `clean` is clean, `matches <n>` sets FAIL, each record into Parsed References. Any other line, none or a timeout is an error: Status stays; the pack is UNVERIFIED, never a pass. `EXIT=` never decides it; an error is never BLOCKED. Copy the `pack-changed` paths and `skipped-binary` count to the `Lint pack:` line.
60
+ A first `BASE_SHA:` not matching `^[0-9a-f]{40}$` is an error; else run `npm run -s lint-pack -- <BASE_SHA>` or `make -s lint-pack BASE=<BASE_SHA>` in the capture form with an explicit Bash `timeout`; past it, wait for its end notice, then Read the log. Its last `LINT_PACK:` line decides: `no-config` is not configured, `clean` is clean, `matches <n>` sets FAIL, each record into Parsed References. Any other line or none is an error: Status stays; the pack is UNVERIFIED, never a pass. `EXIT=` never decides it; an error is never BLOCKED. Copy the `pack-changed` paths and `skipped-binary` count to the `Lint pack:` line.
61
61
 
62
62
  ## Output
63
63
 
@@ -72,7 +72,7 @@ HEAD: {`git rev-parse HEAD`, read before the first command}
72
72
  | Command | Status | Exit | Duration |
73
73
  |---|---|---|---|
74
74
 
75
- Passed alone: {paths, if any}; bound 20 files, 120 s each
75
+ Passed alone: {paths, if any}; bound 20 files
76
76
 
77
77
  ### Failures (if FAIL)
78
78
 
@@ -98,4 +98,4 @@ Report cap: final message at most about 1,500 tokens; longer material goes to a
98
98
 
99
99
  ## Boundaries
100
100
 
101
- BLOCKED: no validation command found, a table command that cannot start, or missing dependencies.
101
+ BLOCKED: no validation command found, a table command that cannot start or with no `EXIT=` line read, or missing dependencies.
@@ -7,9 +7,11 @@ D-COMPLIANCE-INJECT: the compliance instruction reaches an agent only through a
7
7
  prompt, and only when the resolved lens is on. No Code, Fix, Review or Design body names
8
8
  the compliance skill, so a lens-off spawn pays nothing for it. `compliance_directive()` is the
9
9
  one home of the directive text; `compliance_inject()` is how a pass-through host sets it.
10
- An install-time variant keyed on the machine switch would be wrong: the lens is the union of
11
- the machine, the default branch and the worktree (D-LENS-UNION), so a repository can turn it
12
- on when the machine switch is off.
10
+ No install-time variant carries the lens: it is the union of the machine, the default
11
+ branch and the worktree (D-LENS-UNION), so a repository can turn it on when the machine
12
+ switch is off. The posting steps are the opposite case. They are an install-time variant
13
+ keyed on the machine switch alone, as compliance arms in each host, and a repository key
14
+ cannot restore one.
13
15
 
14
16
  @define compliance_frameworks():
15
17
  `COMPLIANCE_FRAMEWORKS` is the settings line's `COMPLIANCE` with `generic` written `none`: `off`, `none`, or the framework ids the machine and this repository declare.
@@ -36,6 +36,7 @@ Gate 2 inputs are produced by `/devflow:dynamic-plan`'s plan-challenge step —
36
36
  **Test agent** (only if acceptance criteria or a test plan exist):
37
37
  - Scenario-based acceptance tests covering functionality, API contracts, performance
38
38
  - 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.
39
+ - BLOCKED (no result), or any other or missing verdict → no fix, and the run never reports PASS.
39
40
 
40
41
  **When Gate 2 inputs are absent:**
41
42
  - No plan → skip the Evaluate agent silently (note in output: "Gate 2 Evaluate agent skipped — no plan available")
@@ -142,17 +143,17 @@ This applies to both: multiple Code agents working on a single ticket AND multi-
142
143
  @define build_execution_doctrine():
143
144
  ### Build execution doctrine — the Running commands block (LOAD-BEARING)
144
145
 
145
- 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:
146
+ 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:
146
147
 
147
148
  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}`).
148
149
 
149
- - 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.
150
- - Never background a command and wait on it, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
151
- - 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.
152
- - 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.
153
- - 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>`.
150
+ - 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.
151
+ - Never background a build or test, and never poll across turns: no `sleep` or `true` turns, no sentinel-file checks, no Monitor.
152
+ - Prefer the scoped command for the change; for the whole set, one workspace command over per-package loops.
153
+ - 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.
154
+ - A run expected to exceed the ceiling may be split into parts, run in sequence.
154
155
  - Never re-run a command when nothing it reads has changed.
155
- - 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.
156
+ - 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.
156
157
  - The same rules hold inside a dynamic Workflow sub-agent.
157
158
 
158
159
  **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.
@@ -205,7 +206,7 @@ Each ticket engine run returns a structured result. The Synthesize agent or the
205
206
  ],
206
207
  "gate2": {
207
208
  "evaluateVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED",
208
- "testVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED",
209
+ "testVerdict": "PASS | FAIL | FAIL-FIXED | SKIPPED | BLOCKED",
209
210
  "skipReasons": ["string — why a gate was skipped, if applicable"]
210
211
  }
211
212
  }
@@ -3,12 +3,21 @@ A partial: the publication-value gate. D-SETTINGS-LINE: it imports nothing from
3
3
  its consumers, and this text takes `REVIEW_PUBLICATION` from the line resolved
4
4
  there for the worktree's root.
5
5
 
6
- @define publication_gate():
6
+ `publication_resolve` is the two resolution steps alone. `publication_gate` is those steps plus the
7
+ note that names the Git agent's publication gate; a host with compliance off calls only the steps,
8
+ because the gate's reference file is not installed there.
9
+
10
+ @define publication_resolve():
7
11
  **Resolve `REVIEW_PUBLICATION` per worktree:** take `REVIEW_PUBLICATION` from that worktree's settings line — the line resolved above for `{root}`, the worktree's root, by the settings block when this run has not yet resolved that root; multi-worktree repos may resolve different values per worktree. The line already caps the personal choice at the team's (D-PUBLICATION-CEILING), so it is `off`, `auto` or `full`, and `off` when the line was unresolvable.
8
12
 
9
13
  **Evidence stub:** only when `EVIDENCE_POLICY` is `required`, a resolved `off` becomes `stub`, so a counts-only record still reaches the PR. `stub` is never a config value: the settings line never carries it.
14
+ @end
15
+
16
+ @define publication_gate():
17
+ {{publication_resolve()}}
10
18
 
11
19
  Note: `auto` is NOT fail-open — under `auto`, the Git agent probes the repository visibility and treats any error or unrecognised value as PUBLIC (mode STUB). What each value does is decided by the Git agent's publication gate (`references/publication-gate.md` step 2); this partial only resolves the value.
12
20
  @end
13
21
 
22
+ @export publication_resolve
14
23
  @export publication_gate
@@ -9,15 +9,11 @@ output-dir: dist/commands
9
9
  @import "./_partials/_settings.mds" as settings
10
10
  # Bug Analysis Command
11
11
 
12
- 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.
12
+ Run a proactive bug analysis on the current branch: static analysis tools and parallel semantic analyzers, synthesized into one bug report.
13
13
 
14
14
  ## Usage
15
15
 
16
- ```
17
- /bug-analysis (analyze current branch — incremental if prior analysis exists)
18
- /bug-analysis --full (force full analysis, ignore incremental state)
19
- /bug-analysis --no-static (skip static analysis track, semantic-only)
20
- ```
16
+ `/bug-analysis [--full] [--no-static]`: `--full` ignores incremental state; `--no-static` skips static analysis.
21
17
 
22
18
  ## Phases
23
19
 
@@ -28,7 +24,11 @@ Run a proactive bug analysis on the current branch by combining static analysis
28
24
  {{evidence_policy()}}
29
25
 
30
26
  {{docs_root()}}
27
+ <!-- compliance:off -->
28
+ When `EVIDENCE_POLICY` is `required`, record `TRACEABILITY: DEGRADED (compliance off on this machine; the repository requires evidence)` once, for the Phase 7 report.
29
+ <!-- compliance:end -->
31
30
 
31
+ <!-- compliance:on -->
32
32
  Render the test-plan block from `/implement`'s evidence file, and from nothing else:
33
33
  1. `branch_slug` is `git -C "{worktree}" branch --show-current` with every `/` replaced by `-`.
34
34
  2. Only when `branch_slug` matches `^[A-Za-z0-9._-]{1,200}$` and the file exists, run (the path double-quoted):
@@ -39,12 +39,15 @@ node "$HOME/.devflow/scripts/verify-evidence.cjs" render --plan "{worktree}/.dev
39
39
 
40
40
  3. On `exit=0`, `PR_TEST_PLAN_BLOCK` is its stdout byte for byte without that `exit=` line; in every other case it is `(none)`. Nothing here waits on it: the Git agent pastes it only behind its own check, and only into a PR it creates.
41
41
 
42
+ <!-- compliance:end -->
42
43
  Spawn Git agent:
43
44
 
44
45
  ```
45
46
  Agent(subagent_type="Git", run_in_background=false):
46
47
  "OPERATION: ensure-pr-ready
48
+ <!-- compliance:on -->
47
49
  PR_TEST_PLAN_BLOCK: {PR_TEST_PLAN_BLOCK verbatim, or (none)}
50
+ <!-- compliance:end -->
48
51
  APPLY_CONVENTIONS: {APPLY_CONVENTIONS}
49
52
  Validate branch, commit if needed, push, create PR if needed.
50
53
  Return: branch, base_branch, base_ref, branch-slug, PR#"
@@ -88,7 +91,7 @@ If `pr_number` is absent, set `PR_DESCRIPTION` to `(none)`.
88
91
  CHANGED_FILES=$(git diff --name-only {DIFF_RANGE})
89
92
  ```
90
93
 
91
- Store as `CHANGED_FILES`; Steps 2d and Phase 4 read it. A non-zero diff exit stops.
94
+ Store as `CHANGED_FILES`; Phase 4 reads it. A non-zero diff exit stops.
92
95
 
93
96
  If output is empty → "No changes to analyze." Stop.
94
97
 
@@ -96,75 +99,81 @@ If output is empty → "No changes to analyze." Stop.
96
99
 
97
100
  **Produces:** STATIC_TOOL_STATUS
98
101
 
99
- Skip if `--no-static` flag provided.
102
+ 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.
100
103
 
101
104
  ```bash
102
- SEMGREP_AVAILABLE=$(which semgrep 2>/dev/null && echo "yes" || echo "no")
103
- SNYK_AVAILABLE=$(which snyk 2>/dev/null && echo "yes" || echo "no")
104
- CODEQL_AVAILABLE=$(which codeql 2>/dev/null && echo "yes" || echo "no")
105
+ for t in semgrep snyk codeql jq; do printf '%s=%s\n' "$t" "$(command -v "$t" || echo no)"; done
106
+ 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"
105
107
  ```
106
108
 
107
- 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)`.
109
+ 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.
110
+
111
+ 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.
112
+
113
+ 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`.
108
114
 
109
115
  If some are available: note which tools will run.
110
116
 
111
117
  #### Step 2d: Tiered Static Analysis
112
118
 
113
- **Produces:** STATIC_FINDINGS
119
+ **Produces:** STATIC_FINDINGS, STATIC_STATUS
114
120
  **Requires:** STATIC_TOOL_STATUS, DIFF_RANGE, ANALYSIS_DIR
115
121
 
116
- Skip if `--no-static` flag provided or all tools unavailable.
117
-
118
- Run available tools on the changed files (from `CHANGED_FILES` computed in Step 2b).
122
+ Skip if Step 2c set `STATIC_STATUS`.
119
123
 
120
- **Semgrep and Snyk run in parallel** — launch both in the background, then wait for both before proceeding to CodeQL. CodeQL is conditional and sequential.
124
+ 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 `--`.
121
125
 
122
126
  **Semgrep** (if available):
123
127
  ```bash
124
- # tr '\n' '\0' + xargs -0 is portable across GNU and BSD xargs (macOS ships BSD xargs, which lacks -d)
125
- echo "$CHANGED_FILES" | tr '\n' '\0' | xargs -0 timeout 300 semgrep scan --config auto --sarif --quiet 2>/dev/null
128
+ cd "{worktree}" || exit
129
+ 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=$?
130
+ mv "{ANALYSIS_DIR}"/semgrep.{part,sarif}
131
+ printf '\nexit=%s\n' "$E"
126
132
  ```
127
- Parse SARIF output → extract findings.
128
133
 
129
134
  **Snyk Code** (if available):
130
135
  ```bash
131
- # One project-level scan (a per-file xargs run would invoke snyk O(n) times; --file is for dependency scans).
132
- SNYK_SARIF=$(timeout 300 snyk code test --sarif 2>/dev/null)
133
- # Programmatic filter: keep only results whose file path is in CHANGED_FILES — defense-in-depth, before the LLM agent sees any output.
134
- SNYK_FILTERED=$(echo "$SNYK_SARIF" | jq --argjson files "$(echo "$CHANGED_FILES" | jq -R . | jq -s .)" \
135
- '.runs[].results |= map(select(.locations[].physicalLocation.artifactLocation.uri as $uri | $files | index($uri) != null))' \
136
- 2>/dev/null || echo "$SNYK_SARIF")
136
+ cd "{worktree}" || exit
137
+ snyk code test --sarif >"{ANALYSIS_DIR}/snyk-all.json" 2>/dev/null; E=$?
138
+ git diff --name-only -z {DIFF_RANGE} >"{ANALYSIS_DIR}/changed.z" && jq --rawfile n "{ANALYSIS_DIR}/changed.z" \
139
+ '($n|split("\u0000")) as $f|.runs[]?.results|=[.[]?|select([.locations[]?.physicalLocation.artifactLocation.uri//empty] as $u|($u|length)==0 or any($u[];IN($f[])))]' \
140
+ "{ANALYSIS_DIR}/snyk-all.json" >"{ANALYSIS_DIR}/snyk.sarif" \
141
+ && jq -nr '[inputs|[.runs[]?.results[]?]|length]|"total=\(.[0]) kept=\(.[1])"' "{ANALYSIS_DIR}/snyk-all.json" "{ANALYSIS_DIR}/snyk.sarif" \
142
+ || { echo "Snyk scope filter failed: unfiltered"; cp "{ANALYSIS_DIR}/snyk-all.json" "{ANALYSIS_DIR}/snyk.sarif"; }
143
+ printf '\nexit=%s\n' "$E"
137
144
  ```
138
- Parse `SNYK_FILTERED` SARIF → extract findings. If `jq` is unavailable, fall back to the raw `SNYK_SARIF` and note the limitation.
145
+
146
+ **Counts and table** (after Snyk with `{tools}` = `semgrep snyk`; after CodeQL with `codeql`):
147
+ ```bash
148
+ cd "{ANALYSIS_DIR}" || exit
149
+ L='def L($r):.ruleId as $i|.level//([$r.tool|.driver,.extensions[]?|.rules[]?|select(.id==$i)]|.[0].defaultConfiguration.level)//"warning";'
150
+ for t in {tools}; do [ -f $t.sarif ] || continue
151
+ 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"
152
+ 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"
153
+ done
154
+ cat findings-{semgrep,snyk,codeql}.tsv >findings.tsv 2>/dev/null
155
+ 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"
156
+ ```
157
+ Read `error` as HIGH, `warning` as MEDIUM, `note` as LOW.
139
158
 
140
159
  **CodeQL** (if available AND (`--full` OR Semgrep/Snyk found HIGH/CRITICAL findings)):
141
160
  ```bash
142
- # Use a unique temp directory per run to prevent symlink attacks and concurrent-process clobbering
143
- CODEQL_TMP=$(mktemp -d)
144
- # Register cleanup trap immediately after mktemp — ensures rm -rf runs on EXIT, SIGTERM, and SIGINT
145
- # even if the session is interrupted before reaching the explicit rm -rf below
161
+ CODEQL_TMP=$(mktemp -d) || exit
146
162
  trap 'rm -rf "${CODEQL_TMP}"' EXIT INT TERM
147
- timeout 600 codeql database create "${CODEQL_TMP}/db" --language={detected-language} --source-root=. 2>/dev/null && \
148
- timeout 600 codeql database analyze "${CODEQL_TMP}/db" --format=sarif-latest --output="${CODEQL_TMP}/results.sarif" 2>/dev/null
149
- # Capture exit status before cleanup so cleanup doesn't mask failures
163
+ { codeql database create "${CODEQL_TMP}/db" --language={lang} --source-root="{worktree}" && \
164
+ codeql database analyze "${CODEQL_TMP}/db" --format=sarif-latest --output="{ANALYSIS_DIR}/codeql.sarif"; } >"{ANALYSIS_DIR}/codeql.log" 2>&1
150
165
  CODEQL_EXIT=$?
151
- # Parse SARIF output BEFORE cleanup — rm -rf destroys results.sarif
152
- CODEQL_SARIF=$(cat "${CODEQL_TMP}/results.sarif" 2>/dev/null || echo "")
153
- # Explicit cleanup (trap also covers this path; explicit rm is belt-and-suspenders)
166
+ printf '\nexit=%s\n' "$CODEQL_EXIT"
154
167
  rm -rf "${CODEQL_TMP}"
155
168
  trap - EXIT INT TERM
156
169
  ```
157
- 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.
170
+ 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).
158
171
 
159
- **Normalize** all findings to unified table, cap at top 50 by severity. Truncate each Description entry to 200 characters maximum:
172
+ `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`.
160
173
 
161
- | Tool | File:Line | CWE | Severity | Title | Description |
162
- |------|-----------|-----|----------|-------|-------------|
163
- | {tool} | {file}:{line} | {CWE or —} | {CRITICAL/HIGH/MEDIUM/LOW} | {title} | {description truncated to 200 chars} |
174
+ `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.
164
175
 
165
- Write ALL raw findings to `{ANALYSIS_DIR}/static-findings.md`. Set `STATIC_FINDINGS` to the top-50 table.
166
-
167
- If no tool produced findings: set `STATIC_FINDINGS` to `(none)`.
176
+ 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.
168
177
 
169
178
  ### Phase 3: Context Loading
170
179
 
@@ -188,23 +197,22 @@ If no tool produced findings: set `STATIC_FINDINGS` to `(none)`.
188
197
  #### Plan Artifact
189
198
 
190
199
  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
191
- 2. Read the most recent file that carries an `## Acceptance Criteria` section
192
- 3. Extract `## Acceptance Criteria` section → parse into table: `| ID | Criterion | Type | Testable Condition |`
193
- 4. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
194
- 5. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
200
+ 2. Read the most recent file that carries an `## Acceptance Criteria` section; parse it into the table `| ID | Criterion | Type | Testable Condition |`
201
+ 3. Set `PLAN_CONTEXT` to plan summary; `ACCEPTANCE_RULES` to the table
202
+ 4. If no plan files exist or section not found: `PLAN_CONTEXT=(none)`, `ACCEPTANCE_RULES=(none)`
195
203
 
196
204
  ### Phase 4: File Analysis
197
205
 
198
206
  **Produces:** ACTIVE_FOCUSES
199
207
  **Requires:** DIFF_RANGE
200
208
 
201
- Determine which focus analyzers to run using `CHANGED_FILES` (already computed in Step 2b — do not re-run `git diff`):
209
+ Pick the focus analyzers from `CHANGED_FILES` (Step 2b; do not re-run `git diff`):
202
210
 
203
211
  | Focus | Condition |
204
212
  |-------|-----------|
205
213
  | `security` | Always |
206
214
  | `functional` | Always |
207
- | `integration` | 2+ distinct directories changed (`dirname` unique count ≥ 2) |
215
+ | `integration` | 2+ distinct directories changed |
208
216
  | `usability` | Any `.tsx`, `.jsx`, `.html`, or `.css` file changed |
209
217
 
210
218
  ### Phase 5: Parallel Bug Analysis
@@ -226,7 +234,7 @@ FOCUS: {focus}
226
234
  DIFF_COMMAND: git diff {DIFF_RANGE}
227
235
  ACCEPTANCE_RULES: {ACCEPTANCE_RULES filtered to this focus type, or (none)}
228
236
  PLAN_CONTEXT: {PLAN_CONTEXT}
229
- STATIC_FINDINGS: {STATIC_FINDINGS if focus == security, else (none)}
237
+ STATIC_FINDINGS: <static-findings>{STATIC_FINDINGS if focus == security, else (none)}</static-findings>
230
238
  <!-- curation:on -->
231
239
  DECISIONS_CONTEXT: {DECISIONS_CONTEXT}
232
240
  <!-- curation:end -->
@@ -237,18 +245,13 @@ OUTPUT_PATH: {ANALYSIS_DIR}/{focus}.md
237
245
  Follow devflow:apply-decisions to Read full ADR/PF bodies on demand.
238
246
  <!-- curation:end -->
239
247
  Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE.
240
- IMPORTANT: Write report to {ANALYSIS_DIR}/{focus}.md using Write tool"
248
+ IMPORTANT: Write the report to OUTPUT_PATH using the Write tool"
241
249
  ```
242
250
 
243
- Notes:
244
- - Security analyzer receives full `STATIC_FINDINGS`; all others receive `(none)`
245
- - Filter `ACCEPTANCE_RULES` by the `Type` column matching each focus: security criteria → security analyzer, functional criteria → functional analyzer, etc. Pass the filtered subset only
246
- - Spawn all in a single message for true parallel execution
247
-
248
251
  ### Phase 6: Synthesis
249
252
 
250
253
  **Produces:** BUG_ANALYSIS_SUMMARY
251
- **Requires:** ANALYZER_OUTPUTS, ANALYSIS_DIR, BRANCH_INFO
254
+ **Requires:** ANALYZER_OUTPUTS, ANALYSIS_DIR, BRANCH_INFO, STATIC_STATUS
252
255
 
253
256
  Spawn Synthesize agent:
254
257
 
@@ -258,42 +261,43 @@ Agent(subagent_type="Synthesize", run_in_background=false):
258
261
  ANALYSIS_BASE_DIR: {ANALYSIS_DIR}
259
262
  BRANCH: {branch} -> {base_branch}
260
263
  TIMESTAMP: {timestamp}
264
+ STATIC_STATUS: {STATIC_STATUS}
261
265
  Output: {ANALYSIS_DIR}/bug-analysis-summary.md"
262
266
  ```
263
267
 
264
268
  ### Phase 7: Finalize
265
269
 
266
- **Requires:** BRANCH_INFO, ANALYSIS_DIR
270
+ **Requires:** BRANCH_INFO, ANALYSIS_DIR, STATIC_STATUS
267
271
 
268
- 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.
272
+ 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.
269
273
  2. Report to user:
270
274
 
271
275
  ```
272
276
  ## Bug Analysis Complete
273
277
 
274
278
  **Branch**: {branch} -> {base_branch}
279
+ <!-- compliance:on -->
275
280
  {any base `TRACEABILITY: DEGRADED` line from Phase 1, as returned}
281
+ <!-- compliance:off -->
282
+ {any base `TRACEABILITY: DEGRADED` line from Phase 1, as returned, and the compliance-off line of Phase 1}
283
+ <!-- compliance:end -->
276
284
  **Analysis**: {ANALYSIS_DIR}
285
+ **Static analysis**: {STATIC_STATUS per tool; if a tool did not complete: "marker kept" and the way out; any Snyk filter note}
277
286
 
278
287
  ### Risk Assessment: {risk_level}
279
288
 
280
289
  {brief_reasoning}
281
290
 
282
291
  ### Bug Counts
283
- | Category | CRITICAL | HIGH | MEDIUM | LOW | Total |
284
- |----------|----------|------|--------|-----|-------|
285
- | Security | {n} | {n} | {n} | {n} | {n} |
286
- | Functional | {n} | {n} | {n} | {n} | {n} |
287
- | Integration | {n} | {n} | {n} | {n} | {n} |
288
- | Usability | {n} | {n} | {n} | {n} | {n} |
292
+ {The summary's Bug Summary table: Security, Functional, Integration and Usability rows by CRITICAL, HIGH, MEDIUM, LOW and Total}
289
293
 
290
294
  ### Top Findings
291
- {List top 3-5 bugs by severity and confidence}
295
+ {Top 3-5 bugs by severity and confidence}
292
296
 
293
297
  ### Artifacts
294
298
  - Bug report: {ANALYSIS_DIR}/bug-analysis-summary.md
295
- - Per-focus reports: {ANALYSIS_DIR}/{security|functional|integration|usability}.md
296
- - Static findings: {ANALYSIS_DIR}/static-findings.md (if static analysis ran)
299
+ - Per-focus reports: {ANALYSIS_DIR}/{focus}.md
300
+ - Static findings: {ANALYSIS_DIR}/static-findings.md (if it ran)
297
301
 
298
302
  {if any CRITICAL or HIGH bugs found:}
299
303
  Run `/resolve` to process and fix these findings.
@@ -301,76 +305,26 @@ Run `/resolve` to process and fix these findings.
301
305
 
302
306
  ## Architecture
303
307
 
304
- ```
305
- /bug-analysis (orchestrator — spawns agents only)
306
- │
307
- ├─ Phase 1: Pre-flight
308
- │ └─ Git agent (ensure-pr-ready)
309
- │
310
- ├─ Phase 2: Static Analysis
311
- │ └─ 2a incremental range, 2b changed files (stop if none), 2c tools, 2d semgrep → snyk → codeql
312
- │
313
- ├─ Phase 3: Context Loading
314
- <!-- curation:on -->
315
- │ ├─ index.md (pre-rendered) → DECISIONS_CONTEXT
316
- <!-- curation:end -->
317
- │ ├─ Feature knowledge load → FEATURE_KNOWLEDGE
318
- │ └─ {worktree}/.devflow/docs/design/*.md → PLAN_CONTEXT + ACCEPTANCE_RULES
319
- │
320
- ├─ Phase 4: File Analysis
321
- │ └─ Detect active focuses (security + functional always; integration + usability conditional)
322
- │
323
- ├─ Phase 5: Bug Analysis (PARALLEL)
324
- │ └─ Debug agent per active focus (security also reads STATIC_FINDINGS)
325
- │
326
- ├─ Phase 6: Synthesis
327
- │ └─ Synthesize agent (mode: bug-analysis)
328
- │
329
- └─ Phase 7: Finalize
330
- ├─ Write .last-analysis-head
331
- └─ Display results + suggest /resolve if blocking bugs found
332
- ```
308
+ Seven phases: Phase 1 to Phase 7.
333
309
 
334
310
  ## Edge Cases
335
311
 
336
312
  | Case | Handling |
337
313
  |------|----------|
338
- | No new commits since last analysis | Stop: "No new commits since last analysis. Use --full for a full re-analysis." |
339
314
  | Rebase invalidates `.last-analysis-head` SHA | `git cat-file -t` check fails → fallback to full diff |
340
315
  | Zero changed files in DIFF_RANGE with exit 0 | Stop: "No changes to analyze." |
341
- | Same-minute analysis collision | `mkdir` with seconds suffix (`YYYY-MM-DD_HHMMSS`) |
342
- | All static tools unavailable | Warn, proceed with semantic-only analysis |
343
- | `--no-static` flag | Skip Phase 2c and 2d entirely; `STATIC_FINDINGS=(none)` |
344
316
  | `--full` flag | Bypass incremental detection (Step 2a), run full diff from base |
345
- | CodeQL database creation fails | Skip CodeQL, note in output, continue with other tools |
317
+ | 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 |
346
318
 
347
319
  ## Resolve Compatibility
348
320
 
349
- Run `/resolve` after `/bug-analysis` to fix identified bugs. `/resolve` automatically detects and uses bug analysis reports when no code review report exists.
321
+ `/resolve` detects and uses bug analysis reports when no code review report exists.
350
322
 
351
323
  ## Principles
352
324
 
353
- 1. **Orchestration only** — Command spawns agents, doesn't do analysis work itself
354
- 2. **Parallel, not background** — Analyzers spawn in one message with `run_in_background=false`
355
- 3. **Static + semantic** — Two complementary tracks, each validates the other
356
- 4. **Incremental by default** — Only analyze new changes unless `--full` specified
357
- 5. **Verify before reporting** — Debug agents self-verify every finding
358
- 6. **Honest reporting** — Display risk level and counts directly from synthesis
325
+ 1. **Orchestration only** — the command spawns agents and does no analysis itself
326
+ 2. **Incremental by default** — only new changes, unless `--full`
359
327
 
360
328
  ## Phase Completion Checklist
361
329
 
362
- Before reporting results, verify every phase was executed:
363
-
364
- - [ ] Phase 1: Pre-flight → BRANCH_INFO captured, PR_DESCRIPTION fetched (or `(none)`)
365
- - [ ] Phase 2: Static Analysis → STATIC_FINDINGS captured (or `(none)` if skipped/no tools/no findings); ANALYSIS_DIR created; CHANGED_FILES populated
366
- <!-- curation:on -->
367
- - [ ] Phase 3: Context Loading → DECISIONS_CONTEXT captured, FEATURE_KNOWLEDGE loaded (or `(none)`), PLAN_CONTEXT and ACCEPTANCE_RULES captured (or `(none)`)
368
- <!-- curation:off -->
369
- - [ ] Phase 3: Context Loading → FEATURE_KNOWLEDGE loaded (or `(none)`), PLAN_CONTEXT and ACCEPTANCE_RULES captured (or `(none)`)
370
- <!-- curation:end -->
371
- - [ ] Phase 4: File Analysis → ACTIVE_FOCUSES determined (security + functional always present)
372
- - [ ] Phase 5: Parallel Bug Analysis → ANALYZER_OUTPUTS captured per active focus; all reports written to ANALYSIS_DIR
373
- - [ ] Phase 6: Synthesis → BUG_ANALYSIS_SUMMARY written to `{ANALYSIS_DIR}/bug-analysis-summary.md`
374
- - [ ] Phase 7: Finalize → `.last-analysis-head` updated; results displayed to user
375
-
376
- If any phase is unchecked, execute it before proceeding.
330
+ 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.