@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
@@ -48,7 +48,7 @@ INIT_EARLY=$(gsd_run query init.new-milestone)
48
48
  if [[ "$INIT_EARLY" == @file:* ]]; then INIT_EARLY=$(cat "${INIT_EARLY#@file:}"); fi
49
49
  ```
50
50
 
51
- `GSD_WS` must chain to every downstream routing suggestion in this workflow (Step 4's shared-file guard, and the `/gsd:discuss-phase`/`/gsd:plan-phase` routing hints below) per the routing-propagation contract in `references/workstream-flag.md` — never let it silently drop.
51
+ `GSD_WS` must chain to every downstream routing suggestion in this workflow (Step 4's shared-file guard, and the `/gsd:discuss-phase`/`/gsd:plan-phase` routing hints below) per the routing-propagation contract in `gsd-core/references/workstream-flag.md` — never let it silently drop.
52
52
 
53
53
  **If `response_language` is set:** All user-facing questions, prompts, and explanations in this workflow (including the "What do you want to build next?" prompt and seed-selection questions below) MUST be presented in `{response_language}`. Technical terms, code, file paths, and subagent prompts stay in English — only user-facing output is translated.
54
54
 
@@ -160,7 +160,7 @@ AskUserQuestion:
160
160
 
161
161
  ## 4. Update PROJECT.md
162
162
 
163
- PROJECT.md is shared across workstreams (`references/workstream-flag.md` marks it `# Shared` in the directory diagram). This step has two independently-scoped parts — only Part A is workstream-guarded.
163
+ PROJECT.md is shared across workstreams (`gsd-core/references/workstream-flag.md` marks it `# Shared` in the directory diagram). This step has two independently-scoped parts — only Part A is workstream-guarded.
164
164
 
165
165
  <!-- gsd:section id="project-md-milestone-write" when="state:flat-mode" -->
166
166
  If `section_manifest` (from `INIT_EARLY`) is `null` or `"project-md-milestone-write"` is in its `included` list: read and execute `gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md`. Otherwise (a workstream is active) skip — do not read the file; Part B below still runs regardless of `GSD_WS`.
@@ -259,9 +259,14 @@ rejects any `--archive-version` value that is not a plain version token (no path
259
259
  Stage the phase archive move + source removal so they land in the same commit as the milestone start (atomic — no orphaned uncommitted deletions, no un-archived dirs carried forward). `phases.clear` archives each non-999 dir to `milestones/<version>-phases/`; staging both dirs captures the new archive and the removals together (#1871).
260
260
 
261
261
  ```bash
262
- git add .planning/milestones/ .planning/phases/ 2>/dev/null || true
262
+ COMMIT_DOCS=$(gsd_run query config-get commit_docs 2>/dev/null || echo "true")
263
+ if [ "$COMMIT_DOCS" != "false" ]; then
264
+ git add .planning/milestones/ .planning/phases/ 2>/dev/null || true
265
+ fi
263
266
  ```
264
267
 
268
+ When `commit_docs` is false, the archive move and phase removals are deliberately left unstaged here — not a bug — since Step 6's commit is skipped too.
269
+
265
270
  Stage PROJECT.md in both modes. Step 4's Part A guard — not this commit — is what protects the shared `## Current Milestone` heading (#2308): when a workstream is active Part A never writes it, so the only change PROJECT.md can carry here is Part B's idempotent `## Evolution` backfill, which must be committed rather than stranded as a dangling edit. Do NOT reintroduce a `[ -n "$GSD_WS" ]` branch around this commit: `GSD_WS` is set in Step 1's shell and each step's bash block runs in its own shell (the same reason Step 5 round-trips `OUTGOING_MILESTONE` through a file), so such a guard reads an unset variable, always takes the flat-mode branch, and only appears to work.
266
271
 
267
272
  ```bash
@@ -351,9 +356,9 @@ Focus ONLY on what's needed for the NEW features.
351
356
 
352
357
  <question>{QUESTION}</question>
353
358
 
354
- <files_to_read>
359
+ <required_reading>
355
360
  - {project_path} (Project context)
356
- </files_to_read>
361
+ </required_reading>
357
362
 
358
363
  ${AGENT_SKILLS_RESEARCHER}
359
364
 
@@ -390,12 +395,12 @@ After all 4 complete, spawn synthesizer:
390
395
  Agent(prompt="
391
396
  Synthesize research outputs into SUMMARY.md.
392
397
 
393
- <files_to_read>
398
+ <required_reading>
394
399
  - {research_dir}/STACK.md
395
400
  - {research_dir}/FEATURES.md
396
401
  - {research_dir}/ARCHITECTURE.md
397
402
  - {research_dir}/PITFALLS.md
398
- </files_to_read>
403
+ </required_reading>
399
404
 
400
405
  ${AGENT_SKILLS_SYNTHESIZER}
401
406
 
@@ -518,13 +523,13 @@ gsd_run query commit "docs: define milestone v[X.Y] requirements" --files .plann
518
523
  ```text
519
524
  Agent(prompt="
520
525
  <planning_context>
521
- <files_to_read>
526
+ <required_reading>
522
527
  - {project_path}
523
528
  - {requirements_path}
524
529
  - {research_dir}/SUMMARY.md (if exists)
525
530
  - {config_path}
526
531
  - {milestones_path}
527
- </files_to_read>
532
+ </required_reading>
528
533
 
529
534
  ${AGENT_SKILLS_ROADMAPPER}
530
535
 
@@ -752,9 +752,9 @@ Subsequent: Research what's needed to add [target features] to an existing [doma
752
752
  What's the standard 2025 stack for [domain]?
753
753
  </question>
754
754
 
755
- <files_to_read>
755
+ <required_reading>
756
756
  - {project_path} (Project context and goals)
757
- </files_to_read>
757
+ </required_reading>
758
758
 
759
759
  ${AGENT_SKILLS_RESEARCHER}
760
760
 
@@ -796,9 +796,9 @@ Subsequent: How do [target features] typically work? What's expected behavior?
796
796
  What features do [domain] products have? What's table stakes vs differentiating?
797
797
  </question>
798
798
 
799
- <files_to_read>
799
+ <required_reading>
800
800
  - {project_path} (Project context)
801
- </files_to_read>
801
+ </required_reading>
802
802
 
803
803
  ${AGENT_SKILLS_RESEARCHER}
804
804
 
@@ -836,9 +836,9 @@ Subsequent: How do [target features] integrate with existing [domain] architectu
836
836
  How are [domain] systems typically structured? What are major components?
837
837
  </question>
838
838
 
839
- <files_to_read>
839
+ <required_reading>
840
840
  - {project_path} (Project context)
841
- </files_to_read>
841
+ </required_reading>
842
842
 
843
843
  ${AGENT_SKILLS_RESEARCHER}
844
844
 
@@ -876,9 +876,9 @@ Subsequent: What are common mistakes when adding [target features] to [domain]?
876
876
  What do [domain] projects commonly get wrong? Critical mistakes?
877
877
  </question>
878
878
 
879
- <files_to_read>
879
+ <required_reading>
880
880
  - {project_path} (Project context)
881
- </files_to_read>
881
+ </required_reading>
882
882
 
883
883
  ${AGENT_SKILLS_RESEARCHER}
884
884
 
@@ -912,12 +912,12 @@ Agent(prompt="
912
912
  Synthesize research outputs into SUMMARY.md.
913
913
  </task>
914
914
 
915
- <files_to_read>
915
+ <required_reading>
916
916
  - {research_dir}/STACK.md
917
917
  - {research_dir}/FEATURES.md
918
918
  - {research_dir}/ARCHITECTURE.md
919
919
  - {research_dir}/PITFALLS.md
920
- </files_to_read>
920
+ </required_reading>
921
921
 
922
922
  ${AGENT_SKILLS_SYNTHESIZER}
923
923
 
@@ -1161,12 +1161,12 @@ Spawn gsd-roadmapper agent with path references:
1161
1161
  Agent(prompt="
1162
1162
  <planning_context>
1163
1163
 
1164
- <files_to_read>
1164
+ <required_reading>
1165
1165
  - {project_path} (Project context)
1166
1166
  - {requirements_path} (v1 Requirements)
1167
1167
  - {research_dir}/SUMMARY.md (Research findings - if exists)
1168
1168
  - {config_path} (Granularity and mode settings)
1169
- </files_to_read>
1169
+ </required_reading>
1170
1170
 
1171
1171
  ${AGENT_SKILLS_ROADMAPPER}
1172
1172
 
@@ -1262,9 +1262,9 @@ Use AskUserQuestion:
1262
1262
  User feedback on roadmap:
1263
1263
  [user's notes]
1264
1264
 
1265
- <files_to_read>
1265
+ <required_reading>
1266
1266
  - {roadmap_path} (Current roadmap to revise)
1267
- </files_to_read>
1267
+ </required_reading>
1268
1268
 
1269
1269
  ${AGENT_SKILLS_ROADMAPPER}
1270
1270
 
@@ -104,11 +104,22 @@ Illustrative bash:
104
104
  ```bash
105
105
  INCOMPLETE_PHASE=""
106
106
  ROADMAP_JSON=$(gsd_run query roadmap.analyze)
107
+ ROADMAP_SCOPE=$(echo "$ROADMAP_JSON" | jq -r '.scope // "complete"')
107
108
  if [ $? -ne 0 ] || [ -z "$ROADMAP_JSON" ]; then
108
109
  echo "⚠ WARNING: resume-incomplete-phase scan could not run (roadmap.analyze failed)." >&2
109
110
  echo " The incomplete-phase invariant (#160) could not be verified." >&2
110
111
  echo " Proceeding to prior-phase completeness check — review project state carefully." >&2
111
112
  # Fall through to prior_phase_completeness rather than silently skipping
113
+ elif [ "$ROADMAP_SCOPE" != "complete" ]; then
114
+ # #3184/#3165: roadmap.analyze succeeded and returned a well-formed document,
115
+ # but its milestone window did not see all of its input, so `.phases[]` is a
116
+ # NON-answer rather than a real empty. Looping it would run the invariant over
117
+ # a phase list the scan could not populate and report "clean" — the silent
118
+ # disarm #3165 reports. Treated as scan-failed, same as an outright failure.
119
+ echo "⚠ WARNING: resume-incomplete-phase scan could not be scoped (roadmap.analyze scope: $ROADMAP_SCOPE)." >&2
120
+ echo " The milestone window did not cover the whole ROADMAP, so the phase list is incomplete." >&2
121
+ echo " The incomplete-phase invariant (#160) could not be verified — review project state carefully." >&2
122
+ # Fall through to prior_phase_completeness rather than silently passing
112
123
  else
113
124
  for PHASE_NUM in $(echo "$ROADMAP_JSON" | jq -r '.phases[] | (.number // .phase_number // empty)'); do
114
125
  PHASE_JSON=$(gsd_run query find-phase "$PHASE_NUM")
@@ -338,6 +349,7 @@ Resume with: `/gsd:progress --next --auto` once resolved.
338
349
  - [ ] `--no-resume`: Route 0 skipped, prior_phase_completeness defer prompt runs as before
339
350
  - [ ] `--force`: everything skipped (Gates, Route 0, prior_phase_completeness) → straight to `determine_next_action`
340
351
  - [ ] Scan uses `gsd_run` (canonical resolver form); errors are surfaced rather than suppressed
352
+ - [ ] A `roadmap.analyze` result whose `scope` is not `complete` is treated as scan-failed (warn + fall through), never as a clean empty scan (#3184/#3165)
341
353
  - [ ] Predicate is plans-without-summaries (`plans.length > summaries.length`) — consistent with `determine_next_action` Route 4
342
354
  - [ ] Next action correctly determined from routing rules
343
355
  - [ ] Command invoked immediately without user confirmation
@@ -73,6 +73,14 @@ INGEST_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--ingest[[:space:]]+([^[:s
73
73
  RESEARCH_PHASE_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--research-phase[[:space:]]+([^[:space:]-][^[:space:]]*) ]]; then RESEARCH_PHASE_PARAM="--research-phase ${BASH_REMATCH[2]}"; fi
74
74
  REVIEWS_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--reviews([[:space:]]|$) ]]; then REVIEWS_PARAM="--reviews"; fi
75
75
  CHUNKED_PARAM=""; if [[ "$ARGUMENTS" =~ (^|[[:space:]])--chunked([[:space:]]|$) ]]; then CHUNKED_PARAM="--chunked"; fi
76
+ # Project the just-completed planning mode onto the execute-phase follow-up (#3297):
77
+ # a --gaps run creates gap_closure plans, so the Next Up handoff must point at
78
+ # execute-phase's matching --gaps-only scope rather than the whole-phase run.
79
+ # Standard and --reviews runs leave GAPS_EXEC_FLAG empty → their Next Up is unchanged.
80
+ GAPS_MODE=false
81
+ if [[ "$ARGUMENTS" =~ (^|[[:space:]])--gaps([[:space:]]|$) ]]; then GAPS_MODE=true; fi
82
+ GAPS_EXEC_FLAG=""
83
+ if [ "$GAPS_MODE" = "true" ]; then GAPS_EXEC_FLAG="--gaps-only"; fi
76
84
  INIT=$(gsd_run query init.plan-phase "$PHASE" $GRAN_PARAM $PRD_PARAM $INGEST_PARAM $RESEARCH_PHASE_PARAM $REVIEWS_PARAM $CHUNKED_PARAM)
77
85
  if [[ "$INIT" == @file:* ]]; then INIT=$(cat "${INIT#@file:}"); fi
78
86
  AGENT_SKILLS_RESEARCHER=$(gsd_run query agent-skills gsd-phase-researcher)
@@ -82,7 +90,7 @@ CONTEXT_WINDOW=$(gsd_run query config-get context_window 2>/dev/null || echo "20
82
90
  MVP_MODE_CFG=$(gsd_run query config-get workflow.mvp_mode 2>/dev/null || echo "false")
83
91
  ```
84
92
 
85
- When the tdd capability's `workflow.tdd_mode` is active (resolved via the plan:pre render-hooks), the planner agent is instructed to apply `type: tdd` to eligible tasks using heuristics from `references/tdd.md`. The TDD guidance is injected via the tdd capability's contribution hook at §5.6; no inline config-get is needed.
93
+ When the tdd capability's `workflow.tdd_mode` is active (resolved via the plan:pre render-hooks), the planner agent is instructed to apply `type: tdd` to eligible tasks using heuristics from `gsd-core/references/tdd.md`. The TDD guidance is injected via the tdd capability's contribution hook at §5.6; no inline config-get is needed.
86
94
 
87
95
  When `CONTEXT_WINDOW >= 500000`, the planner prompt includes the 3 most recent prior-phase CONTEXT.md/SUMMARY.md files plus any phases in the current phase's `Depends on:` field (explicit deps load regardless of recency).
88
96
 
@@ -92,7 +100,7 @@ Parse JSON for: `researcher_model`, `planner_model`, `checker_model`, `research_
92
100
 
93
101
  **If `response_language` is set:** All user-facing orchestrator output MUST be in `{response_language}`; technical terms, code, paths, and subagent prompts stay in English. Pass `response_language: {value}` into every spawned subagent prompt.
94
102
 
95
- **File paths (for <files_to_read> blocks):** `state_path`, `roadmap_path`, `requirements_path`, `context_path`, `research_path`, `verification_path`, `uat_path`, `reviews_path`. These are null if files don't exist.
103
+ **File paths (for <required_reading> blocks):** `state_path`, `roadmap_path`, `requirements_path`, `context_path`, `research_path`, `verification_path`, `uat_path`, `reviews_path`. These are null if files don't exist.
96
104
 
97
105
  **If `planning_exists` is false:** Error — run `/gsd:new-project` first.
98
106
 
@@ -151,7 +159,7 @@ Defer the `phase.mvp-mode` query until `PHASE` is finalized (after explicit argu
151
159
  ```bash
152
160
  WALKING_SKELETON=false
153
161
  if [ "$MVP_MODE" = "true" ] && [ "$padded_phase" = "01" ]; then
154
- PRIOR_SUMMARIES=$(gsd_run query phases.list --pick summaries_total 2>/dev/null || echo "0")
162
+ PRIOR_SUMMARIES=$(gsd_run query phases.list --type summaries --pick count 2>/dev/null)
155
163
  if [ "$PRIOR_SUMMARIES" = "0" ]; then WALKING_SKELETON=true; fi
156
164
  fi
157
165
  ```
@@ -160,7 +168,7 @@ When `WALKING_SKELETON=true`:
160
168
  - Planner is instructed to produce `SKELETON.md` in the phase directory alongside `PLAN.md`. The template lives at `~/.claude/gsd-core/references/skeleton-template.md` — the planner reads it when producing SKELETON.md (lazy; not loaded on non-skeleton runs).
161
169
  - The plan must scaffold project + routing + one real DB read/write + one real UI interaction + dev deployment — the thinnest possible end-to-end working slice.
162
170
 
163
- **Interaction with `--prd <filepath>`.** `--mvp` and `--prd` compose. The PRD express path (Step 3.5) creates `CONTEXT.md` from the PRD file and continues to research; the Walking Skeleton gate fires independently from the conditions above. When both are active on Phase 1 of a new project, the planner receives `WALKING_SKELETON=true` and PRD-derived context simultaneously — the PRD informs *what the skeleton should prove*. No precedence is needed; the two signals are orthogonal. See [`references/mvp-concepts.md`](../references/mvp-concepts.md) for the broader interaction map.
171
+ **Interaction with `--prd <filepath>`.** `--mvp` and `--prd` compose. The PRD express path (Step 3.5) creates `CONTEXT.md` from the PRD file and continues to research; the Walking Skeleton gate fires independently from the conditions above. When both are active on Phase 1 of a new project, the planner receives `WALKING_SKELETON=true` and PRD-derived context simultaneously — the PRD informs *what the skeleton should prove*. No precedence is needed; the two signals are orthogonal. See [`gsd-core/references/mvp-concepts.md`](../references/mvp-concepts.md) for the broader interaction map.
164
172
 
165
173
  Extract express-path args from $ARGUMENTS: `PRD_FILE` (`--prd <filepath>`), `INGEST_PATH` (`--ingest <path-or-glob>`), and optional `INGEST_FORMAT` (`--ingest-format <auto|nygard|madr|narrative>`, default `auto`).
166
174
 
@@ -453,7 +461,7 @@ Read the `activeHooks` array directly from `PLAN_PRE_HOOKS_JSON` / `HOOKS_JSON`
453
461
  - If `ref.agent` is set, dispatch with `Agent(prompt=filled_hook_fragment, subagent_type=ref.agent, model="{researcher_model}")`. Use the hook's `fragment.inline` as the prompt body and fill phase fields before spawning.
454
462
  - The `research` hook is handled by §5.1's research decision. The `pattern-mapper` hook is handled by §7.8 after `RESEARCH_PATH` is known. Future plan:pre agent hooks use the same `ref.agent` fragment contract.
455
463
 
456
- **AI integration capability:** If the active `ai-integration` step hook is present, `AI_SPEC_PATH` is empty, and the phase goal contains AI keywords (`agent`, `llm`, `rag`, `chatbot`, `embedding`, `langchain`, `llamaindex`, `crewai`, `langgraph`, `openai`, `anthropic`, `vector`, `eval`, `ai system`), then:
464
+ **AI integration capability:** If the active `ai-integration` step hook is present, `AI_SPEC_PATH` is empty, and the phase goal contains AI keywords (`agent`, `llm`, `rag`, `chatbot`, `embedding`, `langchain`, `llamaindex`, `crewai`, `langgraph`, `openai`, `anthropic`, `vector`, `llm eval`), then:
457
465
  - In pipeline / `--auto` mode, invoke the hook's `ref.skill` via `Skill(skill="gsd-${ref.skill}", args="${PHASE} --auto ${GSD_WS}")`.
458
466
  - In manual mode, display the existing non-blocking `/gsd:ai-integration-phase {N}` recommendation and let the user continue planning without AI-SPEC or stop to run the capability workflow first.
459
467
 
@@ -483,7 +491,8 @@ Display: `Using UI design contract: ${UI_SPEC_PATH}`. Continue to step 6.
483
491
  Read the ephemeral auto-chain flag:
484
492
 
485
493
  ```bash
486
- AUTO_CHAIN=$(gsd_run query check auto-mode --pick auto_chain_active 2>/dev/null || echo "false")
494
+ AUTO_CHAIN=$(gsd_run query check auto-mode --pick auto_chain_active 2>/dev/null)
495
+ AUTO_CHAIN="${AUTO_CHAIN:-false}"
487
496
  ```
488
497
 
489
498
  **Branch 5 — `AUTO_CHAIN` is `true` (pipeline / `--auto`):** Fire each active UI **step** hook — runs independently of whether a gate is active (covers `{ui_phase:true,ui_safety_gate:false}`). For each entry in `activeHooks` (in array order) where `kind == "step"` and `ref.skill` is set:
@@ -691,7 +700,7 @@ Planner prompt:
691
700
  **Phase:** {phase_number}
692
701
  **Mode:** {standard | gap_closure | reviews}
693
702
 
694
- <files_to_read>
703
+ <required_reading>
695
704
  - {state_path} (Project State)
696
705
  - {roadmap_path} (Roadmap)
697
706
  - {requirements_path} (Requirements)
@@ -715,7 +724,7 @@ ${CONTEXT_WINDOW >= 500000 ? `
715
724
  - CONTEXT.md, SUMMARY.md, and LEARNINGS.md from any phases listed in the current phase's "Depends on:" field in ROADMAP.md (regardless of recency — explicit dependencies always load, deduplicated against the 3 most recent)
716
725
  - Skip all other prior phases to stay within context budget
717
726
  ` : ''}
718
- </files_to_read>
727
+ </required_reading>
719
728
  ${API_SURFACE_PATH ? `
720
729
  <intel_surface_hint>
721
730
  **API Surface (HINT — may be incomplete):** When \`intel.enabled\` is true, \`${API_SURFACE_PATH}\` lists symbols extracted from the codebase by regex/JS analysis. Prefer symbols listed there when referencing existing code. This surface is regex/JS-derived and MAY BE INCOMPLETE — a symbol's absence means *unknown*, not *nonexistent*. Never treat the surface as exhaustive. If you reference a symbol that is not in the surface and this phase creates it, list it under "Artifacts this phase produces".
@@ -775,7 +784,7 @@ Output consumed by /gsd:execute-phase. Plans need:
775
784
  - Tasks in XML format with read_first and acceptance_criteria fields (MANDATORY on every task)
776
785
  - Verification criteria
777
786
  - must_haves for goal-backward verification
778
- - If the SPEC has an `## Edge Coverage` section, lift every resolved (verification: explicit) edge's acceptance criterion into `must_haves.truths` as a plain string, and every resolved (verification: backstop) edge **as a structured flat-scalar marker** — an object item `{ statement: <the check>, verification: backstop }`, NOT a prose note (the verifier branches deterministically on the `verification: backstop` field; a parenthetical is unparseable — the #1110 fragility). Use a flat scalar `verification:` continuation key, never a nested object (ADR-550 #1278). At verify time a `backstop` truth the verifier cannot confirm with explicit evidence abstains → `human_needed` (reason `insufficient_spec`), never a silent pass (#1154; see `references/honest-verifier.md`). `unresolved` edges are explicit assumptions — surface them in the plan, do not silently drop them. **Otherwise** (`EDGE_ABSENT`): apply the SAME lift to the fallback report `{COVERAGE}` (per §C of `references/specless-probe-fallback.md`); a SPEC-supplied section is never re-run.
787
+ - If the SPEC has an `## Edge Coverage` section, lift every resolved (verification: explicit) edge's acceptance criterion into `must_haves.truths` as a plain string, and every resolved (verification: backstop) edge **as a structured flat-scalar marker** — an object item `{ statement: <the check>, verification: backstop }`, NOT a prose note (the verifier branches deterministically on the `verification: backstop` field; a parenthetical is unparseable — the #1110 fragility). Use a flat scalar `verification:` continuation key, never a nested object (ADR-550 #1278). At verify time a `backstop` truth the verifier cannot confirm with explicit evidence abstains → `human_needed` (reason `insufficient_spec`), never a silent pass (#1154; see `gsd-core/references/honest-verifier.md`). `unresolved` edges are explicit assumptions — surface them in the plan, do not silently drop them. **Otherwise** (`EDGE_ABSENT`): apply the SAME lift to the fallback report `{COVERAGE}` (per §C of `gsd-core/references/specless-probe-fallback.md`); a SPEC-supplied section is never re-run.
779
788
  - If the SPEC has a `## Prohibitions` section, lift every resolved prohibition into the `must_haves.prohibitions:` sibling block (NOT `truths` — ADR-550 D3) with `statement`+`status`+`verification`, via the single `projectProhibitions` serializer (Hyrum — no second serializer); unresolved -> flagged assumptions, don't drop; never put a must-NOT under `truths`. **Otherwise** (`PROHIB_ABSENT`), author the recalled prohibitions into the SAME block via the SAME `projectProhibitions` contract but **descriptor-less** (no `check_*`) so each disposes flagged-unverified; never auto-dismiss. Section-level precedence + no-silent-drop equality apply (§C).
780
789
  - If a `-UI-SPEC.md` exists (resolved above as `UI_SPEC_PATH`) with a `## UI Considerations` section, lift it by the **identical rule** as `## Edge Coverage` above — resolved (explicit) → `must_haves.truths` string, resolved (backstop) → flat scalar `{ statement, verification: backstop }`, `unresolved` → explicit planner assumption (no new verb — ADR-550 #1278/#1154; #1867). Read it from `UI_SPEC_PATH` (the SPEC glob excludes `-UI-SPEC.md`).
781
790
  - **"Artifacts this phase produces" section (MANDATORY)** — list every symbol this phase creates: decorators, classes, functions, CLI flags, struct/dataclass fields, new file paths. The plan-review-convergence source-grounding pass reads this section to exclude newly-created symbols from drift verification; omitting it causes new symbols to be flagged for acknowledgement.
@@ -863,7 +872,12 @@ If `section_manifest` is `null` or `"chunked-planning-mode"` is in its `included
863
872
  **Triggered when:** Agent() returns but the return contains no recognized marker (`## PLANNING COMPLETE`, `## PHASE SPLIT RECOMMENDED`, `## ⚠ Source Audit`, `## CHECKPOINT REACHED`, `## PLANNING INCONCLUSIVE`).
864
873
 
865
874
  ```bash
866
- DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')
875
+ # #3218: this asks "did the planner write files to disk at all" — a
876
+ # planner-produced-nothing check, not outstanding-work counting — so it
877
+ # takes the PHYSICAL set (`plan_count_all`, status:superseded INCLUDED): a
878
+ # superseded plan is still a file the planner wrote, and this check must not
879
+ # read "nothing written" just because every plan happens to be superseded.
880
+ DISK_PLANS=$(gsd_run query find-phase "${PHASE_NUMBER}" | jq -r '.plan_count_all // 0')
867
881
  ```
868
882
 
869
883
  **If `DISK_PLANS` > 0:** The planner wrote plans to disk but the Agent() return was empty or
@@ -965,14 +979,14 @@ Checker prompt:
965
979
  **Phase Goal:** {goal from ROADMAP}
966
980
  **Mode:** {standard | gap_closure | reviews}
967
981
 
968
- <files_to_read>
982
+ <required_reading>
969
983
  - {PHASE_DIR}/*-PLAN.md (Plans to verify)
970
984
  - {roadmap_path} (Roadmap)
971
985
  - {requirements_path} (Requirements)
972
986
  - {context_path} (USER DECISIONS from /gsd:discuss-phase)
973
987
  - {research_path} (Technical Research — includes Validation Architecture)
974
988
  - {reviews_path} (Cross-AI Review Feedback - if --reviews; verify actionable findings are represented in PLAN.md)
975
- </files_to_read>
989
+ </required_reading>
976
990
 
977
991
  ${AGENT_SKILLS_CHECKER}
978
992
 
@@ -1037,7 +1051,12 @@ If thinking_partner disabled: skip this block entirely.
1037
1051
  **Triggered when:** Checker Agent() returns but the return contains neither `## VERIFICATION PASSED` nor `## ISSUES FOUND`.
1038
1052
 
1039
1053
  ```bash
1040
- DISK_PLANS=$(ls "${PHASE_DIR}"/*-PLAN.md 2>/dev/null | wc -l | tr -d ' ')
1054
+ # #3218: this asks "did the planner write files to disk at all" — a
1055
+ # planner-produced-nothing check, not outstanding-work counting — so it
1056
+ # takes the PHYSICAL set (`plan_count_all`, status:superseded INCLUDED): a
1057
+ # superseded plan is still a file the planner wrote, and this check must not
1058
+ # read "nothing written" just because every plan happens to be superseded.
1059
+ DISK_PLANS=$(gsd_run query find-phase "${PHASE_NUMBER}" | jq -r '.plan_count_all // 0')
1041
1060
  ```
1042
1061
 
1043
1062
  **If `DISK_PLANS` > 0:** Plans exist on disk; the checker return was empty or truncated (the
@@ -1094,10 +1113,10 @@ Revision prompt:
1094
1113
  **Phase:** {phase_number}
1095
1114
  **Mode:** revision
1096
1115
 
1097
- <files_to_read>
1116
+ <required_reading>
1098
1117
  - {PHASE_DIR}/*-PLAN.md (Existing plans)
1099
1118
  - {context_path} (USER DECISIONS from /gsd:discuss-phase)
1100
- </files_to_read>
1119
+ </required_reading>
1101
1120
 
1102
1121
  ${AGENT_SKILLS_PLANNER}
1103
1122
 
@@ -1358,12 +1377,15 @@ Proactive, non-blocking coverage report gated on `workflow.post_planning_gaps`
1358
1377
 
1359
1378
  ```bash
1360
1379
  PLAN_POST_HOOKS_JSON=$(gsd_run loop render-hooks plan:post --raw)
1361
- PHASE_REQ_IDS=$(gsd_run query init.plan-phase "$PHASE" --pick phase_req_ids 2>/dev/null || echo TBD)
1380
+ PHASE_REQ_IDS=$(gsd_run query init.plan-phase "$PHASE" --pick phase_req_ids 2>/dev/null)
1381
+ PHASE_REQ_IDS="${PHASE_REQ_IDS:-TBD}"
1362
1382
  ```
1363
1383
 
1364
1384
  Read the `activeHooks` array from `PLAN_POST_HOOKS_JSON` in-context. If the
1365
1385
  `gap-analysis` gate hook is absent (capability inactive), skip this step.
1366
1386
 
1387
+ ⚠ **Validate `check` before shell use** (third-party manifest input) — `loop-hook-dispatch.md` § `gate`.
1388
+
1367
1389
  **For each active entry where `kind == "gate"`** (process in array order). **Dispatch by check shape** (the registry validates exactly one of `query`/`predicate`/`agentVerdict`):
1368
1390
 
1369
1391
  ```bash
@@ -1462,6 +1484,8 @@ Route to `<offer_next>` (existing behavior).
1462
1484
  <offer_next>
1463
1485
  Output this markdown directly (not as a code block):
1464
1486
 
1487
+ `${GAPS_EXEC_FLAG}` projects the just-completed planning mode onto the follow-up execute command (#3297): it expands to `--gaps-only` for a `--gaps` planning run (so the handoff points at execute-phase's gap-closure scope — only the newly created `gap_closure: true` plans — not the whole phase) and to empty for a standard or `--reviews` run (whole-phase scope, unchanged). Substitute it verbatim; when empty, collapse the extra space.
1488
+
1465
1489
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
1466
1490
  GSD ► PHASE {X} PLANNED ✓
1467
1491
  ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
@@ -1484,7 +1508,7 @@ Verification: {Passed | Passed with override | Skipped}
1484
1508
 
1485
1509
  /clear then:
1486
1510
 
1487
- /gsd:execute-phase {X} ${GSD_WS}
1511
+ /gsd:execute-phase {X} ${GAPS_EXEC_FLAG} ${GSD_WS}
1488
1512
 
1489
1513
  ───────────────────────────────────────────────────────────────
1490
1514
 
@@ -156,9 +156,12 @@ Skill(skill="gsd-plan-phase", args="{PHASE} {GSD_WS}")
156
156
 
157
157
  Run plan-phase **inline** (do NOT wrap it in Agent()). The convergence orchestrator runs at depth 0 with Agent available, so inline plan-phase can spawn gsd-planner and gsd-plan-checker at depth 1 — the one level of nesting that works on Claude Code. Wrapping plan-phase in Agent() would push it to depth 1 where the Agent tool is absent, preventing it from spawning any sub-agents. Wait until plan-phase completes and PLAN.md files are committed before continuing.
158
158
 
159
- After plan-phase completes, verify plans were created:
159
+ After plan-phase completes, verify plans were created. This asks "did initial
160
+ planning write files to disk" — a planner-produced-nothing check, not
161
+ outstanding-work counting — so it takes the PHYSICAL set (`plan_count_all`,
162
+ `status: superseded` INCLUDED, #3218):
160
163
  ```bash
161
- PLAN_COUNT=$(ls ${phase_dir}/${padded_phase}-*-PLAN.md 2>/dev/null | wc -l)
164
+ PLAN_COUNT=$(gsd_run query find-phase "${PHASE}" | jq -r '.plan_count_all // 0')
162
165
  ```
163
166
 
164
167
  If PLAN_COUNT == 0: Error — initial planning failed. Exit.
@@ -254,6 +257,51 @@ Run this pass unless `plan_review.source_grounding` is `false`. It verifies ever
254
257
  - Signature mismatches cannot be asserted under `grep`/`intel`; report the signature as UNCHECKABLE.
255
258
  5. **Coverage block.** Append a "Verification coverage" section to `REVIEWS.md` listing every UNCHECKABLE/skipped symbol and why — a clean review must never silently mean "nothing was checked."
256
259
 
260
+ ### Cross-artifact fact-drift pass (same gate: `plan_review.source_grounding`)
261
+
262
+ Run this pass whenever the source-grounding pass ran — it is the second axis of the same drift guard, gated by the same `plan_review.source_grounding` key and adding no config surface of its own. Where source-grounding asks *"does this symbol exist in the source?"*, this asks *"does the project state the same fact in two planning artifacts, and do the two disagree?"* Because each phase runs in a fresh context, an agent typically reads only one artifact and trusts it, so a stale duplicate silently steers it wrong.
263
+
264
+ **Key on knowledge, not on similar text.** DRY is about a single authoritative representation of a piece of *knowledge*. Two passages that merely read alike, or that restate one fact at different levels of detail, are NOT drift. Only a contradiction is.
265
+
266
+ 1. **Phase status — decided by the seam, not by judgment.** Do not eyeball this axis:
267
+
268
+ ```bash
269
+ DRIFT=$(gsd_run drift-guard phase-status --phase "${PHASE}")
270
+ # $DRIFT is JSON: {"verdict":"consistent|lag|drifted|uncheckable","stateStatus":…,"roadmapStatus":…}
271
+ ```
272
+
273
+ - `drifted` — STATE.md and ROADMAP.md contradict each other. Report it; the authority is STATE.md.
274
+ - `lag` — one lifecycle step apart between non-terminal statuses. NOT a finding.
275
+ - `consistent` — nothing to report.
276
+ - `uncheckable` — a document was absent or carried a status outside both vocabularies. Record it in the coverage block; never read it as consistent.
277
+
278
+ Completeness is terminal: when exactly one side says the phase is complete, the verdict is `drifted` and never `lag`, however few steps apart the two words look.
279
+
280
+ 2. **Pair up the remaining facts by judgment.** The authority column names the source of truth, so a finding can say which side to keep:
281
+
282
+ | Fact class | Artifact pair | Authority | Decided by |
283
+ |---|---|---|---|
284
+ | Success criteria / must-have truths | ROADMAP.md Success Criteria ↔ PLAN.md `must_haves.truths` | ROADMAP.md | judgment |
285
+ | Requirement IDs | ROADMAP.md `**Requirements:**` ↔ PLAN.md task requirement refs | ROADMAP.md | judgment |
286
+ | Phase status | STATE.md status ↔ ROADMAP.md phase state | STATE.md | step 1 (deterministic) |
287
+ | Glossary / domain term | CONTEXT.md `Decisions` ↔ PLAN.md usage of the term | CONTEXT.md | judgment |
288
+
289
+ 3. **Judge each judgment pair.** FLAG only when ALL THREE hold:
290
+
291
+ 1. both sides name the *same* fact — same requirement ID, same success criterion, or the same defined term; and
292
+ 2. the two representations *contradict*, one asserting what the other denies, rather than differing in wording or in level of detail; and
293
+ 3. the pair is one of the judgment pairs above.
294
+
295
+ 4. **Record.** Emit each finding into `REVIEWS.md` beside the source-grounding coverage block, quoting both locations and naming the divergence and the authority, so the author can collapse the two copies to a single source of truth.
296
+
297
+ **Do NOT flag:** a wording-only difference that asserts the same thing; a fact that appears in one artifact only — single-source is the target state, not a finding; a PLAN that ADDS a truth beyond the roadmap Success Criteria, which is sanctioned (plans may add, never subtract); a `lag` verdict from step 1 — two non-terminal statuses a single lifecycle step apart, in either direction, since STATE.md is written at planning time independently of ROADMAP.md and can lead as readily as trail (a disagreement about *completion* is never lag, and step 1 already reports it as `drifted`); anything under CONTEXT.md's `Claude's Discretion` or `Deferred Ideas`, which are non-authoritative by design.
298
+
299
+ **Report once, not twice — these belong to `gsd-plan-checker`:** a PLAN that omits a roadmap Success Criterion is scope reduction (Dimension 7b); a requirement ID the ROADMAP never defines is requirement coverage (Dimension 1); two PLAN.md files in one phase disagreeing is cross-plan data contracts (Dimension 9).
300
+
301
+ **Severity: advisory, never a blocker.** This pass never sets `hardBlock`, and its findings contribute to neither `HIGH_COUNT` nor `ACTIONABLE_COUNT` — a project carrying pre-existing drift must still be able to converge, or an advisory check becomes an endless replan loop.
302
+
303
+ **Coverage, never silence.** If STATE.md or CONTEXT.md is absent, that axis is skipped and the skip is recorded in the same "Verification coverage" block. A clean pass must never mean "nothing was compared."
304
+
257
305
  After agent returns, verify REVIEWS.md exists:
258
306
  ```bash
259
307
  REVIEWS_FILE=$(ls ${phase_dir}/${padded_phase}-REVIEWS.md 2>/dev/null)
@@ -181,8 +181,14 @@ if [ -z "$ROADMAP" ]; then
181
181
  else
182
182
  for PHASE_NUM in $(echo "$ROADMAP" | jq -r '.phases[] | (.number // .phase_number)'); do
183
183
  PHASE_DATA=$(echo "$ROADMAP" | jq --arg n "$PHASE_NUM" '.phases[] | select((.number // .phase_number) == ($n | tonumber))')
184
- PLAN_COUNT=$(echo "$PHASE_DATA" | jq '(.plans // []) | length')
185
- SUMMARY_COUNT=$(echo "$PHASE_DATA" | jq '(.summaries // []) | length')
184
+ # #3218: $PHASE_DATA is a `.phases[]` entry from `roadmap.analyze`, which
185
+ # emits `plan_count`/`summary_count` SCALARS (src/roadmap.cts) — it has
186
+ # never emitted `.plans`/`.summaries` ARRAYS. Reading those absent keys
187
+ # (even with a `// []` fallback) always produced 0, permanently disabling
188
+ # this resume-incomplete-phase check. Read the scalars the producer
189
+ # actually emits.
190
+ PLAN_COUNT=$(echo "$PHASE_DATA" | jq '.plan_count // 0')
191
+ SUMMARY_COUNT=$(echo "$PHASE_DATA" | jq '.summary_count // 0')
186
192
  if [ "${PLAN_COUNT:-0}" -gt "${SUMMARY_COUNT:-0}" ]; then
187
193
  INCOMPLETE_PHASE="$PHASE_NUM"
188
194
  break
@@ -213,11 +219,13 @@ Then exit the route step. Do NOT run Steps 1 through Routes A-F.
213
219
 
214
220
  **Step 1: Count plans, summaries, and issues in current phase**
215
221
 
216
- List files in the current phase directory:
222
+ Get plan/summary counts for the current phase from the single owner (#3218 — LIVE
223
+ counts, i.e. `status: superseded` plans excluded, matching "outstanding work"):
217
224
 
218
225
  ```bash
219
- (ls -1 .planning/phases/[current-phase-dir]/*-PLAN.md 2>/dev/null || true) | wc -l
220
- (ls -1 .planning/phases/[current-phase-dir]/*-SUMMARY.md 2>/dev/null || true) | wc -l
226
+ PHASE_COUNTS=$(gsd_run query find-phase "${CURRENT_PHASE}")
227
+ X=$(echo "$PHASE_COUNTS" | jq -r '.plan_count // 0')
228
+ Y=$(echo "$PHASE_COUNTS" | jq -r '.summary_count // 0')
221
229
  (ls -1 .planning/phases/[current-phase-dir]/*-UAT.md 2>/dev/null || true) | wc -l
222
230
  ```
223
231
 
@@ -450,7 +458,17 @@ UAT.md exists with `status: partial` — testing session ended before all items
450
458
  All plans have summaries, but canonical verification has not passed. The phase is implementation-complete, not phase-complete.
451
459
 
452
460
  ```
453
- `/gsd:execute-phase {phase} ${GSD_WS}` — re-run execution verification
461
+ ---
462
+
463
+ ## Verification Report Missing
464
+
465
+ **Phase {phase}** has all plans summarized, but no canonical `*-VERIFICATION.md` exists yet. ${VERIFICATION_NEXT_ACTION}
466
+
467
+ `/clear` then:
468
+
469
+ `/gsd:execute-phase {phase} ${GSD_WS}` — resumes at the verification gates
470
+
471
+ ---
454
472
  ```
455
473
 
456
474
  ---
@@ -460,7 +478,17 @@ All plans have summaries, but canonical verification has not passed. The phase i
460
478
  VERIFICATION.md has an unexpected status. The phase is implementation-complete, not phase-complete.
461
479
 
462
480
  ```
481
+ ---
482
+
483
+ ## Verification Status Unexpected
484
+
485
+ **Phase {phase}** has all plans summarized, but its `*-VERIFICATION.md` reports an unexpected status. ${VERIFICATION_NEXT_ACTION}
486
+
487
+ `/clear` then:
488
+
463
489
  `/gsd:execute-phase {phase} ${GSD_WS}` — regenerate verification
490
+
491
+ ---
464
492
  ```
465
493
 
466
494
  ---
@@ -18,9 +18,9 @@ Checker prompt:
18
18
  **Mode:** quick-full
19
19
  **Task Description:** ${DESCRIPTION}
20
20
 
21
- <files_to_read>
21
+ <required_reading>
22
22
  - ${QUICK_DIR}/${quick_id}-PLAN.md (Plan to verify)
23
- </files_to_read>
23
+ </required_reading>
24
24
 
25
25
  ${AGENT_SKILLS_CHECKER}
26
26
 
@@ -74,9 +74,9 @@ Revision prompt:
74
74
  <revision_context>
75
75
  **Mode:** quick-full (revision)
76
76
 
77
- <files_to_read>
77
+ <required_reading>
78
78
  - ${QUICK_DIR}/${quick_id}-PLAN.md (Existing plan)
79
- </files_to_read>
79
+ </required_reading>
80
80
 
81
81
  ${AGENT_SKILLS_PLANNER}
82
82
 
@@ -17,9 +17,9 @@ Agent(
17
17
  Task directory: ${QUICK_DIR}
18
18
  Task goal: ${DESCRIPTION}
19
19
 
20
- <files_to_read>
20
+ <required_reading>
21
21
  - ${QUICK_DIR}/${quick_id}-PLAN.md (Plan)
22
- </files_to_read>
22
+ </required_reading>
23
23
 
24
24
  ${AGENT_SKILLS_VERIFIER}
25
25
 
@@ -32,15 +32,36 @@ Check must_haves against actual codebase. Create VERIFICATION.md at ${QUICK_DIR}
32
32
 
33
33
  > **ORCHESTRATOR RULE — CODEX RUNTIME**: After calling Agent() above, stop working on this task immediately. Do not read more files, edit code, or run tests related to this task while the subagent is active. Wait for the subagent to return its result. This prevents duplicate work, conflicting edits, and wasted context. Only resume when the subagent result is available.
34
34
 
35
- Read verification status:
35
+ Read verification status via the canonical query (frontmatter-anchored, and total over its input space):
36
36
  ```bash
37
- grep "^status:" "${QUICK_DIR}/${quick_id}-VERIFICATION.md" | cut -d: -f2 | tr -d ' '
37
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
38
+ STATUS=$(gsd_run query verification.status "${QUICK_DIR}" --pick status 2>/dev/null)
38
39
  ```
39
40
 
40
- Store as `$VERIFICATION_STATUS`.
41
+ `--pick status` returns the bare value, so this path needs **no `jq`**. That is deliberate: #2589
42
+ established that a `| jq -r '.field'` pipe yields an **empty** variable with no diagnostic on any
43
+ machine without jq — the default on Windows/Git-Bash — which here would route a perfectly good
44
+ `passed` verification into the recovery arm below.
41
45
 
42
- | Status | Action |
46
+ Route on `$STATUS` and store the display string as `$VERIFICATION_STATUS` (consumed by the quick index row and the completion banner).
47
+
48
+ The query is **total**: beyond the verifier's own statuses it can also return `missing` (no `*-VERIFICATION.md`, or no `status` in its frontmatter), `unknown` (a value outside the verifier's schema) or `stale` (a summary newer than the verification file). Which one wins when more than one applies is the query's own precedence, not this table's concern — all three land in the same arm here. That arm is reachable in normal operation and must never be dropped.
49
+
50
+ | `$STATUS` | Action |
43
51
  |--------|--------|
44
52
  | `passed` | Store `$VERIFICATION_STATUS = "Verified"`, continue to step 7 |
45
53
  | `human_needed` | Display items needing manual check, store `$VERIFICATION_STATUS = "Needs Review"`, continue |
46
54
  | `gaps_found` | Display gap summary, offer: 1) Re-run executor to fix gaps, 2) Accept as-is. Store `$VERIFICATION_STATUS = "Gaps"` |
55
+ | anything else — `missing`, `unknown`, `stale`, or empty | Do **not** improvise a result. Report that verification produced no usable status, naming `$STATUS`, then offer: 1) Re-run the verifier, 2) Accept as-is without verification. Store `$VERIFICATION_STATUS = "Unverified (${STATUS:-no result})"` |
56
+
57
+ > **Why the status only, and not `next_action` / `next_command`.** The query projects those two for
58
+ > the phase pipeline — they name `execute-phase`, `plan-phase --gaps` and `verify-work`, and they
59
+ > append a phase-number argument taken from the directory basename. A quick task directory is named
60
+ > `${quick_id}-${slug}` with a date-derived `quick_id`, so that argument resolves to the date: for
61
+ > `260808-abc-some-slug` the projected recovery command carries `260808` as its phase argument — a
62
+ > date posing as a phase number.
63
+ > Quick therefore supplies its own recovery actions above. The split is the point:
64
+ > `readVerificationStatus` *discovers and parses* shape-agnostically — it scans whatever directory
65
+ > it is given for `*-VERIFICATION.md` — which is what makes the status half correct for
66
+ > `${QUICK_DIR}`; but it also reads that directory's basename as a phase token to build the
67
+ > projected commands, and that is the half quick must not use.