@opengsd/gsd-core 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-debug-session-manager.md +11 -0
  4. package/agents/gsd-doc-synthesizer.md +2 -4
  5. package/agents/gsd-executor.md +5 -5
  6. package/agents/gsd-mempalace-curator.md +5 -2
  7. package/agents/gsd-phase-researcher.md +20 -1
  8. package/agents/gsd-plan-checker.md +37 -0
  9. package/agents/gsd-planner.md +44 -46
  10. package/agents/gsd-user-profiler.md +3 -0
  11. package/agents/gsd-verifier.md +12 -3
  12. package/bin/install.js +841 -971
  13. package/bin/lib/ui-safety-gate.cjs +2 -0
  14. package/commands/gsd/code-review.md +1 -1
  15. package/commands/gsd/execute-phase.md +1 -1
  16. package/commands/gsd/map-codebase.md +1 -1
  17. package/commands/gsd/mempalace-capture.md +1 -1
  18. package/commands/gsd/mempalace-recall.md +1 -1
  19. package/commands/gsd/new-milestone.md +1 -1
  20. package/commands/gsd/quick.md +1 -1
  21. package/commands/gsd/review-backlog.md +2 -1
  22. package/commands/gsd/verify-work.md +1 -1
  23. package/gsd-core/bin/gsd-tools.cjs +469 -88
  24. package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
  25. package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
  26. package/gsd-core/bin/lib/api-coverage.cjs +3 -5
  27. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  28. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  29. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  30. package/gsd-core/bin/lib/audit.cjs +876 -240
  31. package/gsd-core/bin/lib/broken-windows.cjs +1 -1
  32. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  33. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  34. package/gsd-core/bin/lib/capability-registry.cjs +575 -101
  35. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  36. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  37. package/gsd-core/bin/lib/capability-validator.cjs +495 -22
  38. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  39. package/gsd-core/bin/lib/check-command-router.cjs +71 -37
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  41. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  42. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  43. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  44. package/gsd-core/bin/lib/commands.cjs +651 -86
  45. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  47. package/gsd-core/bin/lib/config-loader.cjs +75 -0
  48. package/gsd-core/bin/lib/config.cjs +10 -1
  49. package/gsd-core/bin/lib/core-utils.cjs +127 -29
  50. package/gsd-core/bin/lib/decisions.cjs +23 -0
  51. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  52. package/gsd-core/bin/lib/frontmatter.cjs +155 -20
  53. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  54. package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
  55. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  56. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  57. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  58. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  59. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  60. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  61. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  62. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  63. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  64. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  65. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  66. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  67. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  68. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  69. package/gsd-core/bin/lib/init.cjs +321 -129
  70. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  71. package/gsd-core/bin/lib/install-engine.cjs +745 -258
  72. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  73. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  74. package/gsd-core/bin/lib/install-profiles.cjs +134 -57
  75. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  76. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  77. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  78. package/gsd-core/bin/lib/installer-migrations.cjs +138 -31
  79. package/gsd-core/bin/lib/io.cjs +10 -0
  80. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  81. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  82. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  83. package/gsd-core/bin/lib/milestone.cjs +754 -70
  84. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  85. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  86. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  87. package/gsd-core/bin/lib/pattern.cjs +122 -0
  88. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  89. package/gsd-core/bin/lib/phase-id.cjs +444 -36
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  91. package/gsd-core/bin/lib/phase-locator.cjs +125 -18
  92. package/gsd-core/bin/lib/phase.cjs +646 -143
  93. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  94. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  95. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  96. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  97. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  98. package/gsd-core/bin/lib/planning-workspace.cjs +56 -6
  99. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  100. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  101. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  102. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  103. package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
  104. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  105. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  106. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  107. package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
  108. package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
  109. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  110. package/gsd-core/bin/lib/roadmap.cjs +385 -94
  111. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
  112. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  113. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
  114. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  115. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  116. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
  117. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  118. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  119. package/gsd-core/bin/lib/security.cjs +104 -5
  120. package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
  121. package/gsd-core/bin/lib/smart-entry.cjs +142 -22
  122. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  123. package/gsd-core/bin/lib/state-document.cjs +152 -8
  124. package/gsd-core/bin/lib/state-transition.cjs +371 -117
  125. package/gsd-core/bin/lib/state.cjs +1794 -357
  126. package/gsd-core/bin/lib/surface.cjs +23 -9
  127. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  128. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  129. package/gsd-core/bin/lib/uat-predicate.cjs +9 -3
  130. package/gsd-core/bin/lib/uat.cjs +399 -56
  131. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  132. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  133. package/gsd-core/bin/lib/unusable-input.cjs +24 -0
  134. package/gsd-core/bin/lib/update-context.cjs +8 -2
  135. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  136. package/gsd-core/bin/lib/validate.cjs +20 -6
  137. package/gsd-core/bin/lib/vendor/README.md +37 -0
  138. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  139. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  140. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  141. package/gsd-core/bin/lib/verification.cjs +258 -8
  142. package/gsd-core/bin/lib/verify.cjs +368 -888
  143. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  144. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  145. package/gsd-core/bin/lib/workstream.cjs +2 -2
  146. package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
  147. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  148. package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
  149. package/gsd-core/references/agent-contracts.md +43 -26
  150. package/gsd-core/references/checkpoints.md +2 -2
  151. package/gsd-core/references/context-budget.md +1 -1
  152. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  153. package/gsd-core/references/doc-conflict-engine.md +1 -1
  154. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  155. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  156. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  157. package/gsd-core/references/execute-phase-response-language.md +1 -1
  158. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  159. package/gsd-core/references/gate-prompts.md +1 -1
  160. package/gsd-core/references/git-planning-commit.md +2 -1
  161. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  162. package/gsd-core/references/model-profiles.md +12 -4
  163. package/gsd-core/references/mvp-concepts.md +9 -9
  164. package/gsd-core/references/planner-guidance.md +3 -9
  165. package/gsd-core/references/planner-preconditions.md +1 -1
  166. package/gsd-core/references/planner-reviews.md +1 -1
  167. package/gsd-core/references/planning-config.md +8 -6
  168. package/gsd-core/references/revision-loop.md +1 -1
  169. package/gsd-core/references/specless-probe-fallback.md +1 -1
  170. package/gsd-core/references/universal-anti-patterns.md +3 -3
  171. package/gsd-core/references/verifier-phase-gates.md +192 -0
  172. package/gsd-core/references/verify-mvp-mode.md +1 -1
  173. package/gsd-core/references/workstream-flag.md +22 -6
  174. package/gsd-core/templates/discussion-log.md +1 -1
  175. package/gsd-core/templates/phase-prompt.md +2 -4
  176. package/gsd-core/templates/state.md +4 -4
  177. package/gsd-core/templates/verification-report.md +9 -1
  178. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  179. package/gsd-core/workflows/autonomous.md +1 -1
  180. package/gsd-core/workflows/cleanup.md +62 -3
  181. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
  182. package/gsd-core/workflows/code-review-fix.md +37 -10
  183. package/gsd-core/workflows/code-review.md +38 -12
  184. package/gsd-core/workflows/complete-milestone.md +141 -18
  185. package/gsd-core/workflows/debug.md +7 -5
  186. package/gsd-core/workflows/diagnose-issues.md +35 -9
  187. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  188. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  189. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -1
  190. package/gsd-core/workflows/edit-phase.md +26 -1
  191. package/gsd-core/workflows/eval-review.md +3 -5
  192. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +31 -6
  193. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  194. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +2 -0
  195. package/gsd-core/workflows/execute-phase.md +38 -50
  196. package/gsd-core/workflows/execute-plan.md +36 -4
  197. package/gsd-core/workflows/explore.md +131 -4
  198. package/gsd-core/workflows/fast.md +10 -2
  199. package/gsd-core/workflows/health.md +73 -4
  200. package/gsd-core/workflows/import.md +4 -4
  201. package/gsd-core/workflows/ingest-docs.md +5 -5
  202. package/gsd-core/workflows/mvp-phase.md +6 -3
  203. package/gsd-core/workflows/new-milestone.md +14 -9
  204. package/gsd-core/workflows/new-project.md +14 -14
  205. package/gsd-core/workflows/next.md +12 -0
  206. package/gsd-core/workflows/plan-phase.md +41 -17
  207. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  208. package/gsd-core/workflows/progress.md +34 -6
  209. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
  210. package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
  211. package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
  212. package/gsd-core/workflows/quick.md +35 -15
  213. package/gsd-core/workflows/review.md +26 -5
  214. package/gsd-core/workflows/secure-phase.md +1 -1
  215. package/gsd-core/workflows/session-report.md +2 -1
  216. package/gsd-core/workflows/settings.md +66 -2
  217. package/gsd-core/workflows/ship.md +104 -44
  218. package/gsd-core/workflows/spec-phase.md +30 -12
  219. package/gsd-core/workflows/sync-skills.md +63 -8
  220. package/gsd-core/workflows/transition.md +46 -11
  221. package/gsd-core/workflows/ui-phase.md +5 -5
  222. package/gsd-core/workflows/ui-review.md +2 -2
  223. package/gsd-core/workflows/update.md +1 -1
  224. package/gsd-core/workflows/validate-phase.md +1 -1
  225. package/gsd-core/workflows/verify-work.md +9 -7
  226. package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
  227. package/hooks/dist/gsd-check-update-worker.js +56 -13
  228. package/hooks/dist/gsd-check-update.js +19 -1
  229. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  230. package/hooks/dist/gsd-cursor-subagent-start.js +77 -2
  231. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  232. package/hooks/dist/gsd-prompt-guard.js +21 -20
  233. package/hooks/dist/gsd-read-injection-scanner.js +38 -24
  234. package/hooks/dist/gsd-statusline.js +18 -0
  235. package/hooks/dist/gsd-update-banner.js +22 -1
  236. package/hooks/dist/gsd-workflow-guard.js +134 -36
  237. package/hooks/dist/lib/git-cmd.js +92 -59
  238. package/hooks/dist/lib/injection-patterns.js +45 -0
  239. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  240. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  241. package/hooks/gsd-agent-isolation-guard.js +103 -14
  242. package/hooks/gsd-check-update-worker.js +56 -13
  243. package/hooks/gsd-check-update.js +19 -1
  244. package/hooks/gsd-cursor-pre-tool.js +0 -3
  245. package/hooks/gsd-cursor-subagent-start.js +77 -2
  246. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  247. package/hooks/gsd-prompt-guard.js +21 -20
  248. package/hooks/gsd-read-injection-scanner.js +38 -24
  249. package/hooks/gsd-statusline.js +18 -0
  250. package/hooks/gsd-update-banner.js +22 -1
  251. package/hooks/gsd-workflow-guard.js +134 -36
  252. package/hooks/lib/git-cmd.js +92 -59
  253. package/hooks/lib/injection-patterns.js +45 -0
  254. package/hooks/lib/isolation-deny-reason.js +39 -0
  255. package/hooks/lib/isolation-sentinel.js +9 -0
  256. package/package.json +21 -9
  257. package/pi/gsd.cjs +19 -5
  258. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  259. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  260. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  261. package/scripts/changeset/lint.cjs +60 -5
  262. package/scripts/check-alias-drift.cjs +7 -43
  263. package/scripts/check-contract-drift.cjs +297 -0
  264. package/scripts/ci-test-scope.cjs +19 -2
  265. package/scripts/command-contract-helpers.cjs +903 -1
  266. package/scripts/gen-adr-index.cjs +728 -38
  267. package/scripts/gen-capability-registry.cjs +3 -15
  268. package/scripts/gen-context-index.cjs +2 -11
  269. package/scripts/gen-health-docs.cjs +390 -0
  270. package/scripts/gen-inventory-manifest.cjs +50 -4
  271. package/scripts/gen-loop-host-contract.cjs +4 -24
  272. package/scripts/gen-registry.cjs +3 -14
  273. package/scripts/lib/alias-drift-families.cjs +46 -0
  274. package/scripts/lib/drift-scan.cjs +278 -0
  275. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  276. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  277. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  278. package/scripts/lint-canary-version-leak.cjs +73 -0
  279. package/scripts/lint-command-contract.cjs +96 -13
  280. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  281. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  282. package/scripts/lint-default-flip-documentation.cjs +193 -0
  283. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  284. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  285. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  286. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  287. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  288. package/scripts/lint-milestone-window-drift.cjs +468 -0
  289. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  290. package/scripts/lint-plan-count-drift.cjs +318 -0
  291. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  292. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  293. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  294. package/scripts/lint-regression-test-names.cjs +15 -13
  295. package/scripts/lint-removed-but-needed.cjs +320 -0
  296. package/scripts/lint-state-field-drift.cjs +805 -0
  297. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  298. package/scripts/lint-test-file-count.allowlist.json +21 -10
  299. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  300. package/scripts/lint-vendored-deps.cjs +124 -0
  301. package/scripts/pr-changed-files.cjs +63 -0
  302. package/scripts/pr-template-policy.cjs +14 -4
  303. package/scripts/prompt-injection-scan.sh +25 -0
  304. package/scripts/require-issue-link-policy.cjs +192 -0
  305. package/scripts/state-write-path-drift-baseline.json +19 -0
  306. package/scripts/sync-runtime-launcher.cjs +2 -4
  307. package/skills/gsd-autonomous/SKILL.md +0 -1
  308. package/skills/gsd-code-review/SKILL.md +1 -1
  309. package/skills/gsd-execute-phase/SKILL.md +1 -2
  310. package/skills/gsd-map-codebase/SKILL.md +1 -1
  311. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  312. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  313. package/skills/gsd-new-milestone/SKILL.md +1 -1
  314. package/skills/gsd-next/SKILL.md +0 -1
  315. package/skills/gsd-plan-phase/SKILL.md +0 -1
  316. package/skills/gsd-progress/SKILL.md +0 -1
  317. package/skills/gsd-quick/SKILL.md +1 -1
  318. package/skills/gsd-review-backlog/SKILL.md +2 -1
  319. package/skills/gsd-stats/SKILL.md +0 -1
  320. package/skills/gsd-verify-work/SKILL.md +1 -1
  321. package/vscode/package.json +1 -1
  322. package/gsd-core/workflows/discovery-phase.md +0 -298
  323. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  324. package/gsd-core/workflows/verify-phase.md +0 -574
  325. package/scripts/affected-tests-lib.cjs +0 -554
  326. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  327. package/scripts/run-affected-tests.cjs +0 -7
  328. package/scripts/run-tests.cjs +0 -1051
@@ -1,6 +1,6 @@
1
1
  <purpose>
2
2
 
3
- Archive accumulated phase directories from completed milestones into `.planning/milestones/v{X.Y}-phases/`. Identifies which phases belong to each completed milestone, shows a dry-run summary, and moves directories on confirmation.
3
+ Archive accumulated phase directories from completed milestones into `.planning/milestones/v{X.Y}-phases/`. Identifies which phases belong to each completed milestone, shows a dry-run summary, and moves directories on confirmation. Also offers retroactive archival of `.planning/quick/` (#2142) when it is non-empty.
4
4
 
5
5
  </purpose>
6
6
 
@@ -9,6 +9,7 @@ Archive accumulated phase directories from completed milestones into `.planning/
9
9
  1. `.planning/MILESTONES.md`
10
10
  2. `.planning/milestones/` directory listing
11
11
  3. `.planning/phases/` directory listing
12
+ 4. `.planning/quick/` directory listing
12
13
 
13
14
  </required_reading>
14
15
 
@@ -69,6 +70,26 @@ Match phase directories to milestone membership. Only include directories that s
69
70
 
70
71
  </step>
71
72
 
73
+ <step name="identify_quick_tasks">
74
+
75
+ Check whether `.planning/quick/` has anything to retroactively archive (#2142):
76
+
77
+ ```bash
78
+ ls -d .planning/quick/*/ 2>/dev/null || true
79
+ ```
80
+
81
+ **If no directories are found:** `.planning/quick/` is empty (or absent) — say nothing about quick-task archival and do not offer the step. Skip straight to `show_dry_run` with no quick-task summary or prompt.
82
+
83
+ **If at least one directory is found:** determine the target milestone. Unlike phase directories — whose milestone membership is derivable from the archived ROADMAP snapshot each completed milestone already has — quick tasks carry **no on-disk provenance** at all; there is no way to tell which milestone any given quick task directory belongs to. The target is therefore the single most recent completed milestone (from `.planning/MILESTONES.md`, already read in `identify_completed_milestones`, listed newest-first) that does not yet have a `-quick` archive directory:
84
+
85
+ ```bash
86
+ ls -d .planning/milestones/v*-quick 2>/dev/null || true
87
+ ```
88
+
89
+ Walk `.planning/MILESTONES.md`'s entries newest-first and pick the first version with no matching `v{version}-quick` directory above. If every completed milestone already has a `-quick` archive, or `.planning/MILESTONES.md` has no entries, there is no valid target — say so and skip the quick-task step entirely (do not prompt).
90
+
91
+ </step>
92
+
72
93
  <step name="show_dry_run">
73
94
 
74
95
  Present a dry-run summary for each milestone:
@@ -92,6 +113,19 @@ These phase directories will be archived:
92
113
  Destination: .planning/milestones/v{X.Z}-phases/
93
114
  ```
94
115
 
116
+ **If a quick-task target milestone was determined in `identify_quick_tasks`**, add:
117
+
118
+ ```
119
+ ### Quick tasks — bucket-all into v{X.Y}
120
+ {N} directories under .planning/quick/ will ALL be archived into this ONE milestone
121
+ (v{X.Y} — {Milestone Name}), regardless of when each was actually completed.
122
+ Quick tasks carry no on-disk record of which milestone they belong to, so this is
123
+ a bucket-all, not a per-milestone split — unlike the phase archival above, which
124
+ is derived per-milestone from each archived ROADMAP snapshot.
125
+
126
+ Destination: .planning/milestones/v{X.Y}-quick/
127
+ ```
128
+
95
129
  **Stale local branches (upstream gone):**
96
130
 
97
131
  First, update remote-tracking refs so the candidate list matches the execution list exactly:
@@ -112,11 +146,12 @@ Show each branch name. If none, show:
112
146
  No stale local branches detected.
113
147
  ```
114
148
 
115
- If no phase directories remain to archive (all already moved or deleted) AND no stale branches exist:
149
+ If no phase directories remain to archive (all already moved or deleted) AND no stale branches exist AND no quick-task target milestone was determined:
116
150
 
117
151
  ```
118
152
  No phase directories found to archive. Phases may have been removed or archived previously.
119
153
  No stale local branches detected either.
154
+ No quick tasks to archive either.
120
155
  ```
121
156
 
122
157
  Stop here.
@@ -127,6 +162,12 @@ AskUserQuestion: "Proceed with archiving and pruning?" with options: "Yes — ar
127
162
 
128
163
  If "Cancel": Stop.
129
164
 
165
+ **If a quick-task target milestone was determined in `identify_quick_tasks`**, ask a separate, explicit question — this is a distinct, bucket-all action and must not be silently folded into the "Yes" above:
166
+
167
+ AskUserQuestion: "Archive ALL {N} quick-task directories into v{X.Y} — {Milestone Name}? This buckets every remaining quick task into this ONE milestone; there is no way to split them per-milestone." with options: "Yes — archive quick tasks into v{X.Y}" | "Skip"
168
+
169
+ If "Skip": do not run `archive_quick_tasks` — proceed to `archive_phases` (or `report`, if there were no phase directories to archive) with quick-task archival omitted.
170
+
130
171
  </step>
131
172
 
132
173
  <step name="archive_phases">
@@ -147,6 +188,20 @@ Repeat for all milestones in the cleanup set.
147
188
 
148
189
  </step>
149
190
 
191
+ <step name="archive_quick_tasks">
192
+
193
+ Only run this step when the "Yes — archive quick tasks into v{X.Y}" option was confirmed in `show_dry_run`.
194
+
195
+ Uses the narrow `milestone.archive-quick` command (#2142 escalation) rather than `milestone.complete --archive-quick`: cleanup runs against milestones that are typically ALREADY completed, and `milestone.complete` is the full close-out — it archives ROADMAP/REQUIREMENTS and writes a MILESTONES.md entry, so re-running it against an already-completed milestone would clobber that milestone's archived ROADMAP/REQUIREMENTS snapshot (the very snapshot this cleanup depends on) and duplicate its MILESTONES.md entry. `milestone.archive-quick` shares the same move/README-index/table-reset logic as `milestone.complete --archive-quick` (same underlying helper) without any of that.
196
+
197
+ ```bash
198
+ gsd_run query milestone.archive-quick "v{X.Y}"
199
+ ```
200
+
201
+ This moves every directory under `.planning/quick/` into `.planning/milestones/v{X.Y}-quick/`, (re)writes that directory's `README.md` index, and clears STATE.md's `### Quick Tasks Completed` table rows — identical move/index/reset behavior to the `--archive-quick` flag documented in `complete-milestone.md`'s `archive_milestone` step, without touching ROADMAP.md, REQUIREMENTS.md, MILESTONES.md, or milestone-completion guards. Extract `archived` from the result to confirm.
202
+
203
+ </step>
204
+
150
205
  <step name="prune_local_branches">
151
206
 
152
207
  After phase archival, prune local branches whose upstream has been deleted. Use the same filter as the dry-run so the execution list matches exactly what the user confirmed:
@@ -168,7 +223,7 @@ Notes:
168
223
  Commit the changes:
169
224
 
170
225
  ```bash
171
- gsd_run query commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/
226
+ gsd_run query commit "chore: archive phase directories from completed milestones" --files .planning/milestones/ .planning/phases/ .planning/quick/ .planning/STATE.md
172
227
  ```
173
228
 
174
229
  </step>
@@ -179,6 +234,8 @@ gsd_run query commit "chore: archive phase directories from completed milestones
179
234
  Archived:
180
235
  {For each milestone}
181
236
  - v{X.Y}: {N} phase directories → .planning/milestones/v{X.Y}-phases/
237
+ {If quick-task archival ran}
238
+ - v{X.Y}: {N} quick-task directories → .planning/milestones/v{X.Y}-quick/ (bucket-all — see known limit)
182
239
 
183
240
  Pruned: {N} local branches whose upstream is gone.
184
241
 
@@ -196,6 +253,8 @@ Pruned: {N} local branches whose upstream is gone.
196
253
  - [ ] Dry-run summary shown and user confirmed (covers both archival and pruning)
197
254
  - [ ] Phase directories moved to `.planning/milestones/v{X.Y}-phases/`
198
255
  - [ ] Stale local branches pruned (branches whose upstream is gone)
256
+ - [ ] `.planning/quick/` checked; quick-task archival offered only when non-empty
257
+ - [ ] When offered and confirmed, ALL remaining quick-task directories archived into the single named target milestone (bucket-all, not per-milestone) via `milestone.archive-quick`
199
258
  - [ ] Changes committed
200
259
 
201
260
  </success_criteria>
@@ -1,6 +1,6 @@
1
1
  When `FALLOW_ENABLED=true`:
2
2
 
3
- 1) Resolve binary via PATH first, then `node_modules/.bin/fallow`.
3
+ 1) Resolve binary via `node_modules/.bin/fallow` first, then PATH.
4
4
  ```bash
5
5
  FALLOW_BIN=$(FALLOW_CWD="$(pwd)" node -e "
6
6
  const { resolveFallowBinary } = require('./gsd-core/bin/lib/fallow-runner.cjs');
@@ -26,10 +26,20 @@ FALLOW_STDERR_TMP=$(mktemp)
26
26
 
27
27
  # Phase scope uses fallow's native changed-files scoping (--changed-since <base>).
28
28
  # Derive the phase base commit; if none is found, fall back to repo scope (fallow
29
- # auto-detects the base branch).
29
+ # auto-detects the base branch). #3191/#3503: the grep is the SAME anchored,
30
+ # POSIX-portable conventional-commit-scope derivation the workflow's Tier-3
31
+ # scope step uses (subject-line `type((phase-)?N(-plan)?):`, padded or unpadded
32
+ # phase spelling). Free prose in commit bodies — "deferred to Phase N per
33
+ # D-09", "### Phase N" format examples — never captures the base, and a bare
34
+ # digit substring matches version strings, dates, and other phases; the oldest
35
+ # such false match would silently widen --changed-since far past the phase.
30
36
  FALLOW_SCOPE_ARGS=()
31
37
  if [ \"$FALLOW_SCOPE\" = \"phase\" ]; then
32
- FALLOW_PHASE_COMMITS=$(git log --oneline --all --grep=\"${PADDED_PHASE}\" --format=\"%H\" 2>/dev/null)
38
+ PHASE_SCOPE_NUM=\"${PADDED_PHASE}\"
39
+ case \"$PADDED_PHASE\" in
40
+ 0[0-9]*) PHASE_SCOPE_NUM=\"${PADDED_PHASE#0}|${PADDED_PHASE}\" ;;
41
+ esac
42
+ FALLOW_PHASE_COMMITS=$(git log --oneline --all --extended-regexp --grep=\"^[[:alpha:]]+!?\((phase-)?(${PHASE_SCOPE_NUM})(-[0-9]+)?\)!?:\" --format=\"%H\" 2>/dev/null)
33
43
  if [ -n \"$FALLOW_PHASE_COMMITS\" ]; then
34
44
  FALLOW_BASE=$(echo \"$FALLOW_PHASE_COMMITS\" | tail -1)^
35
45
  FALLOW_SCOPE_ARGS=(--changed-since \"$FALLOW_BASE\")
@@ -207,9 +207,9 @@ Use Agent() to spawn agent:
207
207
 
208
208
  ```text
209
209
  Agent(subagent_type="gsd-code-fixer", model="{FIXER_MODEL}", prompt="
210
- <files_to_read>
210
+ <required_reading>
211
211
  ${REVIEW_PATH}
212
- </files_to_read>
212
+ </required_reading>
213
213
 
214
214
  <config>
215
215
  phase_dir: ${PHASE_DIR}
@@ -256,7 +256,11 @@ if [ "$AUTO_MODE" = "true" ]; then
256
256
  # Total fix passes = MAX_ITERATIONS. Loop uses -lt (not -le) intentionally.
257
257
  ITERATION=1
258
258
  MAX_ITERATIONS=3
259
-
259
+ # #3190: track whether the loop converged (re-review came back clean) vs
260
+ # degraded (hit the cap). Convergence determines whether the .iterN.md backups
261
+ # are spent scratch (cleaned below) or retained for post-mortem analysis.
262
+ CONVERGED=false
263
+
260
264
  while [ $ITERATION -lt $MAX_ITERATIONS ]; do
261
265
  ITERATION=$((ITERATION + 1))
262
266
 
@@ -315,6 +319,7 @@ ${AGENT_SKILLS_REVIEWER}")
315
319
  " 2>/dev/null)
316
320
 
317
321
  if [ "$NEW_STATUS" = "clean" ]; then
322
+ CONVERGED=true
318
323
  echo ""
319
324
  echo "✓ All issues resolved after iteration ${ITERATION}."
320
325
  break
@@ -324,9 +329,9 @@ ${AGENT_SKILLS_REVIEWER}")
324
329
  echo "Issues remain. Applying fixes for iteration ${ITERATION}..."
325
330
 
326
331
  Agent(subagent_type="gsd-code-fixer", model="{FIXER_MODEL}", prompt="
327
- <files_to_read>
332
+ <required_reading>
328
333
  ${REVIEW_PATH}
329
- </files_to_read>
334
+ </required_reading>
330
335
 
331
336
  <config>
332
337
  phase_dir: ${PHASE_DIR}
@@ -353,14 +358,24 @@ ${AGENT_SKILLS_FIXER}")
353
358
  echo ""
354
359
  echo "⚠ Reached maximum iterations (${MAX_ITERATIONS}). Remaining issues documented in REVIEW-FIX.md."
355
360
  fi
361
+
362
+ # #3190: on convergence the .iterN.md backups are spent scratch — their
363
+ # stated purpose is post-mortem analysis "if iterations degrade", and
364
+ # convergence means no degradation. Remove them so the phase directory is
365
+ # clean (no dirty REVIEW.md or backup files after the run). They are RETAINED
366
+ # when the loop degraded (hit MAX_ITERATIONS / fixer failure) so the
367
+ # post-mortem trail survives. Backup CREATION (cp … .iterN.md) is unchanged.
368
+ if [ "$CONVERGED" = "true" ]; then
369
+ rm -f "${REVIEW_PATH%.md}.iter"*.md "${FIX_REPORT_PATH%.md}.iter"*.md 2>/dev/null || true
370
+ fi
356
371
  fi
357
372
  ```
358
373
 
359
374
  Key design decisions for --auto (addresses ALL review HIGH concerns):
360
375
  1. **Re-review scope**: Uses REVIEW_FILES_ARRAY from original REVIEW.md frontmatter, falling back to full phase scope. Scope is NOT lost between iterations. Uses portable while-read loop (bash 3.2+ compatible, handles spaces in paths).
361
376
  2. **Artifact semantics**: REVIEW.md is overwritten by each re-review (latest review state). REVIEW-FIX.md is overwritten by each fixer iteration (latest fix state with iteration count). There is ONE final version of each artifact, not per-iteration copies.
362
- Backup files (.iterN.md) preserve history for post-mortem analysis if iterations degrade.
363
- 3. **Commit timing**: Fix commits happen per-finding inside the agent. REVIEW-FIX.md is NOT committed until step 7 (after ALL iterations complete). Only ONE docs commit for REVIEW-FIX.md, not one per iteration.
377
+ Backup files (.iterN.md) preserve history for post-mortem analysis if iterations degrade. On successful convergence (#3190) the backups are spent scratch and removed; on degradation (hit MAX_ITERATIONS / fixer failure) they are retained for post-mortem.
378
+ 3. **Commit timing**: Fix commits happen per-finding inside the agent. REVIEW-FIX.md is NOT committed until step 7 (after ALL iterations complete). Only ONE docs commit, not one per iteration. In --auto that single commit also stages the converged REVIEW.md alongside REVIEW-FIX.md (#3190), so the two committed artifacts agree — the initial code-review commit held iteration-1 REVIEW.md content, and the --auto re-review loop overwrote it each iteration.
364
379
  </step>
365
380
 
366
381
  <step name="commit_fix_report">
@@ -369,7 +384,8 @@ After ALL iterations complete (or single pass in non-auto mode), validate and co
369
384
  ```bash
370
385
  if [ -f "${FIX_REPORT_PATH}" ]; then
371
386
  # Validate REVIEW-FIX.md has valid YAML frontmatter with status field
372
- HAS_STATUS=$(REVIEW_PATH="${REVIEW_PATH}" node -e "
387
+ # #3190: export FIX_REPORT_PATH (the var the body reads), not REVIEW_PATH.
388
+ HAS_STATUS=$(FIX_REPORT_PATH="${FIX_REPORT_PATH}" node -e "
373
389
  const fs = require('fs');
374
390
  const content = fs.readFileSync(process.env.FIX_REPORT_PATH, 'utf-8');
375
391
  const match = content.replace(/\r\n/g, '\n').match(/^---\n([\s\S]*?)\n---/);
@@ -380,9 +396,19 @@ if [ -f "${FIX_REPORT_PATH}" ]; then
380
396
  echo "REVIEW-FIX.md created at ${FIX_REPORT_PATH}"
381
397
 
382
398
  if [ "$COMMIT_DOCS" = "true" ]; then
399
+ # #3190: --auto's re-review loop overwrote REVIEW.md each iteration
400
+ # (auto_iteration_loop), but the only prior REVIEW.md commit is the
401
+ # initial code-review pass (iteration-1 content). Stage the converged
402
+ # REVIEW.md alongside REVIEW-FIX.md in this single docs commit so the two
403
+ # committed artifacts agree. Non-auto single-pass runs never rewrite
404
+ # REVIEW.md, so it is left untouched (guarded on AUTO_MODE).
405
+ COMMIT_FILES=("${FIX_REPORT_PATH}")
406
+ if [ "$AUTO_MODE" = "true" ] && [ -f "${REVIEW_PATH}" ]; then
407
+ COMMIT_FILES+=("${REVIEW_PATH}")
408
+ fi
383
409
  gsd_run query commit \
384
410
  "docs(${PADDED_PHASE}): add code review fix report" \
385
- --files "${FIX_REPORT_PATH}"
411
+ --files "${COMMIT_FILES[@]}"
386
412
  fi
387
413
  else
388
414
  echo "Warning: REVIEW-FIX.md has invalid frontmatter (no status field). Not committing."
@@ -426,7 +452,8 @@ Extract frontmatter fields:
426
452
 
427
453
  ```bash
428
454
  # Extract only the YAML frontmatter block (between first two --- lines)
429
- FIX_FRONTMATTER=$(REVIEW_PATH="${REVIEW_PATH}" node -e "
455
+ # #3190: export FIX_REPORT_PATH (the var the body reads), not REVIEW_PATH.
456
+ FIX_FRONTMATTER=$(FIX_REPORT_PATH="${FIX_REPORT_PATH}" node -e "
430
457
  const fs = require('fs');
431
458
  const content = fs.readFileSync(process.env.FIX_REPORT_PATH, 'utf-8');
432
459
  const match = content.replace(/\r\n/g, '\n').match(/^---\n([\s\S]*?)\n---/);
@@ -237,12 +237,26 @@ against the diff and warn about (then add) any changed files the SUMMARY extract
237
237
  surface — so a partial SUMMARY result can no longer silently mask the rest of the phase.
238
238
  ```bash
239
239
  # Compute diff base from phase commits — fail closed if no reliable base found.
240
- # #2989: anchor the grep to the phase-mention convention ("Phase N" / "phase N"
241
- # with a word boundary) so a bare digit substring doesn't match version strings,
242
- # dates, issue refs, or other phases' numbers. With --extended-regexp, \b is
243
- # a word boundary. When no commit genuinely references the phase, this yields
244
- # empty and the fail-closed warning below actually fires.
245
- PHASE_COMMITS=$(git log --oneline --all --grep="[Pp]hase ${PADDED_PHASE}\b" --extended-regexp --format="%H" 2>/dev/null)
240
+ # #3503: anchor the grep to GSD's own conventional-commit phase scopes — the
241
+ # subject-line formats this system itself emits (docs(phase-N): from
242
+ # execute-phase.md, plan scopes feat(N-MM):/test(N-MM): from references/tdd.md,
243
+ # bare phase scopes docs(N):). The #2989/#3191 prose anchor "[Pp]hase N"
244
+ # matched free prose in ANY commit body — planning commits forward-reference
245
+ # later phases ("deferred to Phase N per D-09"), doc commits use "### Phase N"
246
+ # as a format example — and tail -1 (oldest match) turned each false positive
247
+ # into a base unboundedly before the phase, while GSD's own scope commits
248
+ # never contain the literal "Phase N" at all. The ^ anchor makes this a
249
+ # subject-line match, so commit-body prose can never capture the base.
250
+ # Workflows emit the UNPADDED roadmap phase number (docs(phase-6):) while
251
+ # PADDED_PHASE is zero-padded ("06") — accept both spellings.
252
+ # #3191: stay POSIX-ERE portable — the boundary is the closing paren + colon,
253
+ # never \b (not a POSIX ERE token; under --extended-regexp it silently matches
254
+ # nothing on macOS regex(3), making this fallback dead on Apple platforms).
255
+ PHASE_SCOPE_NUM="${PADDED_PHASE}"
256
+ case "${PADDED_PHASE}" in
257
+ 0[0-9]*) PHASE_SCOPE_NUM="${PADDED_PHASE#0}|${PADDED_PHASE}" ;;
258
+ esac
259
+ PHASE_COMMITS=$(git log --oneline --all --extended-regexp --grep="^[[:alpha:]]+!?\((phase-)?(${PHASE_SCOPE_NUM})(-[0-9]+)?\)!?:" --format="%H" 2>/dev/null)
246
260
  DIFF_BASE=""
247
261
  if [ -n "$PHASE_COMMITS" ]; then
248
262
  DIFF_BASE=$(echo "$PHASE_COMMITS" | tail -1)^
@@ -306,7 +320,7 @@ fi
306
320
 
307
321
  **Post-processing (all tiers):**
308
322
 
309
- 1. **Expand tilde paths:** SUMMARY.md `key-files` entries may record a `~/...`-prefixed path (e.g. `~/.claude/gsd-core/workflows/verify-phase.md`). Bash only tilde-expands a literal `~` written in source text, never one arriving as the value of an already-expanded variable, so every later `[ -f "$file" ]` check must see a real, expanded path or it misclassifies the file as deleted.
323
+ 1. **Expand tilde paths:** SUMMARY.md `key-files` entries may record a `~/...`-prefixed path (e.g. `~/.claude/gsd-core/workflows/verify-work.md`). Bash only tilde-expands a literal `~` written in source text, never one arriving as the value of an already-expanded variable, so every later `[ -f "$file" ]` check must see a real, expanded path or it misclassifies the file as deleted.
310
324
  ```bash
311
325
  EXPANDED_FILES=()
312
326
  for file in "${REVIEW_FILES[@]}"; do
@@ -423,17 +437,29 @@ Compute the review output path:
423
437
  REVIEW_PATH="${PHASE_DIR}/${PADDED_PHASE}-REVIEW.md"
424
438
  ```
425
439
 
426
- Compute DIFF_BASE for agent context (in case agent needs it):
440
+ Compute DIFF_BASE for agent context (in case agent needs it). #3191/#3503: this
441
+ must be the SAME anchored, POSIX-portable conventional-commit-scope derivation
442
+ the Tier-3 scope step uses — the reviewer agent consumes `diff_base` exactly
443
+ when `files:` is empty, i.e. the same fail-closed scenario Tier 3 protects, so
444
+ a divergent recomputation here re-arms the mis-scoping one tier down:
427
445
  ```bash
428
- PHASE_COMMITS=$(git log --oneline --all --grep="${PADDED_PHASE}" --format="%H" 2>/dev/null)
446
+ PHASE_SCOPE_NUM="${PADDED_PHASE}"
447
+ case "${PADDED_PHASE}" in
448
+ 0[0-9]*) PHASE_SCOPE_NUM="${PADDED_PHASE#0}|${PADDED_PHASE}" ;;
449
+ esac
450
+ PHASE_COMMITS=$(git log --oneline --all --extended-regexp --grep="^[[:alpha:]]+!?\((phase-)?(${PHASE_SCOPE_NUM})(-[0-9]+)?\)!?:" --format="%H" 2>/dev/null)
429
451
  if [ -n "$PHASE_COMMITS" ]; then
430
452
  DIFF_BASE=$(echo "$PHASE_COMMITS" | tail -1)^
453
+ # Verify the parent commit exists (first commit in repo has no parent)
454
+ if ! git rev-parse "${DIFF_BASE}" >/dev/null 2>&1; then
455
+ DIFF_BASE=$(echo "$PHASE_COMMITS" | tail -1)
456
+ fi
431
457
  else
432
458
  DIFF_BASE=""
433
459
  fi
434
460
  ```
435
461
 
436
- Build files_to_read block for agent:
462
+ Build required_reading block for agent:
437
463
  ```bash
438
464
  FILES_TO_READ=""
439
465
  for file in "${REVIEW_FILES[@]}"; do
@@ -488,9 +514,9 @@ Print: `◆ Spawning code reviewer... (runs in a subagent — no output until it
488
514
 
489
515
  ```
490
516
  Agent(subagent_type="gsd-code-reviewer", model="{REVIEWER_MODEL}", prompt="
491
- <files_to_read>
517
+ <required_reading>
492
518
  ${FILES_TO_READ}
493
- </files_to_read>
519
+ </required_reading>
494
520
 
495
521
  ${STRUCTURAL_FINDINGS_BLOCK}
496
522
 
@@ -61,26 +61,135 @@ These items are open. Choose an action:
61
61
  ```
62
62
 
63
63
  If user chooses [A] (Acknowledge):
64
- 1. Re-run `gsd-tools.cjs query audit-open --json` to get structured data
65
- 2. Write acknowledged items to STATE.md under `## Deferred Items` section:
64
+ 1. Re-run `gsd-tools.cjs query audit-open --json` to get structured data.
65
+ 2. Acknowledge every open item through the `audit-open acknowledge` CLI writer — this is what actually suppresses each item starting at the NEXT `audit-open` scan; the STATE.md table in step 3 is a disclosure record only, it is no longer the suppression mechanism. Every acknowledge call's exit status is accumulated (`ACK_FAILURES`); the step HALTS before closing if any failed — a refusal (`unsupported_heading_shape`, `ambiguous`, `not_found`, missing file, etc.) must never be silently discarded and let the close proceed as if everything were suppressed. `AUDIT_JSON` uses the same `@file:` large-payload sentinel handling `INIT_MANAGER` uses in `verify_readiness` below — `io.output` swaps any JSON payload over 50000 chars for a `@file:<path>` marker, and feeding that literal string to `jq` would silently make every loop body below iterate zero times:
66
+ ```bash
67
+ AUDIT_JSON=$(gsd_run query audit-open --json)
68
+ if [[ "$AUDIT_JSON" == @file:* ]]; then AUDIT_JSON=$(cat "${AUDIT_JSON#@file:}"); fi
69
+ MILESTONE_VERSION="v[X.Y]" # already known from ROADMAP.md's active milestone header — the same identifier `milestone.complete` uses in the archive_milestone step
70
+
71
+ ACK_FAILURES=0
72
+ ACK_FAILURE_LOG=""
73
+ record_ack_failure() {
74
+ ACK_FAILURES=$((ACK_FAILURES + 1))
75
+ ACK_FAILURE_LOG="${ACK_FAILURE_LOG}
76
+ - $1"
77
+ }
78
+
79
+ # debug_sessions / threads (--slug)
80
+ # NOTE: `< <(...)` process substitution, not `... | while`, so the loop
81
+ # runs in THIS shell — a `| while` pipeline puts the loop in a subshell
82
+ # and any ACK_FAILURES/ACK_FAILURE_LOG update inside it is lost the
83
+ # moment the pipeline exits.
84
+ for cat in debug_sessions threads; do
85
+ while IFS= read -r slug; do
86
+ [ -z "$slug" ] && continue
87
+ if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --slug "$slug"; then
88
+ record_ack_failure "$cat slug=$slug"
89
+ fi
90
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r --arg cat "$cat" '.items[$cat][] | select(.scan_error | not) | .slug')
91
+ done
92
+
93
+ # seeds (--seed-id)
94
+ while IFS= read -r seed_id; do
95
+ [ -z "$seed_id" ] && continue
96
+ if ! gsd_run query audit-open acknowledge --category seeds --milestone "$MILESTONE_VERSION" --seed-id "$seed_id"; then
97
+ record_ack_failure "seeds seed_id=$seed_id"
98
+ fi
99
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.seeds[] | select(.scan_error | not) | .seed_id')
100
+
101
+ # todos (--filename) — the scanner caps its list to 5 entries per scan
102
+ # (remainder items carry `_remainder_count`, no `filename`, and are skipped)
103
+ while IFS= read -r filename; do
104
+ [ -z "$filename" ] && continue
105
+ if ! gsd_run query audit-open acknowledge --category todos --milestone "$MILESTONE_VERSION" --filename "$filename"; then
106
+ record_ack_failure "todos filename=$filename"
107
+ fi
108
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.todos[] | select((.scan_error or ._remainder_count) | not) | .filename')
109
+
110
+ # quick_tasks (--dir) — the scanner's `slug` strips a leading
111
+ # YYYYMMDD-/YYYY-MM-DD- date prefix for display; `--dir` needs the
112
+ # ORIGINAL .planning/quick/<dir>/ name, so reconstruct it from `date`+`slug`.
113
+ while IFS= read -r dir; do
114
+ [ -z "$dir" ] && continue
115
+ if ! gsd_run query audit-open acknowledge --category quick_tasks --milestone "$MILESTONE_VERSION" --dir "$dir"; then
116
+ record_ack_failure "quick_tasks dir=$dir"
117
+ fi
118
+ done < <(printf '%s' "$AUDIT_JSON" | jq -r '.items.quick_tasks[] | select(.scan_error | not) | if .date != "" then "\(.date)-\(.slug)" else .slug end')
119
+
120
+ # uat_gaps / verification_gaps / context_questions — phase-scoped
121
+ # (--phase --file [--archived-milestone] when the item was found in an archived phase)
122
+ for cat in uat_gaps verification_gaps context_questions; do
123
+ while IFS= read -r item; do
124
+ [ -z "$item" ] && continue
125
+ phase=$(printf '%s' "$item" | jq -r '.phase')
126
+ file=$(printf '%s' "$item" | jq -r '.file')
127
+ archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty')
128
+ if [ -n "$archived" ]; then
129
+ if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --archived-milestone "$archived"; then
130
+ record_ack_failure "$cat phase=$phase file=$file archived-milestone=$archived"
131
+ fi
132
+ else
133
+ if ! gsd_run query audit-open acknowledge --category "$cat" --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file"; then
134
+ record_ack_failure "$cat phase=$phase file=$file"
135
+ fi
136
+ fi
137
+ done < <(printf '%s' "$AUDIT_JSON" | jq -c --arg cat "$cat" '.items[$cat][] | select(.scan_error | not)')
138
+ done
139
+
140
+ # deferred_items — same phase-scoped identification, plus --text (the
141
+ # exact bullet the audit read, which uniquely identifies the entry)
142
+ while IFS= read -r item; do
143
+ [ -z "$item" ] && continue
144
+ phase=$(printf '%s' "$item" | jq -r '.phase')
145
+ file=$(printf '%s' "$item" | jq -r '.file')
146
+ text=$(printf '%s' "$item" | jq -r '.text')
147
+ archived=$(printf '%s' "$item" | jq -r '.archived_milestone // empty')
148
+ if [ -n "$archived" ]; then
149
+ if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text" --archived-milestone "$archived"; then
150
+ record_ack_failure "deferred_items phase=$phase file=$file archived-milestone=$archived"
151
+ fi
152
+ else
153
+ if ! gsd_run query audit-open acknowledge --category deferred_items --milestone "$MILESTONE_VERSION" --phase "$phase" --file "$file" --text "$text"; then
154
+ record_ack_failure "deferred_items phase=$phase file=$file"
155
+ fi
156
+ fi
157
+ done < <(printf '%s' "$AUDIT_JSON" | jq -c '.items.deferred_items[] | select(.scan_error | not)')
158
+
159
+ if [ "$ACK_FAILURES" -gt 0 ]; then
160
+ echo "ERROR: $ACK_FAILURES acknowledge call(s) failed — HALTING before milestone close. Resolve each listed item manually (e.g. edit the file directly for unsupported_heading_shape/ambiguous, or re-run the audit if a --text/--file target has since changed) and re-run /gsd:complete-milestone:" >&2
161
+ printf '%s\n' "$ACK_FAILURE_LOG" >&2
162
+ exit 1
163
+ fi
164
+ ```
165
+ `todos` is the only category the scanner caps (5 entries per scan, with a remainder count for the rest). Re-run `gsd-tools.cjs query audit-open --json` (through the same `@file:` handling above) and repeat the `todos` block until it reports no `todos` items — every other category always returns its full open set in one pass.
166
+ 3. Re-run `gsd-tools.cjs query audit-open --json` once more and write the items just acknowledged as new rows to STATE.md under `## Deferred Items` — append to the existing table (creating the section if absent) rather than overwriting it, preserving rows recorded at earlier milestone closes:
66
167
  ```markdown
67
168
  ## Deferred Items
68
169
 
69
- Items acknowledged and deferred at milestone close on {date}:
70
-
71
- | Category | Item | Status |
72
- |----------|------|--------|
73
- | debug | {slug} | {status} |
74
- | quick_task | {slug} | {status} |
75
- ...
170
+ Items acknowledged and deferred at milestone close, most recent first:
171
+
172
+ | Category | Item | Status | Deferred At | Milestone |
173
+ |----------|------|--------|-------------|-----------|
174
+ | debug_sessions | {slug} | {status} | {date} | {milestone} |
175
+ | quick_tasks | {slug} | {status} | {date} | {milestone} |
176
+ | threads | {slug} | {status} | {date} | {milestone} |
177
+ | seeds | {seed_id} | {status} | {date} | {milestone} |
178
+ | todos | {filename} | (presence-only) | {date} | {milestone} |
179
+ | uat_gaps | {phase}/{file} | {status} | {date} | {milestone} |
180
+ | verification_gaps | {phase}/{file} | {status} | {date} | {milestone} |
181
+ | context_questions | {phase}/{file} | {question_count} questions | {date} | {milestone} |
182
+ | deferred_items | {phase}/{file}: {text} | acknowledged | {date} | {milestone} |
76
183
  ```
77
- Sanitize all slug and status values via `sanitizeForDisplay()` before writing. Never inject raw file content into STATE.md.
78
- 3. Set `closeout_type=override_closeout` and record `Known verification overrides: {count} (see STATE.md Deferred Items)` in the MILESTONES.md entry.
79
- 4. Proceed with milestone close.
184
+ One row per item actually acknowledged in step 2 (omit categories with nothing to disclose this close). `{date}` is today's date; `{milestone}` is `MILESTONE_VERSION`. Sanitize all slug/status/text values via `sanitizeForDisplay()` before writing. Never inject raw file content into STATE.md.
185
+ 4. Set `closeout_type=override_closeout` and record in the MILESTONES.md entry: `Known verification overrides: {N} newly acknowledged, {M} carried forward from a prior close (see STATE.md Deferred Items)` — `{N}` is the count of items acknowledged in step 2 (the pre-acknowledgment audit JSON's `counts.total`) and `{M}` is that same audit JSON's `acknowledged.total` (items a PRIOR close already suppressed and still are).
186
+ 5. Proceed with milestone close.
80
187
 
81
- If output shows all clear (no open items): set `closeout_type=verified_closeout`, print `All artifact types clear.`, and proceed.
188
+ Acknowledging is verdict-preserving and self-invalidating: it never rewrites the artifact's own `status:` field (except `deferred_items`, whose entry has no other meaning for that field), and the suppression it grants lapses automatically the moment the artifact's observed state changes again — a reopened debug session, an edited UAT gap, a re-triggered seed, etc. resurfaces on its own at the next audit and must be acknowledged again.
82
189
 
83
- SECURITY: Audit JSON output is structured data from the `audit-open` query handler (same JSON contract as legacy `gsd-tools.cjs audit-open`) — validated and sanitized at source. When writing to STATE.md, item slugs and descriptions are sanitized via `sanitizeForDisplay()` before inclusion. Never inject raw user-supplied content into STATE.md without sanitization.
190
+ If output shows all clear (no open items): set `closeout_type=verified_closeout`. If the audit JSON's `acknowledged.total` is `0`, print `All artifact types clear.` and proceed. Otherwise the close is clean only because `{acknowledged.total}` item(s) acknowledged at an earlier milestone close are still being suppressed, not because everything was fixed this time — print `All artifact types clear ({acknowledged.total} previously acknowledged item(s) still suppressed — see STATE.md Deferred Items).` and record `Known verification overrides: 0 newly acknowledged, {acknowledged.total} carried forward from a prior close (see STATE.md Deferred Items)` in the MILESTONES.md entry before proceeding.
191
+
192
+ SECURITY: Audit JSON output is structured data from the `audit-open` query handler (same JSON contract as legacy `gsd-tools.cjs audit-open`) — validated and sanitized at source. The `audit-open acknowledge` writer is the only path that sets the `audit_acknowledged` suppression marker — it snapshots each artifact's current state itself from the identifiers passed on the command line, so this workflow never hand-authors the marker. When writing the STATE.md disclosure table, item identifiers, statuses, and deferred-item text are sanitized via `sanitizeForDisplay()` before inclusion. Never inject raw user-supplied content into STATE.md without sanitization.
84
193
  </step>
85
194
 
86
195
  <step name="verify_readiness">
@@ -257,7 +366,8 @@ Full PROJECT.md evolution review at milestone completion.
257
366
  Read all phase summaries:
258
367
 
259
368
  ```bash
260
- cat .planning/phases/*-*/*-SUMMARY.md
369
+ _SUMMARIES=( .planning/phases/*-*/*-SUMMARY.md )
370
+ if [ -e "${_SUMMARIES[0]}" ]; then cat "${_SUMMARIES[@]}"; fi
261
371
  ```
262
372
 
263
373
  **Full review checklist:**
@@ -397,10 +507,20 @@ Initial user testing showed demand for shape tools.
397
507
 
398
508
  <step name="archive_milestone">
399
509
 
510
+ **Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
511
+
512
+ **Quick-task archival (opt-in — NOT symmetrical with phase archival below, #2142):** unlike phase archival, quick-task archival is **opt-in, default OFF**. Doing nothing leaves `.planning/quick/` untouched, exactly like today's behavior. Decide this BEFORE calling `milestone complete` below, so the flag can be folded into that single invocation rather than issuing a second, redundant call.
513
+
514
+ If `.planning/quick/` contains at least one directory, ask:
515
+
516
+ AskUserQuestion: "Archive completed quick tasks into this milestone too?" with options: "Yes — archive quick tasks into v[X.Y]" | "Skip"
517
+
518
+ If "Yes": set `ARCHIVE_QUICK_FLAG="--archive-quick"`. If "Skip" (or `.planning/quick/` is empty): set `ARCHIVE_QUICK_FLAG=""`.
519
+
400
520
  **Delegate archival to `gsd-tools.cjs query milestone.complete`:**
401
521
 
402
522
  ```bash
403
- ARCHIVE=$(gsd_run query milestone.complete "v[X.Y]" --name "[Milestone Name]")
523
+ ARCHIVE=$(gsd_run query milestone.complete "v[X.Y]" --name "[Milestone Name]" $ARCHIVE_QUICK_FLAG)
404
524
  ```
405
525
 
406
526
  The CLI handles:
@@ -410,11 +530,16 @@ The CLI handles:
410
530
  - Moving audit file to milestones if it exists
411
531
  - Creating/appending MILESTONES.md entry with accomplishments from SUMMARY.md files
412
532
  - Updating STATE.md (status, last activity)
533
+ - When `ARCHIVE_QUICK_FLAG` is `--archive-quick`: moving every directory under `.planning/quick/` into `.planning/milestones/v[X.Y]-quick/`, writing a `README.md` index into that archive directory (generated by scanning the archive directory itself), and clearing the data rows of STATE.md's `### Quick Tasks Completed` table — preserving the table's header and whichever column variant (with/without a Status column) was detected
413
534
 
414
535
  Extract from result: `version`, `date`, `phases`, `plans`, `tasks`, `accomplishments`, `archived`.
415
536
 
416
537
  Verify: `✅ Milestone archived to .planning/milestones/`
417
538
 
539
+ **Known limit (quick-task archival):** there is no on-disk provenance recording which milestone a given quick task belonged to. Archival buckets **all** remaining `.planning/quick/*` into the completing milestone — a quick task that predates an earlier, unarchived milestone lands in the current bucket regardless.
540
+
541
+ Verify after `--archive-quick` was passed: `✅ Quick tasks archived to .planning/milestones/v[X.Y]-quick/`
542
+
418
543
  **Phase archival (default-on):** `milestone complete` archives phase directories to `milestones/v[X.Y]-phases/` by default (#1871), so the next `/gsd:new-milestone` never inherits un-archived dirs. No manual `mkdir`/`mv` or `--archive-phases` flag is needed.
419
544
 
420
545
  If the user explicitly wants to keep phase directories in place as raw execution history, invoke `milestone complete` with `--no-archive-phases`:
@@ -425,8 +550,6 @@ gsd_run query milestone complete v[X.Y] --no-archive-phases
425
550
 
426
551
  Verify after a default (archived) completion: `✅ Phase directories archived to .planning/milestones/v[X.Y]-phases/`
427
552
 
428
- **Text mode (`workflow.text_mode: true` in config or `--text` flag):** Set `TEXT_MODE=true` if `--text` is present in `$ARGUMENTS` OR `text_mode` from init JSON is `true`. When TEXT_MODE is active, replace every `AskUserQuestion` call with a plain-text numbered list and ask the user to type their choice number. This is required for non-Claude runtimes (OpenAI Codex, Gemini CLI, etc.) where `AskUserQuestion` is not available.
429
-
430
553
  After archival, the AI still handles:
431
554
  - Reorganizing ROADMAP.md with milestone grouping (requires judgment) — overwrite in place after extracting Backlog section, with the write-guard's single-use sentinel armed first (a per-step env var cannot reach a hook — see the reorganize step for the sentinel mechanics)
432
555
  - Full PROJECT.md evolution review (requires understanding)
@@ -140,13 +140,14 @@ specialist_dispatch_enabled: true
140
140
  """,
141
141
  subagent_type="gsd-debug-session-manager",
142
142
  model="{debugger_model}",
143
- description="Continue debug session {SLUG}"
143
+ description="Continue debug session {SLUG}",
144
+ run_in_background=false
144
145
  )
145
146
  ```
146
147
 
147
148
  Display the compact summary returned by the session manager.
148
149
 
149
- **Return handling — exhaustive, no fallthrough (#2257).** Apply the same three-way classification as Section 4 "Session Management" below: `DEBUG SESSION COMPLETE` and `ABANDONED` are the only two terminal shapes. ANYTHING ELSE — including the explicit `## CONTINUE_REQUIRED` marker and any unrecognized or malformed summary that is not one of the two terminal markers — is non-terminal. Read `.planning/debug/{SLUG}.md` for the current `status`/`next_action` and AUTO-RESUME by re-spawning `gsd-debug-session-manager` with the SAME `SLUG`/checkpoint (identical `session_params` as the spawn above) — do NOT return control to the user, and do NOT report the session as complete.
150
+ **Return handling — exhaustive, no fallthrough (#2257).** Apply the same three-way classification as Section 4 "Session Management" below: `DEBUG SESSION COMPLETE` and `ABANDONED` are the only two terminal shapes. ANYTHING ELSE — including the explicit `## CONTINUE_REQUIRED` marker and any unrecognized or malformed summary that is not one of the two terminal markers — is non-terminal. Read `.planning/debug/{SLUG}.md` for the current `status`/`next_action` and AUTO-RESUME by re-spawning `gsd-debug-session-manager` with the SAME `SLUG`/`debug_file_path` and the same `session_params` as the spawn above PLUS the resume parameters (#3448): `resume: true`, `resume_status: {status}`, `resume_next_action: {next_action}`, both sourced from `.planning/debug/{SLUG}.md`. The respawn must NOT be parameter-identical to a cold start: identical params drop the recorded next action and the disposition that any earlier checkpoint in the session was already answered, so the debugger re-derives — or stalls before — the very step the checkpoint already names (the #3448 auto-resume stall). Do NOT return control to the user, and do NOT report the session as complete.
150
151
 
151
152
  **Anti-loop guard.** Same two-stop policy as Section 4 "Session Management": (1) a no-progress heuristic keyed on `next_action` ALONE from `.planning/debug/{SLUG}.md` — never `updated`, which is overwritten on every checkpoint write (`agents/gsd-debugger.md`: "Update the file BEFORE taking action"), so it changes every cycle and can never signal no-progress. Two consecutive auto-resumes with `next_action` UNCHANGED stop the loop and print a blocker report to the user (checkpoint path, status, next_action, "N auto-resumes made no progress"). And (2) an absolute hard cap, independent of content: the orchestrator tracks a running total of auto-resume spawns for this `SLUG` within the current `/gsd:debug` invocation; after **3** total auto-resumes for the slug, STOP auto-resuming and emit the blocker report REGARDLESS of whether `next_action` changed. The hard cap is the guaranteed termination bound; the no-progress heuristic is only a faster early exit before the cap is reached.
152
153
 
@@ -203,7 +204,7 @@ Create `.planning/debug/{slug}.md` with initial state using the Write tool (neve
203
204
 
204
205
  After initial context setup, spawn the session manager to handle the full checkpoint/continuation loop. The session manager handles specialist_hint dispatch internally: when gsd-debugger returns ROOT CAUSE FOUND it extracts the specialist_hint field and invokes the matching skill (e.g. typescript-expert, swift-concurrency) before offering fix options.
205
206
 
206
- > **Foreground, blocking spawn — #2196.** The `Agent(subagent_type="gsd-debug-session-manager", …)` call below is FOREGROUND and BLOCKING — it returns the compact session summary directly. Wait for it; do not background it, and do not poll for it. Never pass an agent or session identifier to `TaskOutput` — an agent ID is NOT a task ID, so `TaskOutput <agent-id>` always returns `No task found with ID`. If the spawn returns no usable result (the handoff is lost), do NOT claim the session is still running: preserve the checkpoint at `.planning/debug/{slug}.md`, report the failed handoff plainly, and resume by re-spawning the session manager or via `/gsd:debug continue {slug}`.
207
+ > **Foreground, blocking spawn — #2196.** The `Agent(subagent_type="gsd-debug-session-manager", …)` call below MUST carry `run_in_background: false` — Claude Code backgrounds subagents by default, and only that flag makes the spawn return the compact session summary directly. Wait for it; do not background it, and do not poll for it. Never pass an agent or session identifier to `TaskOutput` — an agent ID is NOT a task ID, so `TaskOutput <agent-id>` always returns `No task found with ID`. If the spawn returns no usable result (the handoff is lost), do NOT claim the session is still running: preserve the checkpoint at `.planning/debug/{slug}.md`, report the failed handoff plainly, and resume by re-spawning the session manager or via `/gsd:debug continue {slug}`.
207
208
 
208
209
  Print before spawning (runs in a subagent — no output until it returns, ~1–5 min; expected, not a freeze):
209
210
  ```
@@ -229,7 +230,8 @@ specialist_dispatch_enabled: true
229
230
  """,
230
231
  subagent_type="gsd-debug-session-manager",
231
232
  model="{debugger_model}",
232
- description="Debug session {slug}"
233
+ description="Debug session {slug}",
234
+ run_in_background=false
233
235
  )
234
236
  ```
235
237
 
@@ -239,7 +241,7 @@ Display the compact summary returned by the session manager.
239
241
 
240
242
  1. **Terminal — complete.** Summary shows `DEBUG SESSION COMPLETE` (without an `ABANDONED` status line): the session is finished. Stop.
241
243
  2. **Terminal — abandoned.** Summary shows `ABANDONED`: note session saved at `.planning/debug/{slug}.md` for later `/gsd:debug continue {slug}`. Stop.
242
- 3. **Non-terminal — auto-resume.** ANYTHING ELSE — including the explicit `## CONTINUE_REQUIRED` marker and any unrecognized or malformed summary that is not one of the two terminal markers above — is non-terminal. Read `.planning/debug/{slug}.md` for the current `status` and `next_action`, then AUTO-RESUME by re-spawning `gsd-debug-session-manager` with the SAME `slug`/`debug_file_path` and identical `session_params` as the spawn above. Do NOT return control to the user; do NOT report the session as complete.
244
+ 3. **Non-terminal — auto-resume.** ANYTHING ELSE — including the explicit `## CONTINUE_REQUIRED` marker and any unrecognized or malformed summary that is not one of the two terminal markers above — is non-terminal. Read `.planning/debug/{slug}.md` for the current `status` and `next_action`, then AUTO-RESUME by re-spawning `gsd-debug-session-manager` with the SAME `slug`/`debug_file_path` and the same `session_params` as the spawn above PLUS the resume parameters (#3448): `resume: true`, `resume_status: {status}`, `resume_next_action: {next_action}`, both read from `.planning/debug/{slug}.md`. The respawn must NOT be parameter-identical to a cold start: identical params drop the recorded next action and the disposition that any earlier checkpoint in the session was already answered, so the debugger re-derives — or stalls before — the very step the checkpoint already names (the #3448 auto-resume stall). Do NOT return control to the user; do NOT report the session as complete.
243
245
 
244
246
  **Anti-loop guard.** Two independent stops apply; the orchestrator honors whichever trips first:
245
247