devflow-kit 3.8.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 (211) hide show
  1. package/CHANGELOG.md +231 -0
  2. package/README.md +6 -6
  3. package/dist/agents/code.md +10 -10
  4. package/dist/agents/fix.md +7 -7
  5. package/dist/agents/git.md +2 -2
  6. package/dist/agents/harden.md +7 -6
  7. package/dist/agents/refactor.md +6 -6
  8. package/dist/agents/skim.md +1 -1
  9. package/dist/agents/triage.md +7 -0
  10. package/dist/cli/commands/ambient.js +10 -2
  11. package/dist/cli/commands/compliance-prompts.js +9 -4
  12. package/dist/cli/commands/compliance.js +171 -24
  13. package/dist/cli/commands/curation.js +79 -23
  14. package/dist/cli/commands/gate-level-prompts.js +39 -23
  15. package/dist/cli/commands/gates.js +6 -7
  16. package/dist/cli/commands/init.js +150 -145
  17. package/dist/cli/commands/install-report.js +34 -0
  18. package/dist/cli/commands/legacy-sweep.js +64 -0
  19. package/dist/cli/commands/memory.js +59 -12
  20. package/dist/cli/commands/proxy.js +6 -1
  21. package/dist/cli/commands/rules.js +29 -12
  22. package/dist/cli/commands/skills.js +42 -9
  23. package/dist/cli/commands/tracker.js +9 -3
  24. package/dist/cli/commands/uninstall.js +354 -60
  25. package/dist/commands/bug-analysis.md +71 -122
  26. package/dist/commands/code-review.md +11 -11
  27. package/dist/commands/debug.md +2 -2
  28. package/dist/commands/dynamic-build.md +25 -25
  29. package/dist/commands/dynamic-plan.md +2 -2
  30. package/dist/commands/dynamic-profile.md +2 -2
  31. package/dist/commands/dynamic-tickets.md +2 -2
  32. package/dist/commands/explore.md +2 -2
  33. package/dist/commands/implement.md +48 -34
  34. package/dist/commands/plan.md +2 -2
  35. package/dist/commands/release.md +3 -3
  36. package/dist/commands/research.md +2 -2
  37. package/dist/commands/resolve.md +50 -46
  38. package/dist/commands/self-review.md +10 -9
  39. package/dist/compliance-off/agents/git.md +120 -0
  40. package/dist/compliance-off/agents/triage.md +180 -0
  41. package/dist/compliance-off/commands/bug-analysis.md +398 -0
  42. package/dist/compliance-off/commands/code-review.md +520 -0
  43. package/dist/compliance-off/commands/dynamic-build.md +1251 -0
  44. package/dist/compliance-off/commands/dynamic-tickets.md +676 -0
  45. package/dist/compliance-off/commands/implement.md +736 -0
  46. package/dist/compliance-off/commands/plan.md +671 -0
  47. package/dist/compliance-off/commands/release.md +309 -0
  48. package/dist/compliance-off/commands/resolve.md +742 -0
  49. package/dist/compliance-off/skills/git/references/decision-markers.md +17 -0
  50. package/dist/compliance-off/skills/git/references/learn-conventions.md +80 -0
  51. package/dist/compliance-off/skills/git/references/pr/check-ci-status.md +33 -0
  52. package/dist/compliance-off/skills/git/references/pr/check-merge-readiness.md +49 -0
  53. package/dist/compliance-off/skills/git/references/pr/fetch-review-threads.md +52 -0
  54. package/dist/compliance-off/skills/git/references/pr/validate-branch.md +47 -0
  55. package/dist/compliance-off/skills/git/references/tracker/_contract.md +33 -0
  56. package/dist/compliance-off/skills/git/references/tracker/_mcp.md +153 -0
  57. package/dist/compliance-off/skills/git/references/tracker/github/create-release.md +35 -0
  58. package/dist/compliance-off/skills/git/references/tracker/github/ensure-pr-ready.md +100 -0
  59. package/dist/compliance-off/skills/git/references/tracker/github/ensure-traceable-issue.md +74 -0
  60. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issue.md +69 -0
  61. package/dist/compliance-off/skills/git/references/tracker/github/fetch-issues-batch.md +65 -0
  62. package/dist/compliance-off/skills/git/references/tracker/github/gather-release-evidence.md +48 -0
  63. package/dist/compliance-off/skills/git/references/tracker/github/setup-task.md +77 -0
  64. package/dist/compliance-off/skills/git/references/tracker/jira/create-release.md +35 -0
  65. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-pr-ready.md +100 -0
  66. package/dist/compliance-off/skills/git/references/tracker/jira/ensure-traceable-issue.md +61 -0
  67. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issue.md +51 -0
  68. package/dist/compliance-off/skills/git/references/tracker/jira/fetch-issues-batch.md +63 -0
  69. package/dist/compliance-off/skills/git/references/tracker/jira/gather-release-evidence.md +47 -0
  70. package/dist/compliance-off/skills/git/references/tracker/jira/setup-task.md +82 -0
  71. package/dist/compliance-off/skills/git/references/tracker/linear/create-release.md +35 -0
  72. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-pr-ready.md +100 -0
  73. package/dist/compliance-off/skills/git/references/tracker/linear/ensure-traceable-issue.md +61 -0
  74. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issue.md +51 -0
  75. package/dist/compliance-off/skills/git/references/tracker/linear/fetch-issues-batch.md +63 -0
  76. package/dist/compliance-off/skills/git/references/tracker/linear/gather-release-evidence.md +47 -0
  77. package/dist/compliance-off/skills/git/references/tracker/linear/setup-task.md +83 -0
  78. package/dist/compliance-off/skills/git/references/trust-rule.md +7 -0
  79. package/dist/core/agent-models.js +19 -4
  80. package/dist/core/area-pause-notice.js +44 -0
  81. package/dist/core/assets.js +126 -17
  82. package/dist/core/compliance-off-files.js +27 -0
  83. package/dist/core/curation-move-skip.js +4 -5
  84. package/dist/core/curation-queue-cleanup.js +6 -5
  85. package/dist/core/curation-store.js +1 -0
  86. package/dist/core/curation-variants.js +460 -112
  87. package/dist/core/evidence-policy.js +10 -8
  88. package/dist/core/flags.js +2 -2
  89. package/dist/core/installed-variants.js +63 -0
  90. package/dist/core/manifest.js +48 -5
  91. package/dist/core/mds-variants.js +700 -3
  92. package/dist/core/orphan-sweep.js +11 -2
  93. package/dist/core/plugins.js +69 -12
  94. package/dist/core/tracked-areas.js +138 -14
  95. package/dist/curation-compliance-off/agents/triage.md +176 -0
  96. package/dist/curation-compliance-off/commands/bug-analysis.md +349 -0
  97. package/dist/curation-compliance-off/commands/code-review.md +481 -0
  98. package/dist/curation-compliance-off/commands/dynamic-build.md +1211 -0
  99. package/dist/curation-compliance-off/commands/dynamic-tickets.md +634 -0
  100. package/dist/curation-compliance-off/commands/implement.md +689 -0
  101. package/dist/curation-compliance-off/commands/plan.md +631 -0
  102. package/dist/curation-compliance-off/commands/release.md +279 -0
  103. package/dist/curation-compliance-off/commands/resolve.md +692 -0
  104. package/dist/curation-off/agents/code.md +10 -10
  105. package/dist/curation-off/agents/fix.md +7 -7
  106. package/dist/curation-off/agents/harden.md +7 -6
  107. package/dist/curation-off/agents/refactor.md +6 -6
  108. package/dist/curation-off/agents/triage.md +7 -0
  109. package/dist/curation-off/commands/bug-analysis.md +69 -119
  110. package/dist/curation-off/commands/code-review.md +11 -11
  111. package/dist/curation-off/commands/debug.md +2 -2
  112. package/dist/curation-off/commands/dynamic-build.md +25 -25
  113. package/dist/curation-off/commands/explore.md +2 -2
  114. package/dist/curation-off/commands/implement.md +48 -34
  115. package/dist/curation-off/commands/plan.md +2 -2
  116. package/dist/curation-off/commands/release.md +1 -1
  117. package/dist/curation-off/commands/resolve.md +50 -46
  118. package/dist/curation-off/commands/self-review.md +10 -9
  119. package/dist/hud/components/curation-counts.js +27 -0
  120. package/dist/skills/git/references/pr/post-resolution-summary.md +12 -11
  121. package/dist/skills/git/references/pr/post-review-summary.md +12 -11
  122. package/dist/skills/git/references/pr/resolve-review-threads.md +1 -1
  123. package/dist/skills/git/references/pr/update-pr-evidence.md +2 -2
  124. package/dist/skills/git/references/publication-gate.md +6 -7
  125. package/dist/skills/git/references/tracker/github/manage-debt.md +6 -2
  126. package/dist/skills/git/references/tracker/jira/manage-debt.md +6 -2
  127. package/dist/skills/git/references/tracker/linear/manage-debt.md +6 -2
  128. package/dist/targets/claude-code/behavior-install.js +23 -9
  129. package/dist/targets/claude-code/charter-render.js +374 -0
  130. package/dist/targets/claude-code/compliance-install.js +32 -12
  131. package/dist/targets/claude-code/compliance-off-files.js +245 -0
  132. package/dist/targets/claude-code/curation-install.js +570 -100
  133. package/dist/targets/claude-code/installer.js +467 -62
  134. package/dist/targets/claude-code/language-stamp.js +6 -0
  135. package/dist/targets/claude-code/linked-folder.js +182 -0
  136. package/dist/targets/claude-code/reference-converge.js +118 -0
  137. package/dist/targets/claude-code/tracker-install.js +21 -8
  138. package/dist/targets/claude-code/tracker-servers.js +20 -6
  139. package/dist/targets/claude-code/workflow-folder-install.js +231 -0
  140. package/dist/variant-axes.json +109 -0
  141. package/package.json +1 -1
  142. package/src/assets/agents/_partials/_code.mds +6 -6
  143. package/src/assets/agents/code.mds +4 -4
  144. package/src/assets/agents/evaluate.md +7 -13
  145. package/src/assets/agents/fix.mds +1 -1
  146. package/src/assets/agents/git.mds +34 -2
  147. package/src/assets/agents/harden.mds +7 -6
  148. package/src/assets/agents/skim.mds +1 -1
  149. package/src/assets/agents/test.md +21 -12
  150. package/src/assets/agents/triage.mds +9 -0
  151. package/src/assets/agents/validate.md +27 -34
  152. package/src/assets/commands/_partials/_compliance.mds +5 -3
  153. package/src/assets/commands/_partials/_decisions.mds +10 -8
  154. package/src/assets/commands/_partials/_engine.mds +9 -8
  155. package/src/assets/commands/_partials/_publication.mds +10 -1
  156. package/src/assets/commands/_partials/_settings.mds +2 -2
  157. package/src/assets/commands/bug-analysis.mds +80 -126
  158. package/src/assets/commands/code-review.mds +52 -9
  159. package/src/assets/commands/dynamic-build.mds +109 -15
  160. package/src/assets/commands/dynamic-tickets.mds +13 -0
  161. package/src/assets/commands/implement.mds +145 -33
  162. package/src/assets/commands/plan.mds +19 -0
  163. package/src/assets/commands/release.mds +50 -1
  164. package/src/assets/commands/resolve.mds +229 -44
  165. package/src/assets/commands/self-review.mds +8 -7
  166. package/src/assets/mds/git/_ops.mds +29 -1
  167. package/src/assets/mds/git/_ops_pr.mds +20 -10
  168. package/src/assets/mds/git/_ops_steps.mds +27 -0
  169. package/src/assets/mds/git/_pr.mds +15 -15
  170. package/src/assets/mds/git/_references.mds +12 -7
  171. package/src/assets/mds/tracker/_common.mds +4 -0
  172. package/src/assets/mds/tracker/_github.mds +43 -4
  173. package/src/assets/mds/tracker/_jira.mds +44 -4
  174. package/src/assets/mds/tracker/_linear.mds +44 -4
  175. package/src/assets/rules/quality.md +2 -0
  176. package/src/assets/scripts/ci-wait.cjs +493 -118
  177. package/src/assets/scripts/hooks/assets/implement-installed +1 -0
  178. package/src/assets/scripts/hooks/assets/orchestrator-charter.md +13 -11
  179. package/src/assets/scripts/hooks/background-memory-update +79 -1
  180. package/src/assets/scripts/hooks/json-helper.cjs +44 -2
  181. package/src/assets/scripts/hooks/lib/curation-store.cjs +82 -20
  182. package/src/assets/scripts/hooks/lib/render-decisions.cjs +9 -1
  183. package/src/assets/scripts/hooks/lib/tracked-areas.cjs +226 -33
  184. package/src/assets/scripts/hooks/memory-permission +10 -2
  185. package/src/assets/scripts/hooks/memory-worker +11 -0
  186. package/src/assets/scripts/hooks/pre-compact-memory +11 -0
  187. package/src/assets/scripts/hooks/preamble +18 -8
  188. package/src/assets/scripts/hooks/queue-append +218 -1
  189. package/src/assets/scripts/hooks/session-start-context +89 -37
  190. package/src/assets/scripts/hooks/session-start-memory +39 -0
  191. package/src/assets/scripts/hooks/session-start-orchestrator +4 -2
  192. package/src/assets/scripts/pr-evidence.cjs +46 -4
  193. package/src/assets/scripts/resolve-settings.cjs +244 -30
  194. package/src/assets/scripts/verify-evidence.cjs +708 -124
  195. package/src/assets/skills/agent-references/SKILL.md +7 -1
  196. package/src/assets/skills/agent-references/references/commands/implement-levels.md +6 -4
  197. package/src/assets/skills/agent-references/references/commands/implement-revalidate.md +3 -3
  198. package/src/assets/skills/agent-references/references/commands/plan-levels.md +1 -1
  199. package/src/assets/skills/agent-references/references/commands/resolve-levels.md +3 -2
  200. package/src/assets/skills/agent-references/references/synthesize/bug-analysis.md +6 -2
  201. package/src/assets/skills/agent-references/references/synthesize/review.md +4 -4
  202. package/src/assets/skills/agent-references/references/triage/severity-tiers.md +43 -0
  203. package/src/assets/skills/agent-references/references/validate/passed-alone.md +14 -0
  204. package/src/assets/skills/docs-framework/SKILL.md +5 -2
  205. package/src/assets/skills/git/SKILL.md +3 -3
  206. package/src/assets/skills/git/references/github-api.md +2 -139
  207. package/src/assets/skills/git/references/github-posting.md +158 -0
  208. package/src/assets/skills/review-methodology/SKILL.md +1 -1
  209. package/src/assets/skills/review-methodology/references/patterns.md +1 -1
  210. package/src/assets/skills/review-methodology/references/report-template.md +1 -2
  211. package/src/assets/skills/testing/SKILL.md +2 -4
@@ -29,7 +29,7 @@ Process issues from code review reports: triage every issue through the blast-ra
29
29
  - Additional filter: must have unresolved reviews (latest review directory has no `resolution-summary.md`)
30
30
  2. **If `--path` flag provided:** use only that worktree, skip discovery
31
31
  **`--path` validation**: Before proceeding, verify the path exists as a directory and appears in `git worktree list` output. If not: report error and stop.
32
- 3. **If only 1 resolvable worktree** (the common case): proceed as single-worktree flow — zero behavior change
32
+ 3. **If only 1 resolvable worktree:** proceed as single-worktree flow
33
33
  4. **If multiple resolvable worktrees:** report "Found N worktrees with unresolved reviews: {list}" and proceed with multi-worktree flow
34
34
 
35
35
  #### Step 0b: Per-Worktree Pre-Flight (Git Agent)
@@ -59,7 +59,7 @@ PR_DESCRIPTION=$(gh pr view {pr_number} --json body --jq '.body' 2>/dev/null ||
59
59
  ```
60
60
  If `pr_number` is absent or the command fails, set `PR_DESCRIPTION` to `(none)`.
61
61
 
62
- **DIFF_FILES** is extracted from the `### Diff Scope` block in the Git agent validate-branch output. Set to empty string `""` if the block is absent (bug-analysis mode edge case).
62
+ **DIFF_FILES** is the `### Diff Scope` block of the validate-branch output, or `""` when the block is absent.
63
63
 
64
64
  #### Step 0c: Target Review Directory
65
65
 
@@ -70,13 +70,13 @@ For each worktree:
70
70
 
71
71
  1. List directories in `{worktree}/.devflow/docs/reviews/{branch-slug}/`
72
72
  2. **If `--review {timestamp}` provided:** use that specific directory (not supported in multi-worktree mode)
73
- 3. **Otherwise:** sort directories by name descending (timestamps are naturally sortable), scan the 10 most recent directories only. Select the first that contains `review-summary.md` (complete review)
73
+ 3. **Otherwise:** sort directories by name descending, scan the 10 most recent directories only. Select the first that contains `review-summary.md` (complete review)
74
74
  4. **If latest directory already has `resolution-summary.md`:** the review is resolved — check bug-analysis fallback (step 5b).
75
- 5. **Legacy fallback:** if no timestamped subdirectories exist but flat `*.md` files do in `{worktree}/.devflow/docs/reviews/{branch-slug}/`, read them directly (backwards compatible).
75
+ 5. **Legacy fallback:** if no timestamped subdirectories exist but flat `*.md` files do in `{worktree}/.devflow/docs/reviews/{branch-slug}/`, read them directly.
76
76
 
77
77
  **5b. Bug analysis fallback** — if no qualifying review directory found (no reviews exist, or all resolved):
78
78
  - List directories in `{worktree}/.devflow/docs/bug-analysis/{branch-slug}/`
79
- - Sort by name descending (timestamps are naturally sortable), scan the 10 most recent directories only. Select the latest that:
79
+ - Sort by name descending, scan the 10 most recent directories only. Select the latest that:
80
80
  - Contains at least one focus report (`security.md`, `functional.md`, `integration.md`, or `usability.md`)
81
81
  - Does NOT contain a `resolution-summary.md`
82
82
  - If found: set `TARGET_DIR` to that path. Reviews take priority — bug analysis is only used when no qualifying review exists.
@@ -88,13 +88,13 @@ Set `TARGET_DIR` to the selected review or bug-analysis directory path, and `TAR
88
88
 
89
89
  **Produces:** FEATURE_KNOWLEDGE, FEATURE_KNOWLEDGE_RULES, EVIDENCE_POLICY, ISSUE_REQUIRED, APPLY_CONVENTIONS, REQUIRE_NON_AUTHOR_APPROVAL, REVIEW_PUBLICATION, RESOLUTION_TS, GATE_LEVEL
90
90
 
91
- **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
91
+ **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root in a one-worktree run:
92
92
 
93
93
  ```bash
94
94
  node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
95
95
  ```
96
96
 
97
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on CURATION=on KNOWLEDGE=off GATE_LEVEL=full` instead.
97
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=off CURATION=off KNOWLEDGE=off GATE_LEVEL=full` instead.
98
98
 
99
99
  The accepted line is the only source of these values.
100
100
 
@@ -196,9 +196,9 @@ Read review reports from `{TARGET_DIR}/*.md` and extract:
196
196
 
197
197
  **Exclude from issue extraction:**
198
198
  - `review-summary.md` (Synthesize agent output, not individual findings)
199
- - `resolution-summary.md` (if it exists from a previous partial run)
199
+ - `resolution-summary.md` (from a previous partial run)
200
200
  - `bug-analysis-summary.md` (Synthesize agent output for bug-analysis runs)
201
- - `static-findings.md` (raw static analysis tool output, not individual findings)
201
+ - `static-findings.md` (raw tool output, not individual findings)
202
202
 
203
203
  **Include:** ALL issues from all categories and severities, including Suggestions.
204
204
 
@@ -252,15 +252,17 @@ Triage every issue: collapse duplicates first, then apply the blast-radius dispo
252
252
  Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE."
253
253
  ```
254
254
 
255
- Wait for Triage agent to complete before proceeding. Parse verdict ledger from Triage agent output:
255
+ When the Triage agent completes, parse its verdict ledger:
256
256
  - **ESCALATED**: Security issues requiring human escalation
257
- - **FIX_NOW**: Valid issues assigned to Code agents (with risk tier: Standard | Careful)
257
+ - **FIX_NOW**: Valid issues assigned to Code agents (with risk tier: Standard | Careful | `Standard, prose only: edit text alone; agent instructions count as logic; if this issue needs a logic change, leave it unedited and name it`; any other value counts as Careful). SCOPE gives one `{id}: {tier}` line per issue.
258
258
  - **FALSE_POSITIVE**: Issues the Review agent got wrong (with cited evidence)
259
259
  - **BY_DESIGN**: Intentional code (with a recorded decision, stated in words, or a code doc citation)
260
260
  - **FIX_SEPARATE**: Valid but out of blast-radius scope (must become manage-debt ticket)
261
261
  - **TECH_DEBT**: Architectural overhaul only — LAST RESORT
262
262
  - **DUPLICATE**: Collapsed duplicate issue — carries `duplicate_of: <primary-id>` referencing the non-DUPLICATE primary; inherits the primary's outcome
263
263
 
264
+ A ledger `### Summary` line `- Tiers: not applied (…)` is the level note.
265
+
264
266
  **Triage agent completeness assertion:** Verify the parsed ledger against ISSUES before proceeding:
265
267
  1. Every issue `id` from ISSUES must appear in exactly one verdict bucket — none may vanish, none may appear in multiple buckets. DUPLICATE is a valid bucket; a valid DUPLICATE entry must name its `duplicate_of` primary (the `Duplicate Of` column of the ledger's DUPLICATE table) and that primary must be a non-DUPLICATE issue id. A missing `duplicate_of` or one that chains to another DUPLICATE is a **Triage agent failure** (retry-then-abort as below).
266
268
  2. If the Triage agent output is empty, contains a skill re-entrancy guard string (e.g., contains `already running`), or is missing any issue IDs from ISSUES: treat as a **Triage agent failure**:
@@ -276,7 +278,7 @@ Wait for Triage agent to complete before proceeding. Parse verdict ledger from T
276
278
 
277
279
  If FIX_NOW list is empty: skip to Phase 5, then run Phases 9, 9b, 9c and 10.
278
280
 
279
- Otherwise, batch FIX_NOW issues for Code agent execution. **DUPLICATE issues are never dispatched** — they inherit the primary's outcome:
281
+ Otherwise, batch FIX_NOW issues. **DUPLICATE issues are never dispatched** — they inherit the primary's outcome:
280
282
  - **Same-file issues** → one batch (one Code agent per file)
281
283
  - **Distinct-file issues** → parallel Code agents
282
284
  - **Max 5 issues per batch** — chunk large sets
@@ -308,26 +310,29 @@ Spawn distinct-file batch Code agents **in a single message** (parallel). Sequen
308
310
  Collect from each Code agent:
309
311
  - `{status, commitShas, unresolved}` per batch
310
312
  - `## Verification` block (commands run + results)
313
+ - An unresolved prose-only issue that Code names as needing a logic change, and whose type is not `security` → FIX_SEPARATE in TRIAGE_RESULTS, Reason `needs a logic change`; every other unresolved issue stays Blocked
311
314
 
312
315
  ### Phase 5: Write resolution-summary.md
313
316
 
314
317
  **Produces:** RESOLUTION_FILE (early write for compaction safety)
315
318
  **Requires:** TRIAGE_RESULTS, CODE_AGENT_RESULTS, TARGET_DIR, TARGET_DIR_REL, BRANCH_INFO, RESOLUTION_TS, GATE_LEVEL
316
319
 
317
- **Immediately write `resolution-summary.md`** to `{TARGET_DIR}` using the Write tool. Do this now — not in Phase 9 — while results are fresh in context.
320
+ **Immediately write `resolution-summary.md`** to `{TARGET_DIR}`.
318
321
 
319
322
  Set `Tracked` for FIX_SEPARATE and TECH_DEBT items to `(pending)` — to be backfilled after Phase 9 manage-debt (or `TRACEABILITY: DEGRADED ({reason})` if manage-debt degrades).
320
323
 
321
324
  DUPLICATE issues are listed **only** in `## Duplicates` — never in `## Fixed Issues`, `## False Positives`, `## By Design`, `## Fix Separately`, `## Deferred to Tech Debt`, `## Escalations`, or `## Blocked`. A duplicate of a FALSE_POSITIVE primary therefore leaves only the primary in the `False Positive` row and the `## False Positives` section; the same holds for every other outcome the duplicate inherits.
322
325
 
323
- Use the template from the Output Artifact section below.
326
+ Open each Issue cell with its severity in brackets when it has one, as `[HIGH]`.
327
+
328
+ Use the template from the Output Artifact section below. Put text not for public posting in a final `## Private notes` section.
324
329
 
325
330
  ### Phase 6: Polish
326
331
 
327
332
  **Produces:** POLISH_RESULT
328
333
  **Requires:** CODE_AGENT_RESULTS, GATE_LEVEL
329
334
 
330
- If any fixes were made, spawn Polish agent to refine the changed code:
335
+ If any fixes were made, spawn Polish agent:
331
336
 
332
337
  ```
333
338
  Agent(subagent_type="Polish", run_in_background=false):
@@ -340,7 +345,7 @@ Commit any improvements with a conventional-commit message."
340
345
 
341
346
  ### Phase 7: Verification Gate
342
347
 
343
- **Produces:** VERIFICATION_STATUS
348
+ **Produces:** VERIFICATION_STATUS, VERIFICATION_BLOCKER
344
349
  **Requires:** CODE_AGENT_RESULTS, BRANCH_INFO
345
350
 
346
351
  If no fixes were made (CODE_AGENT_RESULTS contains 0 commits) → set VERIFICATION_STATUS = SKIPPED, skip: "No fixes applied — skipping Verification Gate."
@@ -357,10 +362,9 @@ Run build, typecheck, lint, test. Report pass/fail with failure details."
357
362
  `validation_retry_count = 0`
358
363
 
359
364
  **If FAIL:**
360
- 1. Extract failure details from Validate agent output
361
- 2. Increment `validation_retry_count`
362
- 3. If `validation_retry_count <= 2`:
363
- - Spawn Fix agent with fix context:
365
+ 1. Increment `validation_retry_count`
366
+ 2. If `validation_retry_count <= 2`:
367
+ - Spawn Fix agent:
364
368
  ```
365
369
  Agent(subagent_type="Fix"):
366
370
  "OPERATION: validation-fix
@@ -372,12 +376,16 @@ Run build, typecheck, lint, test. Report pass/fail with failure details."
372
376
  {COMPLIANCE_DIRECTIVE}"
373
377
  ```
374
378
  - Loop back to re-validate
375
- 4. If `validation_retry_count > 2`: set VERIFICATION_STATUS = FAILED and exit the validation loop — record a blocking callout in the `## Verification` section (never a silent pass).
379
+ 3. If `validation_retry_count > 2`: set VERIFICATION_STATUS = FAILED, Final gate `FAILED after N attempts`; exit the validation loop.
380
+
381
+ **If BLOCKED:** FAILED, Final gate `FAILED (Validate blocked)`, no Fix agent, exit the loop; VERIFICATION_BLOCKER = its cause and log path, for the Phase 10 report only, never resolution-summary.md.
382
+
383
+ **If PASS:** set VERIFICATION_STATUS = PASS, Final gate `PASS`; exit the validation loop.
376
384
 
377
- **If PASS:** set VERIFICATION_STATUS = PASS and exit the validation loop.
385
+ Any other or missing Status: FAILED, Final gate `FAILED (no Validate status)`, exit the loop; never PASS.
378
386
 
379
387
  Record `## Verification` outcome in resolution-summary.md (runs unconditionally after the validation loop exits):
380
- - Rewrite the `## Verification` section with | Command | Result | table + `Final gate: PASS | FAILED after N attempts`
388
+ - Rewrite the `## Verification` section with | Command | Result | table + `Final gate: {value above}`; a FAILED gate gets a blocking callout (never a silent pass)
381
389
 
382
390
  Push all commits to remote once (Code and Fix agents ran with PUSH: false — runs whether the gate PASSED or FAILED, so the branch is always visible before Phase 9 or CI runs):
383
391
  ```bash
@@ -393,9 +401,7 @@ git -C {worktree} push
393
401
  **Produces:** CI_STATUS
394
402
  **Requires:** CODE_AGENT_RESULTS, VERIFICATION_STATUS, BRANCH_INFO
395
403
 
396
- If no issues were fixed (CODE_AGENT_RESULTS contains 0 fixes) → skip: "No fixes applied — skipping CI validation."
397
-
398
- If Verification Gate FAILED → skip CI gate (validation failures already reported).
404
+ If VERIFICATION_STATUS is not PASS → skip CI gate.
399
405
 
400
406
  If `fork_no_push` is set → skip: "Cannot push to fork — skipping CI validation."
401
407
 
@@ -409,16 +415,16 @@ Otherwise, for each worktree with fixes:
409
415
  git -C {worktree} push; echo "exit=$?"
410
416
  ```
411
417
 
412
- Phase 7 has pushed already, so this is a no-op unless the head moved since. `exit=0` continues to the gate. Any other result records `TRACEABILITY: DEGRADED (ci push failed)`, reports "CI status unknown — verify manually before merging" and skips the wait for this worktree. Pass `WORKTREE_PATH: {worktree_path}` on the fix spawn below.
418
+ `exit=0` continues to the gate. Any other result records `TRACEABILITY: DEGRADED (ci push failed)`, reports "CI status unknown — verify manually before merging" and skips the wait for this worktree. Pass `WORKTREE_PATH: {worktree_path}` on the fix spawn below.
413
419
 
414
420
  <!-- PATTERN: ci-status-gate -->
415
- 1. Wait: run `cd {worktree} && HEAD_SHA=$(git rev-parse HEAD) && node "$HOME/.devflow/scripts/ci-wait.cjs" --pr {PR_NUMBER} --head "$HEAD_SHA"` through the Bash tool with `timeout: 600000`. Each run is one wait of at most 570 seconds and prints one line, `CI <STATUS> pr=… head=… failing=… pending=… waited=…` (INDETERMINATE adds `reason=…`). A missing or unparseable line, a status outside the six below, or a non-zero exit is INDETERMINATE.
421
+ 1. Wait: run `cd {worktree} && HEAD_SHA=$(git rev-parse HEAD) && node "$HOME/.devflow/scripts/ci-wait.cjs" --pr {PR_NUMBER} --head "$HEAD_SHA"` through the Bash tool with `timeout: 600000`. Each run waits at most 570 seconds and prints one line, `CI <STATUS> pr=… head=… failing=… pending=… waited=…` (INDETERMINATE adds `reason=…`). A missing or unparseable line, an unknown status or a non-zero exit is INDETERMINATE.
416
422
  2. **If PASSING** → proceed to next phase.
417
423
  3. **If NO_PR or NO_CI** → skip: "No PR/CI configured, skipping CI validation." Proceed to next phase.
418
- 4. **If PENDING** and fewer than 3 waits have run → wait again (step 1). After the third wait → report "CI still running — verify manually before merging" and proceed.
419
- 5. **If INDETERMINATE** and fewer than 3 waits have run → wait again (step 1). After the third wait → report "CI status unknown — verify manually before merging" and proceed.
420
- 6. **If FAILING** and fewer than 2 fixes have run → report the failing checks from the line. Spawn `Agent(subagent_type="Fix")` whose prompt opens with `OPERATION: ci-fix`, with `COMPLIANCE_DIRECTIVE` (when it is not empty), `CI_FAILURES` and `PUSH: false`; `CI_FAILURES` holds the failing-check names from the line and nothing else, because the Fix agent fetches the full names and reads the logs itself and this command reads none. After a fix, push with the command above and, if a wait remains, wait again (step 1); a failed push records `TRACEABILITY: DEGRADED (ci push failed)`, reports "CI status unknown — verify manually before merging" and stops waiting. After the second fix still FAILING → report the failing checks and proceed.
421
- 7. **Budget**: at most 3 waits and 2 fixes in all, per worktree. When one is spent, report the current status and proceed.
424
+ 4. **If PENDING** and fewer than 3 waits have run → wait again. Otherwise → report "CI still running — verify manually before merging" and proceed.
425
+ 5. **If INDETERMINATE** and fewer than 3 waits have run → wait again, unless `waited=570` with `reason=head-mismatch` or `no-workflow-check`. Otherwise → report "CI status unknown — verify manually before merging" and proceed.
426
+ 6. **If FAILING** and fewer than 2 fixes have run → report the failing checks from the line. Spawn `Agent(subagent_type="Fix")` whose prompt opens with `OPERATION: ci-fix`, with `COMPLIANCE_DIRECTIVE` (when it is not empty), `CI_FAILURES` and `PUSH: false`; `CI_FAILURES` holds only the failing-check names from the line: the Fix agent fetches the full names and reads the logs itself. After a fix, push with the command above and, if a wait remains, wait again (step 1); a failed push records `TRACEABILITY: DEGRADED (ci push failed)`, reports "CI status unknown — verify manually before merging" and stops waiting. After the second fix still FAILING → report the failing checks and proceed.
427
+ 7. **Budget**: at most 3 waits and 2 fixes in all, per worktree. When one is spent, report the status and proceed.
422
428
  <!-- /PATTERN: ci-status-gate -->
423
429
 
424
430
  ### Phase 9: Manage Debt (Sequential)
@@ -477,6 +483,8 @@ Append one claim per `### Test Plan Evidence` row whose TP is in the stale file
477
483
 
478
484
  The line ends at `by:test` when the row's Exit is not a number from 0 to 255; append nothing when the report's `HEAD:` is not one 40-hex SHA. Record `Test plan: {n} stale, {m} re-verified` under `## Verification` in resolution-summary.md now, before 9b-2 posts it, and set `STALE_REVERIFICATION` to that line.
479
485
 
486
+ A Test Status whose first word is not PASS or FAIL still gets its claims, but `Test plan: DEGRADED (Test run blocked)` replaces that line. `{m}` counts PASS rows only.
487
+
480
488
  **Step 9b-1: Resolve external threads**
481
489
 
482
490
  Run this step only when `EVIDENCE_POLICY` is `required` and THREAD_MAP is non-empty; otherwise skip it and record `THREAD_RESOLUTION_RESULT` as `SKIPPED ({reason})`.
@@ -556,7 +564,7 @@ If the script prints a `TRACEABILITY: DEGRADED` line: warn, proceed to Phase 10.
556
564
 
557
565
  ### Phase 10: Report
558
566
 
559
- **Requires:** BRANCH_INFO, TARGET_DIR, PR_COMMENT, THREAD_RESOLUTION_RESULT, GATE_LEVEL
567
+ **Requires:** BRANCH_INFO, TARGET_DIR, PR_COMMENT, THREAD_RESOLUTION_RESULT, GATE_LEVEL, VERIFICATION_BLOCKER
560
568
 
561
569
  The resolution summary was already written to `{TARGET_DIR}/resolution-summary.md` in Phase 5 (updated by Phase 7, Phase 9 and Phase 9b-1). Display results to the user:
562
570
 
@@ -580,7 +588,8 @@ The resolution summary was already written to `{TARGET_DIR}/resolution-summary.m
580
588
  | Duplicates Collapsed | {n} |
581
589
 
582
590
  ### Verification
583
- Final gate: {PASS | FAILED after N attempts}
591
+ Final gate: {the value Phase 7 recorded}
592
+ {VERIFICATION_BLOCKER, if any}
584
593
 
585
594
  ### Commits Created
586
595
  - {sha} {message}
@@ -669,10 +678,7 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
669
678
  ├─ Phase 8: CI Status Gate (conditional)
670
679
  ├─ Phase 9: Git agent (manage-debt) — FIX_SEPARATE + TECH_DEBT → backfill Tracked={ISSUE_REF} (or TRACEABILITY: DEGRADED on failure)
671
680
  │
672
- ├─ Phase 9b: Thread resolution + resolution comment
673
- │ ├─ Step 9b-0: verify-evidence.cjs --stale-out → ≤1 Test agent on the stale TPs (wrapped) — only after this run's push
674
- │ ├─ Step 9b-1: Git agent (resolve-review-threads) — evidence policy required
675
- │ ├─ Step 9b-2: Git agent (post-resolution-summary, REVIEW_PUBLICATION) — always-on
681
+ ├─ Phase 9b: 9b-0 Test agent (stale TPs), 9b-1/9b-2 Git agent (threads, resolution comment)
676
682
  │ └─ Step 9b-3: verify-evidence.cjs publish (inline, no Git spawn) — only when the head moved
677
683
  │
678
684
  ├─ Phase 9c: verify-evidence.cjs readiness (inline, no Git spawn) — non-author approval required, report-only
@@ -688,26 +694,24 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
688
694
  | Fix attempt fails | Revert changes, mark BLOCKED, continue others |
689
695
  | Issue dependencies | Sequential chain, skip dependents if predecessor blocked |
690
696
  | No issues parsed | Report "No issues to resolve" |
691
- | Latest review already resolved | Check bug-analysis fallback (Step 0c-5b); if also absent, skip worktree and suggest `/code-review` or `/bug-analysis` |
692
697
  | `--review {timestamp}` in multi-worktree mode | Not supported — use `--path` + `--review` to target specific worktree + review |
693
698
  | gh/GitHub absent | manage-debt degrades (`TRACEABILITY: DEGRADED ({reason})`); Tracked stays `(pending — TRACEABILITY: DEGRADED ({reason}))` — recorded, not dropped |
694
699
  | `EVIDENCE_POLICY` is `standard` | Phases 1b and 9b-step-1 are skipped; post-resolution-summary (Phase 9b step 2) still runs if a PR is known |
695
700
  | `REQUIRE_NON_AUTHOR_APPROVAL` is `false` | Phase 9c is skipped and reported as SKIPPED |
696
- | Public repo (`auto` mode) | Git agent probes visibility, posts counts-only STUB comment; full resolution report stays local |
697
701
  | `reviewPublication: off` | Git agent skips posting the resolution comment (OFF) — except that, only when `EVIDENCE_POLICY` is `required`, Step 0d passes `stub` and a counts-only STUB comment posts |
698
- | Fork PR this run cannot push to (no maintainer edits, no push access of its own) | validate-branch emits `TRACEABILITY: DEGRADED (cannot push to fork)` → `fork_no_push`: Phase 7 skips the push, Phase 8 is skipped, 9b-1 records FIXED threads as DEGRADED instead of citing unpushed commits; 9b-2 still posts the resolution comment; the head did not move, so 9b-0 and 9b-3 are skipped |
702
+ | Fork PR this run cannot push to | `TRACEABILITY: DEGRADED (cannot push to fork)` → `fork_no_push`: no push in Phase 7, Phase 8 skipped, 9b-1 records FIXED threads as DEGRADED, 9b-2 still posts; the head did not move, so 9b-0 and 9b-3 are skipped |
699
703
  | The PR has no trusted evidence record | 9b-0 finds no vouched-for stale line, so no Test agent runs; 9b-3 still refreshes the block and comment, every TP without a claim reading `UNVERIFIED` |
700
704
  | A CI run behind a claim has expired (~90 days) | The TP reads `INDETERMINATE` — never verified, never failed — until a claim at a newer SHA settles it |
701
705
 
702
706
  ## Principles
703
707
 
704
708
  1. **Orchestration only** - Command spawns agents, doesn't do git/resolve work itself
705
- 2. **Git agent for git work** - All git operations go through Git agent; the CI wait, the evidence refresh and the merge-readiness check are inline scripts (plumbing, no model work)
706
- 3. **Traceability-aware** - Policy-gated ops (thread fetch/resolve, merge readiness) are skipped cleanly when the evidence policy does not ask for them; resolution comment (Phase 9b-2) is always-on; test-plan evidence is re-verified (≤1 Test agent) and refreshed only when this run moved the head (Steps 9b-0, 9b-3)
709
+ 2. **Git agent for git work** - All git operations go through Git agent; inline scripts are plumbing
710
+ 3. **Traceability-aware** - Policy-gated ops are skipped cleanly when the evidence policy does not ask for them; resolution comment (Phase 9b-2) is always-on; test-plan evidence is re-verified (≤1 Test agent) and refreshed only when this run moved the head (Steps 9b-0, 9b-3)
707
711
 
708
712
  ## Output Artifact
709
713
 
710
- Written in Phase 5 (Collect Results) to `{TARGET_DIR}/resolution-summary.md`:
714
+ Written in Phase 5 to `{TARGET_DIR}/resolution-summary.md`:
711
715
 
712
716
  ```markdown
713
717
  # Resolution Summary
@@ -716,7 +720,7 @@ Written in Phase 5 (Collect Results) to `{TARGET_DIR}/resolution-summary.md`:
716
720
  **Date**: {RESOLUTION_TS}
717
721
  **Review**: {TARGET_DIR_REL}
718
722
  **Command**: /resolve
719
- **Gate level**: {GATE_LEVEL}
723
+ **Gate level**: {GATE_LEVEL, with any level note}
720
724
 
721
725
  ## Statistics
722
726
  | Metric | Value |
@@ -739,7 +743,7 @@ _(Note: `Deferred` = `## Fix Separately` count + `## Deferred to Tech Debt` coun
739
743
 
740
744
  Regression tests added: {n}
741
745
 
742
- Final gate: PASS | FAILED after {n} attempts
746
+ Final gate: {the value Phase 7 recorded}
743
747
 
744
748
  ## Fixed Issues
745
749
  | Issue | File:Line | Commit |
@@ -24,13 +24,13 @@ Detect changed files and build context:
24
24
  4. Build TASK_DESCRIPTION from recent commit messages or branch name
25
25
  5. Record `HEAD_BEFORE` (`git rev-parse HEAD`) now, before Polish runs — Phase 3 compares against it
26
26
 
27
- **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root when the run has one worktree:
27
+ **Resolve the settings line** once per worktree root, reusing a line this run already resolved for the same root. `{root}` is the worktree the values are for — the repository root in a one-worktree run:
28
28
 
29
29
  ```bash
30
30
  node "$HOME/.devflow/scripts/resolve-settings.cjs" "{root}" 2>/dev/null; echo "exit=$?"
31
31
  ```
32
32
 
33
- Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=on CURATION=on KNOWLEDGE=off GATE_LEVEL=full` instead.
33
+ Accept the output only when it is exactly two lines: `exit=0` last and, before it, one line of the form `TRACKER=<github|jira|linear> TRACKER_SOURCE=<project|personal|machine|default> TRACKER_WARN=<none|mismatch|invalid> SITE=<none|https://<host>> KEY=<none|<key>> REVIEW_PUBLICATION=<off|auto|full> COMPLIANCE=<off|generic|<id>[,<id>…]> MEMORY=<on|off> CURATION=<on|off> KNOWLEDGE=<on|off> GATE_LEVEL=<full|standard|lean>` — these fields, in this order, nothing else, where `<host>` is a lowercase dotted host name alone, `<key>` is 2–10 of `A-Z`, `0-9` and `_` starting with a letter, and each `<id>` is one of `gdpr`, `hipaa`, `pci-dss`, `soc2`, `iso-27001`, `sox`. **Anything else** (a non-zero exit, no line, extra text, or a missing, reordered or unlisted field or value) ⇒ use `TRACKER=github TRACKER_SOURCE=default TRACKER_WARN=invalid SITE=none KEY=none REVIEW_PUBLICATION=off COMPLIANCE=generic MEMORY=off CURATION=off KNOWLEDGE=off GATE_LEVEL=full` instead.
34
34
 
35
35
  The accepted line is the only source of these values.
36
36
 
@@ -131,7 +131,7 @@ Follow devflow:apply-feature-knowledge for FEATURE_KNOWLEDGE. Skip if (none)."
131
131
 
132
132
  If STATUS is BLOCKED, skip this phase: the report (Phase 4) names the blocker.
133
133
 
134
- Run this phase only if HEAD moved — `git rev-parse HEAD` differs from `HEAD_BEFORE`, meaning Polish or Harden committed (Polish alone counts, whatever Harden's STATUS is). Otherwise skip it. Its scope is changed-only over what the two agents changed:
134
+ Run this phase only if HEAD moved — `git rev-parse HEAD` differs from `HEAD_BEFORE`, meaning Polish or Harden committed (Polish alone counts, whatever Harden's STATUS is). Otherwise skip it.
135
135
 
136
136
  Agent(subagent_type="Validate", run_in_background=false):
137
137
  "FILES_CHANGED: {output of `git diff --name-only HEAD_BEFORE..HEAD`}
@@ -140,6 +140,7 @@ Run build, typecheck, lint, test on modified files"
140
140
 
141
141
  **If FAIL:** Report validation failures to user and halt
142
142
  **If PASS:** Continue to report
143
+ **Any other or missing Status** (BLOCKED included): report the status or "none", its cause and log path if any, then halt.
143
144
 
144
145
  ### Phase 4: Report
145
146
 
@@ -230,7 +231,7 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
230
231
  │ └─ 9-pillar quality gate (may fix and commit)
231
232
  │
232
233
  ├─ Phase 3: Validate agent (conditional)
233
- │ └─ If HEAD moved since HEAD_BEFORE (Polish or Harden committed), changed-only Validate over HEAD_BEFORE..HEAD
234
+ │ └─ If HEAD moved: changed-only over HEAD_BEFORE..HEAD
234
235
  │
235
236
  └─ Phase 4: Report
236
237
  └─ Display summary with pillar status
@@ -242,13 +243,13 @@ When the Knowledge agent reports `KB_COMMIT: skipped (detached HEAD)`, the files
242
243
  | No changes | Report "No changes to review" and exit |
243
244
  | Polish agent finds nothing | Normal, continue to Harden agent |
244
245
  | Harden agent BLOCKED | Report blocking issue, halt workflow |
245
- | Validation fails | Report failures, halt (don't create broken state) |
246
+ | Validation fails | Report failures, halt |
247
+ | Validate not PASS or FAIL | Report status and cause, halt |
246
248
  | Not in git repo | Report error, suggest running in git repo |
247
249
 
248
250
  ## Principles
249
251
 
250
252
  1. **Orchestration only** - Command spawns agents, doesn't do the work
251
- 2. **Sequential execution** - Polish agent must complete before Harden agent
252
- 3. **Validation gate** - If Polish agent or Harden agent committed, the changes must pass validation
253
- 4. **Honest reporting** - Display actual agent outputs
254
- 5. **Fail fast** - Stop on BLOCKED or validation failure
253
+ 2. **Validation gate** - If Polish agent or Harden agent committed, the changes must pass validation
254
+ 3. **Honest reporting** - Display actual agent outputs
255
+ 4. **Fail fast** - Stop on BLOCKED or a Validate status other than PASS
@@ -6,6 +6,7 @@ import { formatMoveNotice, gatherMoveNotice } from '../../core/curation-move-not
6
6
  import { isMoveSkipReason } from '../../core/curation-move-skip.js';
7
7
  import { getLedgerRoot, LEDGER_ROOT_TIMEOUT_MS } from '../../core/ledger-root.js';
8
8
  import { isActiveDecisionsStatus } from '../../core/observations.js';
9
+ import { probeTrackedArea } from '../../core/tracked-areas.js';
9
10
  /**
10
11
  * The largest ledger the statusline reads: 8 MiB, far above the roughly 0.5 MB
11
12
  * real ledgers reach.
@@ -101,6 +102,16 @@ export function gatherCurationCounts(cwd) {
101
102
  }
102
103
  return parsedAny ? counts : null;
103
104
  }
105
+ /** Whether anything stands at `ledgerPath`, a link included: the cheap test that keeps git out of a project with no ledger. */
106
+ async function ledgerPresent(ledgerPath) {
107
+ try {
108
+ await fs.promises.lstat(ledgerPath);
109
+ return true;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ }
104
115
  /**
105
116
  * Count the ledger the hooks write for a session started in `cwd`: the ledger
106
117
  * root (getLedgerRoot — the main checkout in a linked worktree, the repository
@@ -109,10 +120,26 @@ export function gatherCurationCounts(cwd) {
109
120
  * status gather, not after it (D-LEDGER-MAIN-WORKTREE). Where the old ledger folder
110
121
  * still stands it adds the move notice (gatherMoveNotice): any notice when both counts
111
122
  * are zero, a skip notice only when a count is above zero.
123
+ *
124
+ * The ledger is read only where git tracks nothing in the curation folders: when it
125
+ * stands, the tracked-areas check asks git, and any answer other than `untracked` (git
126
+ * tracks files there, git did not answer, or the budget ran out) gives no counts and no
127
+ * notice. A project with no ledger makes no git call here. The root lookup and the check
128
+ * share one LEDGER_ROOT_TIMEOUT_MS deadline: the check gets what the lookup left, and when
129
+ * nothing is left it does not run and the answer is no counts.
112
130
  */
113
131
  export async function gatherLedgerCurationCounts(cwd, options = {}) {
132
+ const deadline = Date.now() + LEDGER_ROOT_TIMEOUT_MS;
114
133
  const root = await getLedgerRoot(cwd, { ...options, timeoutMs: LEDGER_ROOT_TIMEOUT_MS });
115
134
  const ledgerRoot = root ?? cwd;
135
+ if (await ledgerPresent(getDecisionsLedgerPath(ledgerRoot))) {
136
+ const timeLeftMs = deadline - Date.now();
137
+ if (timeLeftMs <= 0)
138
+ return null;
139
+ const { answer } = await probeTrackedArea(ledgerRoot, 'curation', { timeoutMs: timeLeftMs });
140
+ if (answer !== 'untracked')
141
+ return null;
142
+ }
116
143
  const counts = gatherCurationCounts(ledgerRoot);
117
144
  const move = gatherMoveNotice(ledgerRoot, options.home ?? os.homedir());
118
145
  if (counts !== null && counts.decisions + counts.pitfalls > 0) {
@@ -1,47 +1,48 @@
1
1
  ## Operation: post-resolution-summary
2
2
 
3
- Post the resolution summary as a single PR comment. Marker-based deduplication — only one comment per workflow run, never edited after posting (D8).
3
+ Post the resolution summary as one PR comment per workflow run, deduplicated by its marker; never edited after posting (D8).
4
4
 
5
5
  **Input:** `PR_NUMBER`, `RESOLUTION_SUMMARY_PATH`, `RESOLUTION_TS`, `WORKTREE_PATH` (optional), `REVIEW_PUBLICATION` (optional; values: `auto` | `full` | `off` | `stub`; absent/unrecognised → `auto`)
6
6
 
7
- **Degradation (D4):** No PR → `TRACEABILITY: DEGRADED (no PR)`, warn, return. Resolution summary is already written to disk.
7
+ **Degradation (D4):** No PR → `TRACEABILITY: DEGRADED (no PR)`, warn, return; the summary stays on disk.
8
8
 
9
- The publication gate this operation applies is the `devflow:git` skill's `references/publication-gate.md` (D10) — the step order below instantiates it.
9
+ Its publication gate is the `devflow:git` skill's `references/publication-gate.md` (D10).
10
10
 
11
11
  The body the steps below compose MUST NOT reproduce verbatim content from any `<external-thread>` body or `<untrusted-issue-body>` — cite only internal evidence (commit SHAs, file:line from this codebase) and the thread's `ext-{N}` id.
12
12
 
13
+ **Private sections:** one starts at any line, fenced or not, of 0-3 spaces, 1-6 `#`, a space or tab and the word `Private` (any case) not followed by a letter or digit. It ends before the next unfenced heading (0-3 spaces) with as many `#` as its start or fewer, else at end of file; an unclosed fence in it runs it to end of file. When unsure, strip more. The read strips them before either mode, closing at each cut any fence the cut leaves open; compose, cap and re-cap use only the stripped text; the file stays unedited. A failed read or strip, blank stripped text or, before the scrub, a body line of that start shape → post nothing; report `**Status**: DEGRADED (no postable summary)` and never quote private text.
14
+
13
15
  **Output:**
14
16
  ```markdown
15
17
  ## Resolution Summary Posted
16
18
  **PR**: #{number}
17
19
  **Publication**: FULL (private repo) | FULL (config override) | STUB (public repository) | OFF (publication disabled by config) | STUB (visibility undeterminable) | STUB (evidence policy)
18
- **Status**: POSTED | POSTED+TRUNCATED (body exceeded 60k after redaction — `NOTE` prepended to body) | SKIPPED (already posted) | DEGRADED ({reason})
20
+ **Status**: POSTED | POSTED+TRUNCATED (body exceeded 60k after redaction) | SKIPPED (already posted) | DEGRADED ({reason})
21
+ **Stripped**: {n} private sections
19
22
  ```
20
23
 
21
24
  Load for `post-resolution-summary` under every tracker provider.
22
25
 
23
- **PR mechanics held here:** the author-filtered marker dedup, the visibility probe, the FULL/STUB compose templates with the 60000-character cap, and the scrub-then-post call.
24
-
25
26
  ### Process
26
27
 
27
- 1. Check for existing marker (author-filtered — a third party posting the marker string must not suppress devflow's comment):
28
+ 1. Check for an existing marker (author-filtered, so a third party's copy of it cannot suppress devflow's comment):
28
29
  - Fetch viewer login: `gh api user --jq '.login'` → store as VIEWER_LOGIN
29
30
  - `gh pr view {PR_NUMBER} --json comments --jq '[.comments[] | select(.author.login == "'"$VIEWER_LOGIN"'")] | .[].body'`
30
31
  - Search the viewer-authored bodies only for the exact key `<!-- devflow:resolution-summary ts:{RESOLUTION_TS} -->`
31
32
  - If found: skip — report `Skipped: already posted for ts:{RESOLUTION_TS}`
32
33
  2. Load `references/publication-gate.md` and resolve `REVIEW_PUBLICATION` by its step 2; `off` ends the op without posting.
33
34
  3. Probe (if mode not yet determined): `gh repo view --json visibility --jq '.visibility'`, case-insensitive. `PRIVATE`/`INTERNAL` → FULL; `PUBLIC` → `STUB (public repository)`; empty output, an error or any other value → `STUB (visibility undeterminable)`. **Fail-closed: on any error or unrecognised value, treat as PUBLIC (mode STUB).**
34
- 4. Read `RESOLUTION_SUMMARY_PATH` — repo-relative; read it under `WORKTREE_PATH` (else cwd).
35
+ 4. Read `RESOLUTION_SUMMARY_PATH` (repo-relative) under `WORKTREE_PATH` (else cwd); strip it (above).
35
36
  5. Compose body (where `{TS}` = `RESOLUTION_TS`):
36
37
  - **FULL mode:**
37
38
  ```
38
39
  <!-- devflow:resolution-summary ts:{TS} -->
39
- {full content of resolution-summary.md}
40
+ {stripped resolution-summary.md}
40
41
 
41
42
  ---
42
43
  *Posted by [devflow](https://github.com/dean0x/devflow)*
43
44
  ```
44
- - **STUB mode** (excluded: finding titles, file:line references, Blocking/Escalations/Third-Party/Verification sections):
45
+ - **STUB mode** (stripped text only; excluded: all but the counts table):
45
46
  ```
46
47
  <!-- devflow:resolution-summary ts:{TS} -->
47
48
  ## Resolution Summary
@@ -53,6 +54,6 @@ Load for `post-resolution-summary` under every tracker provider.
53
54
  Full report: {RESOLUTION_SUMMARY_PATH} (not committed; ask the author)
54
55
  *Posted by [devflow](https://github.com/dean0x/devflow)*
55
56
  ```
56
- Cap body at 60000 characters (GitHub rejects over 65536 with a 422, which the 4xx rule would silently skip); truncate lowest-value sections first (Suggestions, then Pre-existing), keeping the counts table and every Blocking entry; end with `…truncated — full report in the local review artifact {RESOLUTION_SUMMARY_PATH} (not committed; ask the author)`.
57
+ Cap body at 60000 characters; truncate lowest-value sections first (Suggestions, then Pre-existing), keeping the counts table and every Blocking entry; end with `…truncated — full report in the local review artifact {RESOLUTION_SUMMARY_PATH} (not committed; ask the author)`.
57
58
  6. Write body to `$DEVFLOW_BODY_RAW`; apply the Comment-sink scrub (D11) — non-zero exit or missing script → DO NOT POST. Re-check the 60000-char cap on the scrubbed body (redaction may grow it; truncate at a line boundary below 59,800 chars, keeping the truncation pointer sentence; if truncation fires here: emit `NOTE: body exceeded 60k after redaction — truncated/stub posted` in op output and prepend that notice to the body). Post: `gh pr comment {PR_NUMBER} --body-file "$DEVFLOW_BODY"`.
58
59
  7. On 5xx: retry once. If still 5xx: `TRACEABILITY: DEGRADED (5xx on post-resolution-summary)`, warn, return.
@@ -1,14 +1,16 @@
1
1
  ## Operation: post-review-summary
2
2
 
3
- Post a consolidated code review summary as a single PR comment per review run (D7). Marker-based deduplication — if the marker for this cycle+timestamp pair already exists, skip; never edit after posting.
3
+ Post a consolidated code review summary as one PR comment per review run (D7), deduplicated by its cycle+timestamp marker; never edited after posting.
4
4
 
5
5
  **Input:** `PR_NUMBER`, `REVIEW_SUMMARY_PATH`, `CYCLE_NUMBER`, `REVIEW_TIMESTAMP`, `WORKTREE_PATH` (optional), `REVIEW_PUBLICATION` (optional; values: `auto` | `full` | `off` | `stub`; absent/unrecognised → `auto`)
6
6
 
7
- - `REVIEW_TIMESTAMP`: the review directory timestamp slug (e.g., `2026-08-20_1030`); identifies the specific review run within a cycle so a re-review in the same cycle posts its own comment while a true re-run of the same review deduplicates
7
+ - `REVIEW_TIMESTAMP`: the review directory's timestamp slug (e.g., `2026-08-20_1030`), the run half of the dedup key, so a re-review posts its own comment
8
8
 
9
- **Degradation (D4):** No PR / `gh` unauthenticated → `TRACEABILITY: DEGRADED (no PR)`, warn in output, return. Summary is written to disk only.
9
+ **Degradation (D4):** No PR / `gh` unauthenticated → `TRACEABILITY: DEGRADED (no PR)`, warn, return; the summary stays on disk.
10
10
 
11
- The publication gate this operation applies is the `devflow:git` skill's `references/publication-gate.md` (D10) — the step order below instantiates it.
11
+ Its publication gate is the `devflow:git` skill's `references/publication-gate.md` (D10).
12
+
13
+ **Private sections:** one starts at any line, fenced or not, of 0-3 spaces, 1-6 `#`, a space or tab and the word `Private` (any case) not followed by a letter or digit. It ends before the next unfenced heading (0-3 spaces) with as many `#` as its start or fewer, else at end of file; an unclosed fence in it runs it to end of file. When unsure, strip more. The read strips them before either mode, closing at each cut any fence the cut leaves open; compose, cap and re-cap use only the stripped text; the file stays unedited. A failed read or strip, blank stripped text or, before the scrub, a body line of that start shape → post nothing; report `**Status**: DEGRADED (no postable summary)` and never quote private text.
12
14
 
13
15
  **Output:**
14
16
  ```markdown
@@ -17,35 +19,34 @@ The publication gate this operation applies is the `devflow:git` skill's `refere
17
19
  **Cycle**: {CYCLE_NUMBER}
18
20
  **Review timestamp**: {REVIEW_TIMESTAMP}
19
21
  **Publication**: FULL (private repo) | FULL (config override) | STUB (public repository) | OFF (publication disabled by config) | STUB (visibility undeterminable) | STUB (evidence policy)
20
- **Status**: POSTED | POSTED+TRUNCATED (body exceeded 60k after redaction — `NOTE` prepended to body) | SKIPPED (already posted for cycle {N} ts:{REVIEW_TIMESTAMP}) | DEGRADED ({reason})
22
+ **Status**: POSTED | POSTED+TRUNCATED (body exceeded 60k after redaction) | SKIPPED (already posted for cycle {N} ts:{REVIEW_TIMESTAMP}) | DEGRADED ({reason})
23
+ **Stripped**: {n} private sections
21
24
  ```
22
25
 
23
26
  Load for `post-review-summary` under every tracker provider.
24
27
 
25
- **PR mechanics held here:** the author-filtered marker dedup, the visibility probe, the FULL/STUB compose templates with the 60000-character cap, and the scrub-then-post call.
26
-
27
28
  ### Process
28
29
 
29
- 1. Check for existing comment with this run's marker (author-filtered — a third party posting the marker string must not suppress devflow's comment):
30
+ 1. Check for existing comment with this run's marker (author-filtered, so a third party's copy of the marker cannot suppress it):
30
31
  - Fetch viewer login: `gh api user --jq '.login'` → store as VIEWER_LOGIN
31
32
  - `gh pr view {PR_NUMBER} --json comments --jq '[.comments[] | select(.author.login == "'"$VIEWER_LOGIN"'")] | .[].body'`
32
33
  - Search for `<!-- devflow:review-summary cycle:{CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP}` in the viewer-authored comment bodies only (full pair match)
33
34
  - If found: skip — report `Skipped: already posted for cycle {CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP}`
34
35
  2. Load `references/publication-gate.md` and resolve `REVIEW_PUBLICATION` by its step 2; `off` ends the op without posting.
35
36
  3. Probe (if mode not yet determined): `gh repo view --json visibility --jq '.visibility'`, case-insensitive. `PRIVATE`/`INTERNAL` → FULL; `PUBLIC` → `STUB (public repository)`; empty output, an error or any other value → `STUB (visibility undeterminable)`. **Fail-closed: on any error or unrecognised value, treat as PUBLIC (mode STUB).**
36
- 4. Read `REVIEW_SUMMARY_PATH` — repo-relative; read it under `WORKTREE_PATH` (else cwd).
37
+ 4. Read `REVIEW_SUMMARY_PATH` (repo-relative) under `WORKTREE_PATH` (else cwd); strip it (above).
37
38
  5. Compose body:
38
39
  - **FULL mode:**
39
40
  ```
40
41
  <!-- devflow:review-summary cycle:{CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP} -->
41
42
  ## Code Review — Cycle {CYCLE_NUMBER}
42
43
 
43
- {full content of review-summary.md}
44
+ {stripped review-summary.md}
44
45
 
45
46
  ---
46
47
  *Posted by [devflow](https://github.com/dean0x/devflow) · cycle {CYCLE_NUMBER}*
47
48
  ```
48
- - **STUB mode** (excluded: finding titles, file:line references, Blocking/Escalations/Third-Party/Verification sections, merge recommendation):
49
+ - **STUB mode** (stripped text only; excluded: all but the counts table):
49
50
  ```
50
51
  <!-- devflow:review-summary cycle:{CYCLE_NUMBER} ts:{REVIEW_TIMESTAMP} -->
51
52
  ## Code Review — Cycle {CYCLE_NUMBER}
@@ -41,7 +41,7 @@ Each `THREAD_MAP` entry carries one verdict:
41
41
 
42
42
  ### Process
43
43
 
44
- Rate limits: this op fans out, so read the remaining-budget rungs in `references/github-api.md` before the first iteration.
44
+ Rate limits: this op fans out, so read the remaining-budget rungs in `references/github-api.md` before the first iteration. The thread-reply and `resolveReviewThread` recipes are in `references/github-posting.md`.
45
45
 
46
46
  For each `ext-{N}` in THREAD_MAP (sequentially, ≤50, 1s between operations). `fetch-review-threads`
47
47
  returns up to 100 threads, so a busy PR can exceed this bound: process the first 50 in THREAD_MAP
@@ -10,7 +10,7 @@ Update the PR's test-plan block and evidence comment.
10
10
  ```markdown
11
11
  ## PR Evidence
12
12
  {the script's EVIDENCE line}
13
- **Body**: EDITED | UNCHANGED | SKIPPED | DEGRADED ({reason}) · **Comment**: POSTED | SKIPPED | OFF | DEGRADED ({reason})
13
+ **Body**: EDITED | UNCHANGED | SKIPPED | DEGRADED ({reason}) · **Comment**: POSTED | SKIPPED | OFF | EDITED | DEGRADED ({reason})
14
14
  ```
15
15
 
16
16
  Load for `update-pr-evidence` under every tracker provider.
@@ -22,4 +22,4 @@ Load for `update-pr-evidence` under every tracker provider.
22
22
  1. From `WORKTREE_PATH` (else cwd), run `node "$HOME/.devflow/scripts/verify-evidence.cjs" publish --pr {PR_NUMBER}; echo "exit=$?"`, adding `--publication {REVIEW_PUBLICATION}` only when that is `auto`, `full`, `off` or `stub`, and `--evidence "{EVIDENCE_FILE}"` when given — only a value matching `^[A-Za-z0-9._/-]{1,255}$` reaches the shell; any other is a failed run. Set the Bash `timeout` to 180000.
23
23
  2. Print the script's stdout and nothing else. On exit 0 it is the `## PR Evidence` block: the heading, the line `EVIDENCE pr:<n> head:<sha> total:<n> VERIFIED-CI:<n> ATTESTED-LOCAL:<n> UNVERIFIED:<n> STALE:<n> FAILED:<n> INDETERMINATE:<n> stale:<ids|none> exceptions:<kinds|none> approval:<yes|no|unchecked> key:<hex> posted:<yes|no|n/a> body:<same|changed>`, and the `**Body**:` / `**Comment**:` line, then one `TRACEABILITY: DEGRADED ({reason})` line for each part that degraded, such as `DEGRADED (concurrent edit)` (the body edit then reads `SKIPPED`), `DEGRADED (body read-back mismatch)` or `DEGRADED (redaction unavailable)`: a body the script cannot scrub is never posted. On exit 2 it is `TRACEABILITY: DEGRADED (evidence unavailable)`. Any other exit, or empty stdout, is a failed run: emit that same line.
24
24
 
25
- Run nothing else: the script makes the one PR read, the compare-and-swap body edit, its read-back and the append-only comment itself, and posts nothing it could not scrub.
25
+ Run nothing else: the script makes the one PR read, the compare-and-swap body edit, its read-back and the evidence comment itself, edited in place when the viewer's own is the newest evidence comment, and posts nothing it could not scrub.
@@ -2,12 +2,11 @@
2
2
 
3
3
  Applies to **`post-review-summary` and `post-resolution-summary` only.** No other op probes repo visibility.
4
4
 
5
- **Step order inside each summary op:**
6
- 1. Dedup check (D7/D8 marker — unchanged, stays first).
5
+ **Step order:**
6
+ 1. Dedup check (D7/D8 marker; stays first).
7
7
  2. Resolve `REVIEW_PUBLICATION` input: `off` → report `**Publication**: OFF (publication disabled by config)`, op ends without posting. `full` → mode FULL, skip probe. `auto` or absent/unrecognised → probe.
8
8
  - `stub` (never unrecognised) → mode STUB, skip probe; report `STUB (evidence policy)`.
9
- 3. Probe once: `gh repo view --json visibility --jq '.visibility'` — compare case-insensitively. `PRIVATE` or `INTERNAL` → mode FULL. Anything else (including `PUBLIC`, empty output, command error, unauthenticated) → mode STUB. **Fail-closed rule: on any error or unrecognised value, treat as PUBLIC (mode STUB).**
10
- 4. Compose body (full content in FULL mode; stub template in STUB mode — defined per op).
11
- 5. Scrub per D11 (both modes — the stub is also scrubbed).
12
- 6. Re-check 60000-char cap **after** the scrub (redaction tokens may grow the body; truncate at a line boundary below 59,800 chars, keeping the truncation pointer sentence).
13
- 7. Post; 5xx retry-once (unchanged).
9
+ 3. Probe once: `gh repo view --json visibility --jq '.visibility'`, case-insensitive. `PRIVATE` or `INTERNAL` → mode FULL; anything else → mode STUB. **Fail-closed rule: on any error or unrecognised value, treat as PUBLIC (mode STUB).**
10
+ 4. Compose body (defined per op).
11
+ 5. Scrub per D11 (both modes), then re-check the 60000-char cap **after** the scrub.
12
+ 6. Post; 5xx retry-once.