@opengsd/gsd-core 1.11.0 → 1.12.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 (395) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +1 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-dom-verifier.md +169 -0
  7. package/agents/gsd-eval-auditor.md +1 -1
  8. package/agents/gsd-executor.md +17 -9
  9. package/agents/gsd-framework-selector.md +1 -3
  10. package/agents/gsd-intel-updater.md +1 -1
  11. package/agents/gsd-mempalace-curator.md +0 -1
  12. package/agents/gsd-pattern-mapper.md +11 -0
  13. package/agents/gsd-phase-researcher.md +3 -1
  14. package/agents/gsd-plan-checker.md +15 -55
  15. package/agents/gsd-planner.md +6 -4
  16. package/agents/gsd-project-researcher.md +1 -1
  17. package/agents/gsd-research-synthesizer.md +2 -2
  18. package/agents/gsd-roadmapper.md +15 -11
  19. package/agents/gsd-ui-checker.md +63 -4
  20. package/agents/gsd-ui-researcher.md +41 -3
  21. package/agents/gsd-verifier.md +1 -1
  22. package/bin/install.js +609 -134
  23. package/commands/gsd/discuss-phase.md +1 -1
  24. package/commands/gsd/import.md +1 -1
  25. package/commands/gsd/quick.md +8 -4
  26. package/gsd-core/bin/gsd-tools.cjs +567 -51
  27. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  28. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  29. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  30. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  31. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  32. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  33. package/gsd-core/bin/lib/audit.cjs +163 -41
  34. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  35. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  36. package/gsd-core/bin/lib/capability-registry.cjs +336 -95
  37. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  38. package/gsd-core/bin/lib/capability-validator.cjs +205 -18
  39. package/gsd-core/bin/lib/check-command-router.cjs +145 -5
  40. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  41. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  42. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  43. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  44. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  45. package/gsd-core/bin/lib/commands.cjs +543 -44
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  47. package/gsd-core/bin/lib/config-loader.cjs +118 -29
  48. package/gsd-core/bin/lib/config.cjs +92 -2
  49. package/gsd-core/bin/lib/configuration.cjs +129 -37
  50. package/gsd-core/bin/lib/core-utils.cjs +84 -7
  51. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  52. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  53. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  54. package/gsd-core/bin/lib/frontmatter.cjs +840 -305
  55. package/gsd-core/bin/lib/gap-checker.cjs +27 -3
  56. package/gsd-core/bin/lib/git-base-branch.cjs +174 -39
  57. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  58. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +6 -3
  59. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  60. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  61. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  62. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  63. package/gsd-core/bin/lib/init.cjs +120 -41
  64. package/gsd-core/bin/lib/install-engine.cjs +68 -3
  65. package/gsd-core/bin/lib/install-model-override-resolver.cjs +33 -1
  66. package/gsd-core/bin/lib/install-profiles.cjs +78 -4
  67. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  68. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  69. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  70. package/gsd-core/bin/lib/intel.cjs +101 -26
  71. package/gsd-core/bin/lib/io.cjs +160 -15
  72. package/gsd-core/bin/lib/learnings.cjs +85 -14
  73. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  74. package/gsd-core/bin/lib/markdown-table.cjs +52 -4
  75. package/gsd-core/bin/lib/milestone.cjs +90 -5
  76. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  77. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  78. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  79. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  80. package/gsd-core/bin/lib/phase-id.cjs +70 -4
  81. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  82. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  83. package/gsd-core/bin/lib/phase.cjs +405 -84
  84. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  85. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  86. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  87. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  88. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -14
  89. package/gsd-core/bin/lib/planning-workspace.cjs +56 -0
  90. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  91. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  92. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  93. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  94. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  95. package/gsd-core/bin/lib/review-lane-descriptor.cjs +9 -9
  96. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  97. package/gsd-core/bin/lib/roadmap-parser.cjs +79 -16
  98. package/gsd-core/bin/lib/roadmap.cjs +74 -19
  99. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +96 -8
  100. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +34 -1
  101. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +287 -55
  102. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  103. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  104. package/gsd-core/bin/lib/shell-command-projection.cjs +71 -8
  105. package/gsd-core/bin/lib/smart-entry.cjs +12 -22
  106. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  107. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  108. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  109. package/gsd-core/bin/lib/state-document.cjs +186 -0
  110. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  111. package/gsd-core/bin/lib/state-transition.cjs +517 -101
  112. package/gsd-core/bin/lib/state.cjs +946 -163
  113. package/gsd-core/bin/lib/surface.cjs +10 -2
  114. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  115. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  116. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  117. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  118. package/gsd-core/bin/lib/uat.cjs +1376 -125
  119. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  120. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  121. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  122. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  123. package/gsd-core/bin/lib/vendor/README.md +43 -5
  124. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  125. package/gsd-core/bin/lib/verification.cjs +14 -1
  126. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  127. package/gsd-core/bin/lib/verify.cjs +95 -40
  128. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  129. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  130. package/gsd-core/bin/lib/worktree-safety.cjs +177 -21
  131. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  132. package/gsd-core/bin/shared/config-schema.manifest.json +5 -0
  133. package/gsd-core/bin/shared/exit-codes.json +8 -0
  134. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  135. package/gsd-core/bin/shared/model-catalog.json +8 -1
  136. package/gsd-core/references/agent-contracts.md +3 -2
  137. package/gsd-core/references/api-coverage.md +24 -2
  138. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  139. package/gsd-core/references/checkpoints.md +37 -19
  140. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  141. package/gsd-core/references/edge-probe.md +8 -0
  142. package/gsd-core/references/execute-mvp-tdd.md +1 -3
  143. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  144. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  145. package/gsd-core/references/failing-direction.md +78 -0
  146. package/gsd-core/references/gate-prompts.md +1 -1
  147. package/gsd-core/references/git-integration.md +5 -5
  148. package/gsd-core/references/git-planning-commit.md +3 -3
  149. package/gsd-core/references/gsd-run-resolver.md +1 -1
  150. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  151. package/gsd-core/references/model-profiles.md +1 -1
  152. package/gsd-core/references/nyquist-compliance.md +74 -0
  153. package/gsd-core/references/offer-next.md +3 -5
  154. package/gsd-core/references/phase-argument-parsing.md +3 -3
  155. package/gsd-core/references/planner-failing-direction.md +53 -0
  156. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  157. package/gsd-core/references/planner-revision.md +1 -1
  158. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  159. package/gsd-core/references/planning-config.md +37 -8
  160. package/gsd-core/references/reviewer-instances.md +31 -0
  161. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  162. package/gsd-core/references/tdd.md +1 -3
  163. package/gsd-core/references/ui-brand.md +65 -21
  164. package/gsd-core/references/ui-consideration-probe.md +1 -1
  165. package/gsd-core/references/universal-anti-patterns.md +2 -2
  166. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  167. package/gsd-core/references/verify-mvp-mode.md +1 -1
  168. package/gsd-core/references/workstream-flag.md +11 -11
  169. package/gsd-core/templates/README.md +1 -1
  170. package/gsd-core/templates/SECURITY.md +3 -3
  171. package/gsd-core/templates/UI-SPEC.md +25 -3
  172. package/gsd-core/templates/VALIDATION.md +3 -3
  173. package/gsd-core/templates/phase-prompt.md +3 -0
  174. package/gsd-core/templates/state.md +7 -0
  175. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  176. package/gsd-core/workflows/add-backlog.md +1 -1
  177. package/gsd-core/workflows/add-phase.md +3 -3
  178. package/gsd-core/workflows/add-tests.md +3 -8
  179. package/gsd-core/workflows/add-todo.md +1 -1
  180. package/gsd-core/workflows/ai-integration-phase.md +4 -9
  181. package/gsd-core/workflows/audit-fix.md +12 -3
  182. package/gsd-core/workflows/audit-milestone.md +9 -9
  183. package/gsd-core/workflows/audit-uat.md +17 -2
  184. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  185. package/gsd-core/workflows/autonomous.md +10 -26
  186. package/gsd-core/workflows/check-todos.md +1 -1
  187. package/gsd-core/workflows/cleanup.md +2 -2
  188. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +1 -1
  189. package/gsd-core/workflows/code-review-fix.md +1 -1
  190. package/gsd-core/workflows/code-review.md +121 -40
  191. package/gsd-core/workflows/complete-milestone.md +15 -10
  192. package/gsd-core/workflows/debug.md +5 -3
  193. package/gsd-core/workflows/diagnose-issues.md +12 -6
  194. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  195. package/gsd-core/workflows/discuss-phase/modes/chain.md +3 -7
  196. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  197. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  198. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -2
  199. package/gsd-core/workflows/discuss-phase.md +1 -1
  200. package/gsd-core/workflows/do.md +3 -6
  201. package/gsd-core/workflows/docs-update.md +5 -4
  202. package/gsd-core/workflows/edit-phase.md +1 -1
  203. package/gsd-core/workflows/eval-review.md +4 -9
  204. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  205. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +113 -11
  206. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  207. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  208. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  209. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +22 -4
  210. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  211. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  212. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  213. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  214. package/gsd-core/workflows/execute-phase.md +38 -54
  215. package/gsd-core/workflows/execute-plan.md +17 -12
  216. package/gsd-core/workflows/explore.md +1 -1
  217. package/gsd-core/workflows/extract-learnings.md +1 -1
  218. package/gsd-core/workflows/fast.md +2 -2
  219. package/gsd-core/workflows/forensics.md +1 -1
  220. package/gsd-core/workflows/graduation.md +5 -5
  221. package/gsd-core/workflows/health.md +3 -6
  222. package/gsd-core/workflows/import.md +14 -11
  223. package/gsd-core/workflows/inbox.md +4 -5
  224. package/gsd-core/workflows/ingest-docs.md +44 -11
  225. package/gsd-core/workflows/insert-phase.md +5 -5
  226. package/gsd-core/workflows/list-seeds.md +5 -3
  227. package/gsd-core/workflows/list-workspaces.md +1 -1
  228. package/gsd-core/workflows/manager.md +12 -23
  229. package/gsd-core/workflows/map-codebase.md +1 -1
  230. package/gsd-core/workflows/milestone-summary.md +1 -1
  231. package/gsd-core/workflows/mvp-phase.md +2 -2
  232. package/gsd-core/workflows/new-milestone.md +9 -21
  233. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  234. package/gsd-core/workflows/new-project.md +12 -26
  235. package/gsd-core/workflows/new-workspace.md +1 -1
  236. package/gsd-core/workflows/next.md +2 -2
  237. package/gsd-core/workflows/pause-work.md +1 -1
  238. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  239. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  240. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  241. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  242. package/gsd-core/workflows/plan-phase.md +121 -42
  243. package/gsd-core/workflows/plan-review-convergence.md +46 -9
  244. package/gsd-core/workflows/plant-seed.md +2 -2
  245. package/gsd-core/workflows/pr-branch.md +187 -51
  246. package/gsd-core/workflows/profile-user.md +16 -14
  247. package/gsd-core/workflows/progress.md +27 -12
  248. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  249. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +1 -3
  250. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  251. package/gsd-core/workflows/quick/steps/research-phase.md +2 -4
  252. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  253. package/gsd-core/workflows/quick.md +20 -29
  254. package/gsd-core/workflows/remove-phase.md +4 -4
  255. package/gsd-core/workflows/remove-workspace.md +2 -2
  256. package/gsd-core/workflows/resume-project.md +8 -12
  257. package/gsd-core/workflows/review.md +193 -15
  258. package/gsd-core/workflows/scan.md +1 -1
  259. package/gsd-core/workflows/secure-phase.md +2 -2
  260. package/gsd-core/workflows/settings-advanced.md +7 -9
  261. package/gsd-core/workflows/settings-integrations.md +64 -31
  262. package/gsd-core/workflows/settings.md +3 -5
  263. package/gsd-core/workflows/ship.md +12 -6
  264. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  265. package/gsd-core/workflows/sketch.md +12 -18
  266. package/gsd-core/workflows/smart-entry.md +3 -5
  267. package/gsd-core/workflows/spec-phase.md +23 -1
  268. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  269. package/gsd-core/workflows/spike.md +20 -31
  270. package/gsd-core/workflows/stats.md +2 -2
  271. package/gsd-core/workflows/sync-skills.md +1 -1
  272. package/gsd-core/workflows/thread.md +11 -7
  273. package/gsd-core/workflows/transition.md +5 -5
  274. package/gsd-core/workflows/ui-phase.md +10 -16
  275. package/gsd-core/workflows/ui-review.md +6 -10
  276. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  277. package/gsd-core/workflows/undo.md +8 -16
  278. package/gsd-core/workflows/update.md +6 -10
  279. package/gsd-core/workflows/validate-phase.md +2 -2
  280. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  281. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  282. package/gsd-core/workflows/verify-work.md +57 -18
  283. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  284. package/hooks/dist/gsd-config-reload.js +18 -12
  285. package/hooks/dist/gsd-context-monitor.js +19 -10
  286. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  287. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  288. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  289. package/hooks/dist/gsd-cursor-stop.js +2 -1
  290. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  291. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  292. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  293. package/hooks/dist/gsd-graphify-update.sh +22 -18
  294. package/hooks/dist/gsd-node-runner.sh +76 -0
  295. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  296. package/hooks/dist/gsd-prompt-guard.js +16 -7
  297. package/hooks/dist/gsd-read-guard.js +16 -7
  298. package/hooks/dist/gsd-read-injection-scanner.js +17 -8
  299. package/hooks/dist/gsd-session-state.sh +1 -0
  300. package/hooks/dist/gsd-statusline.js +215 -26
  301. package/hooks/dist/gsd-validate-commit.sh +80 -6
  302. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  303. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  304. package/hooks/dist/gsd-workflow-guard.js +34 -16
  305. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  306. package/hooks/dist/gsd-write-guard.js +35 -25
  307. package/hooks/dist/lib/cli-exit.js +560 -0
  308. package/hooks/dist/lib/exit-code-registry.js +98 -0
  309. package/hooks/dist/lib/git-probe.js +84 -0
  310. package/hooks/dist/lib/hook-exit.js +81 -0
  311. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  312. package/hooks/gsd-agent-isolation-guard.js +77 -38
  313. package/hooks/gsd-config-reload.js +18 -12
  314. package/hooks/gsd-context-monitor.js +19 -10
  315. package/hooks/gsd-cursor-post-tool.js +3 -1
  316. package/hooks/gsd-cursor-pre-tool.js +3 -1
  317. package/hooks/gsd-cursor-session-start.js +2 -1
  318. package/hooks/gsd-cursor-stop.js +2 -1
  319. package/hooks/gsd-cursor-subagent-start.js +28 -23
  320. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  321. package/hooks/gsd-ensure-canonical-path.js +2 -1
  322. package/hooks/gsd-graphify-update.sh +22 -18
  323. package/hooks/gsd-node-runner.sh +76 -0
  324. package/hooks/gsd-phase-boundary.sh +1 -0
  325. package/hooks/gsd-prompt-guard.js +16 -7
  326. package/hooks/gsd-read-guard.js +16 -7
  327. package/hooks/gsd-read-injection-scanner.js +17 -8
  328. package/hooks/gsd-session-state.sh +1 -0
  329. package/hooks/gsd-statusline.js +215 -26
  330. package/hooks/gsd-validate-commit.sh +80 -6
  331. package/hooks/gsd-windsurf-pre-command.js +16 -11
  332. package/hooks/gsd-windsurf-pre-write.js +22 -13
  333. package/hooks/gsd-workflow-guard.js +34 -16
  334. package/hooks/gsd-worktree-path-guard.js +36 -21
  335. package/hooks/gsd-write-guard.js +35 -25
  336. package/hooks/lib/cli-exit.js +560 -0
  337. package/hooks/lib/exit-code-registry.js +98 -0
  338. package/hooks/lib/git-probe.js +84 -0
  339. package/hooks/lib/hook-exit.js +81 -0
  340. package/hooks/managed-hooks-registry.cjs +3 -0
  341. package/package.json +12 -7
  342. package/scripts/base64-scan.sh +74 -12
  343. package/scripts/build-hooks.js +5 -0
  344. package/scripts/check-glossary-refs.cjs +77 -15
  345. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  346. package/scripts/ci-check-job-near-cap.cjs +49 -0
  347. package/scripts/ci-pr-mergeability.cjs +262 -0
  348. package/scripts/ci-test-scope.cjs +45 -12
  349. package/scripts/ci-timeout-report.cjs +230 -0
  350. package/scripts/docs-guard-registry.cjs +396 -0
  351. package/scripts/gen-capability-registry.cjs +8 -6
  352. package/scripts/gen-exit-code-docs.cjs +318 -0
  353. package/scripts/gen-exit-code-registry.cjs +891 -0
  354. package/scripts/gen-features.cjs +836 -0
  355. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  356. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  357. package/scripts/gen-loop-host-contract.cjs +134 -1
  358. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  359. package/scripts/gen-state-md-docs.cjs +727 -0
  360. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  361. package/scripts/lib/ci-job-timing.cjs +72 -0
  362. package/scripts/lib/cli-exit.cjs +546 -44
  363. package/scripts/lib/drift-scan.cjs +32 -2
  364. package/scripts/lib/exit-code-registry.cjs +98 -0
  365. package/scripts/lib/ndjson-reporter.cjs +119 -0
  366. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  367. package/scripts/lint-docs-guard-registration.cjs +495 -0
  368. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  369. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  370. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  371. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  372. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  373. package/scripts/lint-phase-enumeration-drift.cjs +21 -8
  374. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  375. package/scripts/lint-removed-but-needed.cjs +184 -16
  376. package/scripts/lint-seam-enforcement.cjs +182 -0
  377. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  378. package/scripts/lint-source-test-name-collision.cjs +241 -0
  379. package/scripts/lint-state-write-path-drift.cjs +337 -432
  380. package/scripts/lint-test-file-count.allowlist.json +122 -4
  381. package/scripts/lint-test-file-count.cjs +25 -3
  382. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  383. package/scripts/lint-vendored-deps.cjs +208 -35
  384. package/scripts/mutation-matrix.cjs +599 -50
  385. package/scripts/prompt-injection-scan.sh +75 -14
  386. package/scripts/secret-scan.sh +75 -13
  387. package/scripts/select-docs-guards.cjs +56 -0
  388. package/scripts/sync-runtime-launcher.cjs +22 -3
  389. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  390. package/skills/gsd-import/SKILL.md +1 -1
  391. package/skills/gsd-quick/SKILL.md +8 -4
  392. package/vscode/package.json +1 -1
  393. package/bin/lib/ui-safety-gate.cjs +0 -109
  394. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  395. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -374,6 +374,12 @@ function normalizeCleanupManifestEntry(entry) {
374
374
  // would make an unrecorded plan look 100% out of scope.
375
375
  const filesModified = (Array.isArray(e.files_modified) ? e.files_modified : [])
376
376
  .filter((f) => typeof f === 'string' && f.trim().length > 0);
377
+ // #3003: same liberal-in-what-we-accept rule as `files_modified` above — non-array,
378
+ // or non-string / empty elements, are dropped rather than coerced, and an EMPTY
379
+ // result omits the field entirely so "declares nothing" stays indistinguishable
380
+ // from "not recorded" (the guard's own absence-check already treats both as unknown).
381
+ const declaredDeletions = (Array.isArray(e.declared_deletions) ? e.declared_deletions : [])
382
+ .filter((f) => typeof f === 'string' && f.trim().length > 0);
377
383
  const normalized = {
378
384
  agent_id: typeof e.agent_id === 'string' ? e.agent_id : null,
379
385
  worktree_path: worktreePath,
@@ -383,6 +389,8 @@ function normalizeCleanupManifestEntry(entry) {
383
389
  };
384
390
  if (filesModified.length > 0)
385
391
  normalized.files_modified = filesModified;
392
+ if (declaredDeletions.length > 0)
393
+ normalized.declared_deletions = declaredDeletions;
386
394
  return normalized;
387
395
  }
388
396
  function normalizeCleanupManifest(manifest) {
@@ -480,16 +488,79 @@ function repoRootStillMidMerge(execGit, repoRoot) {
480
488
  // advisory fire on essentially every wave.
481
489
  const SUMMARY_ARTIFACT_DIR = '.planning';
482
490
  const SUMMARY_ARTIFACT_SUFFIX = 'SUMMARY.md';
491
+ /**
492
+ * Decode a git-quoted path. With `core.quotepath` left at its default (`true`)
493
+ * git wraps any path containing non-ASCII or special bytes in double quotes
494
+ * and C-escapes it — e.g. `tests/é.ts` is emitted as the literal
495
+ * `"tests/\303\251.ts"`. Both sides of the deletion/scope comparison
496
+ * (a declared path and a git-reported path) must agree on the same decoded
497
+ * shape, and decoding it here — rather than passing `-c core.quotepath=false`
498
+ * on the `execGit` call — keeps the git argv, and therefore every existing
499
+ * test fixture that asserts on exact argv, unchanged. It also works
500
+ * regardless of the user's own `core.quotepath` config.
501
+ *
502
+ * A value that is not wrapped in a leading and trailing `"` is returned
503
+ * completely untouched — this is the overwhelmingly common (plain ASCII)
504
+ * case and must not be altered in any way.
505
+ *
506
+ * Escapes decode to BYTES, collected into a Buffer and decoded as UTF-8 only
507
+ * at the end: `\303\251` is two bytes that together form one character (é),
508
+ * so decoding them one at a time would produce mojibake. Malformed input
509
+ * (a trailing lone backslash, or an octal escape with fewer than three
510
+ * digits) never throws — it degrades to treating the character literally, so
511
+ * a single bad path can never take down the whole cleanup wave.
512
+ */
513
+ function decodeGitQuotedPath(raw) {
514
+ if (raw.length < 2 || !raw.startsWith('"') || !raw.endsWith('"'))
515
+ return raw;
516
+ const body = raw.slice(1, -1);
517
+ const bytes = [];
518
+ for (let i = 0; i < body.length; i++) {
519
+ const ch = body[i];
520
+ if (ch !== '\\' || i === body.length - 1) {
521
+ // UTF-8 bytes, not the code unit: a DECLARED path may be quoted while
522
+ // still holding a literal `é`, and pushing 0xE9 alone is invalid UTF-8.
523
+ // codePointAt keeps a surrogate pair together.
524
+ const codePoint = body.codePointAt(i);
525
+ const char = codePoint === undefined ? ch : String.fromCodePoint(codePoint);
526
+ bytes.push(...Buffer.from(char, 'utf8'));
527
+ i += char.length - 1;
528
+ continue;
529
+ }
530
+ const rest = body.slice(i + 1);
531
+ const octalMatch = /^([0-3][0-7][0-7])/.exec(rest);
532
+ if (octalMatch) {
533
+ bytes.push(parseInt(octalMatch[1], 8));
534
+ i += 3;
535
+ continue;
536
+ }
537
+ const next = body[i + 1];
538
+ const CONTROL_ESCAPES = {
539
+ a: 0x07, b: 0x08, f: 0x0c, n: 0x0a, r: 0x0d, t: 0x09, v: 0x0b,
540
+ '\\': 0x5c, '"': 0x22,
541
+ };
542
+ if (Object.prototype.hasOwnProperty.call(CONTROL_ESCAPES, next)) {
543
+ bytes.push(CONTROL_ESCAPES[next]);
544
+ }
545
+ else {
546
+ bytes.push(next.charCodeAt(0));
547
+ }
548
+ i += 1;
549
+ }
550
+ return Buffer.from(bytes).toString('utf8');
551
+ }
483
552
  /**
484
553
  * Normalize one path for scope comparison. Applied to BOTH sides so a declared
485
- * path and a git-reported path meet in the same shape: backslashes become
486
- * slashes unconditionally (a backslash path is not a Windows-only input),
487
- * a leading `./` and any trailing `/` are stripped. This is the single
488
- * normalizer shared by the SUMMARY-artifact predicate and the scope advisory,
489
- * so the two can never disagree about what `./a\b/` means.
554
+ * path and a git-reported path meet in the same shape: any git C-quoting is
555
+ * decoded first (order matters — the backslash-to-slash conversion below
556
+ * would destroy the escape sequences if it ran first), then backslashes
557
+ * become slashes unconditionally (a backslash path is not a Windows-only
558
+ * input), and a leading `./` and any trailing `/` are stripped. This is the
559
+ * single normalizer shared by the SUMMARY-artifact predicate and the scope
560
+ * advisory, so the two can never disagree about what `./a\b/` means.
490
561
  */
491
562
  function normalizeScopePath(raw) {
492
- return String(raw || '')
563
+ return decodeGitQuotedPath(String(raw || '').trim())
493
564
  .replace(/\\/g, '/')
494
565
  .trim()
495
566
  .replace(/^\.\//, '')
@@ -703,6 +774,42 @@ function planWaveScopeConformance(changedPaths, declaredFiles, branch) {
703
774
  }
704
775
  return warnings;
705
776
  }
777
+ /**
778
+ * #3003: split git's reported deletions into declared and undeclared.
779
+ *
780
+ * EXACT match after `normalizeScopePath` — the same normalizer both other path
781
+ * comparisons in this file use, so a declared path and a git-reported path meet in the
782
+ * same shape (`a\b` and `./a/b/` both become `a/b`).
783
+ *
784
+ * Deliberately NOT `declaredScopePrefix`. That helper returns `null` for a glob-leading
785
+ * pattern meaning "matches everything", which is right for the ADVISORY it serves — a
786
+ * false alarm there costs more than a miss — and exactly wrong for a GATE, where it would
787
+ * let `["*.ts"]` disarm the guard. A glob here is simply a literal path that matches
788
+ * nothing. Prefix matching is likewise refused: `["tests"]` must not authorize deleting
789
+ * everything under tests/.
790
+ *
791
+ * Pure — no git, no IO. Returns the undeclared residue in the order git reported it.
792
+ */
793
+ function partitionDeclaredDeletions(deletedPaths, declared) {
794
+ const allowed = new Set((Array.isArray(declared) ? declared : [])
795
+ .filter((p) => typeof p === 'string')
796
+ .map((p) => normalizeScopePath(p))
797
+ .filter((p) => p.length > 0));
798
+ const undeclared = [];
799
+ const seen = new Set();
800
+ for (const raw of Array.isArray(deletedPaths) ? deletedPaths : []) {
801
+ if (typeof raw !== 'string')
802
+ continue;
803
+ const normalized = normalizeScopePath(raw);
804
+ if (!normalized || seen.has(normalized))
805
+ continue;
806
+ seen.add(normalized);
807
+ if (allowed.has(normalized))
808
+ continue;
809
+ undeclared.push(normalized);
810
+ }
811
+ return undeclared;
812
+ }
706
813
  function executeWorktreeWaveCleanupPlan(plan, deps = {}) {
707
814
  const execGit = deps.execGit || execGitDefault;
708
815
  const entries = Array.isArray(plan?.entries) ? plan.entries : [];
@@ -762,13 +869,17 @@ function executeWorktreeWaveCleanupPlan(plan, deps = {}) {
762
869
  continue; // #2852: isolate
763
870
  }
764
871
  if (deletions.stdout) {
765
- // Unconditional: any deletion in this entry's branch blocks THIS entry. Whether
766
- // that guard should have an opt-in for intentional deletions is a deferred
767
- // product decision (issue #2852's own triage scoped it out — tracked in #3003);
768
- // this fix only isolates the block to this one entry (#2852) instead of aborting
769
- // the rest of the wave, same as every other block reason below.
770
- blockEntry(result, 'branch_contains_deletions', deletions.stdout);
771
- continue; // #2852: isolate
872
+ // #3003: a deletion the PLAN declared is authorized; anything else still blocks.
873
+ // Only a SUCCESSFUL check reaches here — a failed one blocked above on its own
874
+ // reason, so a broken check can never be filtered into a pass.
875
+ //
876
+ // The block detail carries ONLY the undeclared residue. Listing declared paths
877
+ // there would misdirect the operator toward paths that were fine.
878
+ const undeclaredDeletions = partitionDeclaredDeletions(deletions.stdout.split('\n'), entry.declared_deletions);
879
+ if (undeclaredDeletions.length > 0) {
880
+ blockEntry(result, 'branch_contains_deletions', undeclaredDeletions.join('\n'));
881
+ continue; // #2852: isolate
882
+ }
772
883
  }
773
884
  // #2596: advisory scope conformance — does the branch's ACTUAL committed
774
885
  // diff stay inside the scope the plan declared? Gated on a declared scope
@@ -779,13 +890,34 @@ function executeWorktreeWaveCleanupPlan(plan, deps = {}) {
779
890
  // ADVISORY ONLY. Unlike the deletions check above, a finding here does NOT
780
891
  // call blockEntry and does NOT touch `ok` — the merge proceeds. Promotion
781
892
  // to a hard gate is a separate, disclosed change.
782
- if (Array.isArray(entry.files_modified) && entry.files_modified.length > 0) {
893
+ // #3003: a declared deletion is in scope by construction — `git diff --name-only`
894
+ // includes deleted paths, so without accounting for the declaration, authorizing a
895
+ // deletion would immediately warn that the same path is out of declared scope.
896
+ //
897
+ // It is SUBTRACTED from the findings rather than UNIONED into the declared scope,
898
+ // and the difference is not cosmetic. `planWaveScopeConformance` reads its scope
899
+ // list with prefix-and-glob semantics, so unioning would silently hand
900
+ // `declared_deletions` a second, WIDER matching rule than the gate gives it:
901
+ // `["*.md"]` (inert at the gate) would yield a null prefix meaning "matches
902
+ // everything" and mute the advisory entirely, and `["src"]` would mute all of
903
+ // `src/`. One field with two matching rules is a trap. Subtracting keeps the
904
+ // field exact-match-only on every surface it touches.
905
+ //
906
+ // Gating stays on `files_modified` alone for the same reason: a plan that declares
907
+ // only deletions has still declared no modification scope, so the advisory stays
908
+ // exactly as silent as it was before this change instead of warning on every
909
+ // modified path.
910
+ const declaredFiles = Array.isArray(entry.files_modified) ? entry.files_modified : [];
911
+ if (declaredFiles.length > 0) {
783
912
  const scopeDiff = execGit(['diff', '--name-only', `HEAD...${entry.branch}`], { cwd: plan.repoRoot });
784
913
  const scopeWarnings = !gitResultOk(scopeDiff)
785
914
  // A broken advisory must never become a gate: record that conformance
786
915
  // is unknown rather than blocking (or, worse, silently passing).
787
916
  ? [{ code: WAVE_CLEANUP_WARNING.SCOPE_CHECK_UNAVAILABLE, branch: entry.branch, path: null }]
788
- : planWaveScopeConformance((scopeDiff.stdout || '').split('\n'), entry.files_modified, entry.branch);
917
+ : planWaveScopeConformance((scopeDiff.stdout || '').split('\n'), declaredFiles, entry.branch)
918
+ // A path-less warning (SCOPE_CHECK_UNAVAILABLE) is never subtracted.
919
+ .filter((w) => w.path === null
920
+ || partitionDeclaredDeletions([w.path], entry.declared_deletions).length > 0);
789
921
  result.warnings.push(...scopeWarnings);
790
922
  allWarnings.push(...scopeWarnings);
791
923
  }
@@ -973,12 +1105,16 @@ function planWorktreeRecordAgent(manifestRaw, fields) {
973
1105
  // 2. Shared validation: run the candidate through the reader's normalizer.
974
1106
  // If it returns null the reader would drop this entry on read — reject now.
975
1107
  const declaredScope = parseDeclaredScopeFlag(fields.files);
1108
+ // #3003: reuse parseDeclaredScopeFlag — a blank/absent --deletions leaves the
1109
+ // entry shape untouched, mirroring the --files rule directly above.
1110
+ const declaredDeletions = parseDeclaredScopeFlag(fields.deletions);
976
1111
  const candidate = {
977
1112
  agent_id: agentId,
978
1113
  worktree_path: worktreePath,
979
1114
  branch,
980
1115
  expected_base: base,
981
1116
  ...(declaredScope.length > 0 ? { files_modified: declaredScope } : {}),
1117
+ ...(declaredDeletions.length > 0 ? { declared_deletions: declaredDeletions } : {}),
982
1118
  };
983
1119
  const entry = normalizeCleanupManifestEntry(candidate);
984
1120
  if (!entry) {
@@ -1072,6 +1208,11 @@ function planWorktreeRecordAgent(manifestRaw, fields) {
1072
1208
  if (entry.files_modified && entry.files_modified.length > 0) {
1073
1209
  recorded.files_modified = entry.files_modified;
1074
1210
  }
1211
+ // #3003: same conservative rule as --files above — a blank --deletions
1212
+ // leaves the entry shape untouched.
1213
+ if (entry.declared_deletions && entry.declared_deletions.length > 0) {
1214
+ recorded.declared_deletions = entry.declared_deletions;
1215
+ }
1075
1216
  worktrees.push(recorded);
1076
1217
  return {
1077
1218
  ok: true,
@@ -1083,7 +1224,7 @@ function planWorktreeRecordAgent(manifestRaw, fields) {
1083
1224
  /**
1084
1225
  * CLI command: append a validated per-agent entry to a wave cleanup manifest.
1085
1226
  *
1086
- * Usage: worktree record-agent --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> [--files "<space-separated paths>"]
1227
+ * Usage: worktree record-agent --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> [--files "<space-separated paths>"] [--deletions "<space-separated paths>"]
1087
1228
  *
1088
1229
  * Fails loudly (non-zero exit + recovery hint on stderr) when a field is
1089
1230
  * missing/garbled or the manifest is absent/malformed, rather than appending an
@@ -1092,13 +1233,15 @@ function planWorktreeRecordAgent(manifestRaw, fields) {
1092
1233
  function cmdWorktreeRecordAgent(cwd, args = [], deps = {}) {
1093
1234
  const flag = (name) => {
1094
1235
  const i = args.indexOf(name);
1095
- return i >= 0 && i + 1 < args.length ? args[i + 1] : '';
1236
+ if (i < 0 || i + 1 >= args.length)
1237
+ return '';
1238
+ return args[i + 1];
1096
1239
  };
1097
1240
  const write = deps.write || ((s) => process.stdout.write(s));
1098
1241
  const writeErr = deps.writeErr || ((s) => process.stderr.write(s));
1099
1242
  const manifestPath = flag('--manifest');
1100
1243
  if (!manifestPath) {
1101
- writeErr('Usage: worktree record-agent --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> [--files "<space-separated paths>"]\n');
1244
+ writeErr('Usage: worktree record-agent --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> [--files "<space-separated paths>"] [--deletions "<space-separated paths>"]\n');
1102
1245
  process.exitCode = 2;
1103
1246
  return { ok: false, reason: 'usage', entry: null };
1104
1247
  }
@@ -1121,6 +1264,7 @@ function cmdWorktreeRecordAgent(cwd, args = [], deps = {}) {
1121
1264
  branch: flag('--branch'),
1122
1265
  base: flag('--base'),
1123
1266
  files: flag('--files'),
1267
+ deletions: flag('--deletions'),
1124
1268
  });
1125
1269
  if (!plan.ok || plan.manifest === null) {
1126
1270
  writeErr(`[gsd] worktree.record-agent: ${plan.reason} — ${plan.hint || ''}\n`);
@@ -1165,12 +1309,16 @@ function planWorktreeCreate(fields) {
1165
1309
  };
1166
1310
  }
1167
1311
  const declaredScope = parseDeclaredScopeFlag(fields.files);
1312
+ // #3003: reuse parseDeclaredScopeFlag — a blank/absent --deletions leaves the
1313
+ // entry shape untouched, mirroring the --files rule directly above.
1314
+ const declaredDeletions = parseDeclaredScopeFlag(fields.deletions);
1168
1315
  const candidate = {
1169
1316
  agent_id: agentId,
1170
1317
  worktree_path: worktreePath,
1171
1318
  branch,
1172
1319
  expected_base: base,
1173
1320
  ...(declaredScope.length > 0 ? { files_modified: declaredScope } : {}),
1321
+ ...(declaredDeletions.length > 0 ? { declared_deletions: declaredDeletions } : {}),
1174
1322
  };
1175
1323
  const entry = normalizeCleanupManifestEntry(candidate);
1176
1324
  if (!entry) {
@@ -1298,7 +1446,7 @@ function executeWorktreeCreatePlan(plan, repoRoot, deps = {}) {
1298
1446
  * validated manifest entry so the worktree is immediately manageable by
1299
1447
  * `worktree cleanup-wave` / `worktree reap-orphans`.
1300
1448
  *
1301
- * Usage: worktree create --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> --root <dir> [--files "<space-separated paths>"]
1449
+ * Usage: worktree create --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> --root <dir> [--files "<space-separated paths>"] [--deletions "<space-separated paths>"]
1302
1450
  *
1303
1451
  * #2584 FIX 1 — ORDERING CONTRACT: every manifest read/parse/shape-validate/
1304
1452
  * plan step runs BEFORE the git side effect (step 5). The ONLY manifest
@@ -1312,13 +1460,15 @@ function executeWorktreeCreatePlan(plan, repoRoot, deps = {}) {
1312
1460
  function cmdWorktreeCreate(cwd, args = [], deps = {}) {
1313
1461
  const flag = (name) => {
1314
1462
  const i = args.indexOf(name);
1315
- return i >= 0 && i + 1 < args.length ? args[i + 1] : '';
1463
+ if (i < 0 || i + 1 >= args.length)
1464
+ return '';
1465
+ return args[i + 1];
1316
1466
  };
1317
1467
  const write = deps.write || ((s) => process.stdout.write(s));
1318
1468
  const writeErr = deps.writeErr || ((s) => process.stderr.write(s));
1319
1469
  const manifestPath = flag('--manifest');
1320
1470
  if (!manifestPath) {
1321
- writeErr('Usage: worktree create --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> --root <dir> [--files "<space-separated paths>"]\n');
1471
+ writeErr('Usage: worktree create --manifest <path> --agent-id <id> --path <worktree> --branch <branch> --base <sha> --root <dir> [--files "<space-separated paths>"] [--deletions "<space-separated paths>"]\n');
1322
1472
  process.exitCode = 2;
1323
1473
  return { ok: false, reason: 'usage' };
1324
1474
  }
@@ -1384,6 +1534,7 @@ function cmdWorktreeCreate(cwd, args = [], deps = {}) {
1384
1534
  branch: flag('--branch'),
1385
1535
  base: flag('--base'),
1386
1536
  files: flag('--files'),
1537
+ deletions: flag('--deletions'),
1387
1538
  });
1388
1539
  if (!plan.ok || !plan.entry) {
1389
1540
  writeErr(`[gsd] worktree.create: ${plan.reason} — ${plan.hint || ''}\n`);
@@ -1455,6 +1606,11 @@ function cmdWorktreeCreate(cwd, args = [], deps = {}) {
1455
1606
  if (Array.isArray(plan.entry.files_modified) && plan.entry.files_modified.length > 0) {
1456
1607
  recorded.files_modified = plan.entry.files_modified;
1457
1608
  }
1609
+ // #3003: same conservative rule as --files above — a blank --deletions
1610
+ // leaves the entry shape untouched.
1611
+ if (Array.isArray(plan.entry.declared_deletions) && plan.entry.declared_deletions.length > 0) {
1612
+ recorded.declared_deletions = plan.entry.declared_deletions;
1613
+ }
1458
1614
  const dedupeKey = `${recorded.worktree_path}\0${recorded.branch}`;
1459
1615
  const alreadyPresent = worktrees.some((existing) => {
1460
1616
  const normalized = normalizeCleanupManifestEntry(existing);
@@ -1,5 +1,5 @@
1
1
  {
2
- "_comment": "Canonical CONFIG_DEFAULTS for the Configuration Module. Nested shape is canonical. CJS flat projection (branching_strategy, sub_repos, etc.) is handled by consumers at the boundary. Security keys (security_enforcement, security_asvs_level, security_block_on) and post_planning_gaps live in workflow.* as their canonical location. resolve_model_ids, context_window, phase_naming are CJS-originated top-level keys included here. brave_search, firecrawl, exa_search default to false in the manifest; at runtime buildNewProjectConfig detects API keys. The plan_checker (CJS flat) → workflow.plan_check (canonical nested) divergence is resolved: canonical name is workflow.plan_check. _auto_chain_active is a runtime-state field included for completeness.",
2
+ "_comment": "Canonical CONFIG_DEFAULTS for the Configuration Module. Nested shape is canonical. CJS flat projection (branching_strategy, sub_repos, etc.) is handled by consumers at the boundary. Security keys (security_enforcement, security_asvs_level, security_block_on) and post_planning_gaps live in workflow.* as their canonical location. resolve_model_ids, context_window, phase_naming are CJS-originated top-level keys included here. brave_search, firecrawl, exa_search default to false in the manifest; at runtime buildNewProjectConfig detects API keys. The plan_checker (CJS flat) \u2192 workflow.plan_check (canonical nested) divergence is resolved: canonical name is workflow.plan_check. _auto_chain_active is a runtime-state field included for completeness.",
3
3
  "model_profile": "balanced",
4
4
  "commit_docs": true,
5
5
  "parallelization": true,
@@ -46,11 +46,13 @@
46
46
  "context_coverage_gate": true,
47
47
  "code_review": true,
48
48
  "code_review_depth": "standard",
49
+ "code_review_depth_overrides": [],
49
50
  "code_review_command": null,
50
51
  "pattern_mapper": true,
51
52
  "plan_bounce": false,
52
53
  "plan_bounce_script": null,
53
54
  "plan_bounce_passes": 2,
55
+ "inline_plan_threshold": 2,
54
56
  "auto_prune_state": false,
55
57
  "post_planning_gaps": true,
56
58
  "security_enforcement": true,
@@ -60,6 +62,7 @@
60
62
  },
61
63
  "planning": {
62
64
  "commit_docs": true,
65
+ "pr_strict": false,
63
66
  "search_gitignored": false,
64
67
  "sub_repos": [],
65
68
  "granularity": "standard"
@@ -97,6 +100,9 @@
97
100
  "source_grounding": true,
98
101
  "source_grounding_authority": "grep"
99
102
  },
103
+ "review": {
104
+ "max_prompt_tokens": null
105
+ },
100
106
  "capabilities": {
101
107
  "strict_known_registries": null,
102
108
  "auto_update": false
@@ -29,6 +29,7 @@
29
29
  "workflow.use_worktrees",
30
30
  "workflow.worktree_skip_hooks",
31
31
  "workflow.code_review_command",
32
+ "workflow.code_review_depth_overrides",
32
33
  "workflow.plan_bounce",
33
34
  "workflow.plan_bounce_script",
34
35
  "workflow.plan_bounce_passes",
@@ -43,16 +44,19 @@
43
44
  "ship.pr_body_sections",
44
45
  "git.branching_strategy",
45
46
  "git.base_branch",
47
+ "git.protected_branches",
46
48
  "git.create_tag",
47
49
  "git.phase_branch_template",
48
50
  "git.milestone_branch_template",
49
51
  "git.quick_branch_template",
50
52
  "planning.commit_docs",
53
+ "planning.pr_strict",
51
54
  "planning.search_gitignored",
52
55
  "planning.sub_repos",
53
56
  "review.default_reviewers",
54
57
  "review.max_prompt_tokens",
55
58
  "review.max_prompt_tokens_per_reviewer",
59
+ "review.parallel_lanes",
56
60
  "workflow.cross_ai_execution",
57
61
  "workflow.cross_ai_command",
58
62
  "workflow.cross_ai_timeout",
@@ -75,6 +79,7 @@
75
79
  "statusline.show_context_tokens",
76
80
  "statusline.state_format",
77
81
  "statusline.show_git",
82
+ "statusline.show_state_freshness",
78
83
  "workflow.max_discuss_passes",
79
84
  "features.thinking_partner",
80
85
  "context",
@@ -0,0 +1,8 @@
1
+ [
2
+ { "code": 2, "name": "HOOK_DENY", "meaning": "Hook protocol deny — the harness blocks the tool call", "owner": "hook-adapter", "authorizedBy": "ADR-3889" },
3
+ { "code": 64, "name": "USAGE", "meaning": "Caller error — bad argv, unknown subcommand, missing argument", "owner": "generic", "authorizedBy": "ADR-3889" },
4
+ { "code": 66, "name": "NO_INPUT", "meaning": "Ran; zero units were in scope, and that emptiness is known to be genuine", "owner": "generic", "authorizedBy": "ADR-3889" },
5
+ { "code": 69, "name": "UNAVAILABLE", "meaning": "Could not run — prerequisite absent, input unreadable, scope unestablished", "owner": "generic", "authorizedBy": "ADR-3889" },
6
+ { "code": 70, "name": "INTERNAL", "meaning": "Self-failure — crash, timeout, killed subprocess", "owner": "generic", "authorizedBy": "ADR-3889" },
7
+ { "code": 80, "name": "DEGRADED", "meaning": "Ran to completion and is reporting a condition through its result payload rather than as a process failure", "owner": "gsd-tools", "authorizedBy": "ADR-3889 + ADR-2980" }
8
+ ]
@@ -0,0 +1,20 @@
1
+ #!/bin/sh
2
+ # GENERATED FILE — DO NOT EDIT BY HAND.
3
+ # Source of truth: gsd-core/bin/shared/exit-codes.json. Regenerate with:
4
+ # node scripts/gen-exit-code-registry.cjs --write
5
+ #
6
+ # One `export EXIT_<NAME>=<code>` per gsd-core/bin/shared/exit-codes.json
7
+ # entry (ADR-3889 §2, #3905/#3906/#3908). POSIX sh, safe under `set -u`:
8
+ # sourcing this file only ever ASSIGNS variables, never reads one, so it
9
+ # cannot trip an unset-variable check regardless of the caller's existing
10
+ # environment.
11
+ #
12
+ # Usage (from a scanner under scripts/):
13
+ # . "$(dirname "$0")/../gsd-core/bin/shared/exit-codes.sh"
14
+ # exit "$EXIT_UNAVAILABLE"
15
+ export EXIT_HOOK_DENY=2
16
+ export EXIT_USAGE=64
17
+ export EXIT_NO_INPUT=66
18
+ export EXIT_UNAVAILABLE=69
19
+ export EXIT_INTERNAL=70
20
+ export EXIT_DEGRADED=80
@@ -112,7 +112,7 @@
112
112
  "openai": {
113
113
  "opus": { "low": { "model": "gpt-5.6-terra", "reasoning_effort": "medium" }, "medium": { "model": "gpt-5.6-sol", "reasoning_effort": "high" }, "high": { "model": "gpt-5.6-sol", "reasoning_effort": "xhigh" } },
114
114
  "sonnet": { "low": { "model": "gpt-5.6-luna", "reasoning_effort": "low" }, "medium": { "model": "gpt-5.6-terra", "reasoning_effort": "medium" }, "high": { "model": "gpt-5.6-sol", "reasoning_effort": "medium" } },
115
- "haiku": { "low": { "model": "gpt-5.6-luna", "reasoning_effort": "minimal" }, "medium": { "model": "gpt-5.6-luna", "reasoning_effort": "medium" }, "high": { "model": "gpt-5.6-terra", "reasoning_effort": "medium" } }
115
+ "haiku": { "low": { "model": "gpt-5.6-luna", "reasoning_effort": "low" }, "medium": { "model": "gpt-5.6-luna", "reasoning_effort": "medium" }, "high": { "model": "gpt-5.6-terra", "reasoning_effort": "medium" } }
116
116
  },
117
117
  "google": {
118
118
  "opus": { "low": { "model": "gemini-2.5-flash-lite" }, "medium": { "model": "gemini-3-flash" }, "high": { "model": "gemini-3.1-pro-preview" } },
@@ -130,6 +130,12 @@
130
130
  "haiku": { "low": null, "medium": null, "high": null }
131
131
  }
132
132
  },
133
+ "codexModelEffort": {
134
+ "_baseline": ["low", "medium", "high", "xhigh", "max"],
135
+ "gpt-5.6-sol": ["low", "medium", "high", "xhigh", "max", "ultra"],
136
+ "gpt-5.6-terra": ["low", "medium", "high", "xhigh", "max"],
137
+ "gpt-5.6-luna": ["low", "medium", "high", "xhigh", "max"]
138
+ },
133
139
  "agents": {
134
140
  "gsd-planner": { "golden": "opus", "balanced": "opus", "budget": "sonnet", "phaseType": "planning", "routingTier": "heavy" },
135
141
  "gsd-roadmapper": { "golden": "opus", "balanced": "sonnet", "budget": "sonnet", "phaseType": "planning", "routingTier": "heavy" },
@@ -149,6 +155,7 @@
149
155
  "gsd-ui-auditor": { "golden": "sonnet", "balanced": "sonnet", "budget": "haiku", "phaseType": "verification", "routingTier": "light" },
150
156
  "gsd-doc-writer": { "golden": "opus", "balanced": "sonnet", "budget": "haiku", "phaseType": "execution", "routingTier": "standard" },
151
157
  "gsd-doc-verifier": { "golden": "sonnet", "balanced": "sonnet", "budget": "haiku", "phaseType": "verification", "routingTier": "light" },
158
+ "gsd-dom-verifier": { "golden": "sonnet", "balanced": "sonnet", "budget": "haiku", "phaseType": "verification", "routingTier": "light" },
152
159
 
153
160
  "gsd-advisor-researcher": { "golden": "opus", "balanced": "sonnet", "budget": "haiku", "phaseType": "research", "routingTier": "standard" },
154
161
  "gsd-ai-researcher": { "golden": "opus", "balanced": "sonnet", "budget": "haiku", "phaseType": "research", "routingTier": "standard" },
@@ -19,8 +19,9 @@ This doc describes what IS, not what should be. Casing inconsistencies are docum
19
19
  | gsd-research-synthesizer | Multi-research synthesis | `## SYNTHESIS COMPLETE`, `## SYNTHESIS BLOCKED` (unconsumed: blocked-research return — spawners detect failure via the #222 SUMMARY.md-on-disk check, no dispatch branch keys on the marker) | `gsd-core/workflows/new-milestone.md`, `gsd-core/workflows/new-project.md` | sentinel-match |
20
20
  | gsd-debugger | Debug investigation | `## DEBUG COMPLETE`, `## ROOT CAUSE FOUND`, `## CHECKPOINT REACHED`, `## INVESTIGATION INCONCLUSIVE`, `## TDD CHECKPOINT`, `## FIX REJECTED BY GUARDRAIL` | `agents/gsd-debug-session-manager.md`, `gsd-core/workflows/diagnose-issues.md`, `gsd-core/workflows/plan-phase.md`, `agents/gsd-executor.md` | sentinel-match |
21
21
  | gsd-debug-session-manager | Debug checkpoint loop | `## DEBUG SESSION COMPLETE`, `## CONTINUE_REQUIRED` | `gsd-core/workflows/debug.md` | sentinel-match |
22
- | gsd-roadmapper | Roadmap creation/revision | `## ROADMAP CREATED`, `## ROADMAP REVISED`, `## ROADMAP BLOCKED`, `## ROADMAP DRAFT` (unconsumed: draft-presentation format the shipped execution flow never invokes — Step 8 returns `## ROADMAP CREATED`; retained for interactive draft review) | `gsd-core/workflows/new-milestone.md`, `gsd-core/workflows/new-project.md` | sentinel-match |
22
+ | gsd-roadmapper | Roadmap creation/revision | `## ROADMAP CREATED`, `## ROADMAP REVISED`, `## ROADMAP BLOCKED` | `gsd-core/workflows/new-milestone.md`, `gsd-core/workflows/new-project.md` | sentinel-match |
23
23
  | gsd-ui-auditor | UI review | `## UI REVIEW COMPLETE` | `gsd-core/workflows/ui-review.md` | sentinel-match |
24
+ | gsd-dom-verifier | Live-DOM UAT verification | No marker (writes `{phase}-DOM-VERIFY.md` directly; the frontmatter `outcome` / `reason` scalars carry the verdict, and `could_not_look` is never conflated with `nothing_to_report`) | `{phase}-DOM-VERIFY.md` artifact, written by the `live-dom-uat` capability's `execute:wave:post` step dispatched from `gsd-core/workflows/execute-phase.md` | artifact+query |
24
25
  | gsd-ui-checker | UI validation | `## ISSUES FOUND`, `## UI-SPEC VERIFIED` | `gsd-core/workflows/plan-phase.md`, `gsd-core/workflows/quick/steps/plan-checker-loop.md`, `gsd-core/workflows/ui-phase.md`, `gsd-core/workflows/verify-work.md`, `agents/gsd-plan-checker.md` | sentinel-match |
25
26
  | gsd-ui-researcher | UI spec creation | `## UI-SPEC COMPLETE`, `## UI-SPEC BLOCKED` | `gsd-core/workflows/ui-phase.md` | sentinel-match |
26
27
  | gsd-verifier | Post-execution verification | `## Verification Complete` (unconsumed: Marker Rule 2 recorded decision — intentional title-case marker; completion is detected via the artifact route, nothing matches the marker) | `*-VERIFICATION.md` artifact + `gsd_run query verification.status` in `gsd-core/workflows/verify-work.md` | artifact+query |
@@ -56,7 +57,7 @@ This doc describes what IS, not what should be. Casing inconsistencies are docum
56
57
  - `structured-return` -- the agent has no way to write files (no `Write` tool) or simply doesn't; it returns parseable sections, a table, or JSON inline, and the caller reads that return text directly
57
58
  5. Markers must appear as H2 headings (`## `) at the start of a line in the agent's final output
58
59
  6. The `Consumed by` / `Kind` columns are machine-enforced by `check:contract-drift` (`scripts/check-contract-drift.cjs`), which cross-checks this table against what each `agents/*.md` file actually emits in-fence and what every `gsd-core/workflows/**`, `commands/**`, and `agents/**` file actually consumes. Update this table whenever an agent's return contract changes -- a stale row is a violation the check will report, not something to leave for later.
59
- 7. A marker entry annotated `(unconsumed: <reason>)` is emitted deliberately but matched by no workflow, command, or agent — e.g. `## ROADMAP DRAFT`, a presentation format a human approves interactively. The check still verifies the marker is declared **and** emitted, and still counts it for case-collision purposes; only the consumer requirement is waived. Use it for display formats, never to silence a real orphan.
60
+ 7. A marker entry annotated `(unconsumed: <reason>)` is emitted deliberately but matched by no workflow, command, or agent — a display/presentation format no orchestrator branch consumes (the `## ROADMAP DRAFT` header was the canonical case until #3797 folded its content into `## ROADMAP CREATED`). The check still verifies the marker is declared **and** emitted, and still counts it for case-collision purposes; only the consumer requirement is waived. Use it for display formats, never to silence a real orphan.
60
61
 
61
62
  ## Key Handoff Contracts
62
63
 
@@ -53,7 +53,26 @@ compound modifiers ("Resolver-only API"), and first-party-qualified services
53
53
  `capabilities/ai-integration/fragments/api-coverage-plan-pre.md`.
54
54
  2. **Seal time (`verify:pre`).** The blocking `api-coverage.verify-pre` gate
55
55
  runs `check api-coverage.verify-pre <phase-dir>` and blocks unless a valid
56
- matrix exists (or no integration is detected).
56
+ matrix exists (or no integration is detected in a scope it could actually
57
+ read).
58
+
59
+ ### Seal-time outcomes
60
+
61
+ | Condition | Verdict |
62
+ |---|---|
63
+ | Valid `COVERAGE.md` matrix | pass |
64
+ | `No external API integration: <reason>` declaration | pass (the reasoned human overrule) |
65
+ | Malformed / partial matrix | **block** |
66
+ | No matrix, integration signal found | **block** |
67
+ | No matrix, no signal, scope was read | pass |
68
+ | No matrix, **scope could not be established** | **block** — `scope_unavailable: true` (#3909) |
69
+ | Phase token unresolvable | **block** |
70
+ | Plan file exists but unreadable | **block** |
71
+ | No `.planning/phases` tree at all | pass — not a GSD project layout |
72
+
73
+ The last four are the fail-closed arms: the gate refuses to certify "no
74
+ external-API integration" from scope it did not read. A phase with real plans
75
+ and no API vocabulary is the fifth row, and is unaffected.
57
76
 
58
77
  ## The coverage matrix format
59
78
 
@@ -126,7 +145,10 @@ zero — the matrix is the durable subtraction record.
126
145
 
127
146
  ```bash
128
147
  echo "$PHASE_SCOPE" | node gsd-core/bin/lib/api-coverage.cjs --json
129
- # exit 0 = integration detected, 1 = none, 2 = startup error
148
+ # exit 0 = integration detected, 1 = none (real input, examined, no signal)
149
+ # NO_INPUT = stdin was empty or whitespace-only (registry code — ADR-3889 Phase 3, #3907)
150
+ # UNAVAILABLE = stdin read failed (registry code)
151
+ # NO_INPUT/UNAVAILABLE emit {"skipped":true,"reason":"no_input"|"stdin_error"} — no `detected` key
130
152
  ```
131
153
 
132
154
  The detector is a pure function (`detectApiIntegration` → `{ detected, signals,
@@ -5,7 +5,7 @@ Smart discuss is the autonomous-optimized variant of `gsd-discuss-phase`. It pro
5
5
  **Inputs:** `PHASE_NUM` from execute_phase. Run init to get phase paths:
6
6
 
7
7
  ```bash
8
- PHASE_STATE=$(gsd-tools query init.phase-op ${PHASE_NUM})
8
+ PHASE_STATE=$(gsd_run query init.phase-op ${PHASE_NUM})
9
9
  ```
10
10
 
11
11
  Parse from JSON: `phase_dir`, `phase_slug`, `padded_phase`, `phase_name`.
@@ -94,7 +94,7 @@ Read the 3-5 most relevant files to understand existing patterns.
94
94
  **Get phase details:**
95
95
 
96
96
  ```bash
97
- DETAIL=$(gsd-tools query roadmap.get-phase ${PHASE_NUM})
97
+ DETAIL=$(gsd_run query roadmap.get-phase ${PHASE_NUM})
98
98
  ```
99
99
 
100
100
  Extract `goal`, `requirements`, `success_criteria` from the JSON response.
@@ -266,7 +266,7 @@ Write the file.
266
266
  **Commit:**
267
267
 
268
268
  ```bash
269
- gsd-tools query commit "docs(${PADDED_PHASE}): smart discuss context" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
269
+ gsd_run query commit "docs(${PADDED_PHASE}): smart discuss context" --files "${phase_dir}/${padded_phase}-CONTEXT.md"
270
270
  ```
271
271
 
272
272
  Display confirmation: