@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
@@ -37,7 +37,7 @@ const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
37
37
  const pattern_cjs_1 = require("./pattern.cjs");
38
38
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- agent-install-check.cjs is an export= CommonJS module
39
39
  const agentInstallCheck = require("./agent-install-check.cjs");
40
- const { checkAgentsInstalled, checkCodexModelPosture } = agentInstallCheck;
40
+ const { checkAgentsInstalled, checkCodexModelPosture, checkCodexSandboxPosture } = agentInstallCheck;
41
41
  // eslint-disable-next-line @typescript-eslint/no-require-imports
42
42
  const ioMod = require("./io.cjs");
43
43
  const { output, error } = ioMod;
@@ -56,7 +56,7 @@ const { SEVERITY: HEALTH_SEVERITY, REMEDY_ACTION, REMEDY_RISK, evaluateRules, ev
56
56
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- planning-snapshot.cjs is an export= CommonJS module
57
57
  const planningSnapshotMod = require("./planning-snapshot.cjs");
58
58
  const { buildPlanningSnapshot } = planningSnapshotMod;
59
- const { planningDir, planningRoot } = planningWorkspace;
59
+ const { planningDir } = planningWorkspace;
60
60
  const { extractFrontmatter, parseMustHavesBlock } = frontmatterMod;
61
61
  const { readStateHeadFreshness } = stateMod;
62
62
  /**
@@ -262,15 +262,78 @@ function cmdVerifySummary(cwd, summaryPath, checkFileCount, raw) {
262
262
  * verify gate fails on the comment echo rather than a real regression. Conservative:
263
263
  * errors only on a confidently-extracted QUOTED literal; ambiguous (bareword) → warning.
264
264
  */
265
+ /**
266
+ * Decode entity-escaped ampersands (& → &) — #3611. Planners emit
267
+ * <automated> bodies with `&amp;&amp;` as the chain operator (66 occurrences
268
+ * vs 0 literal in the reporting repo), and the executing agent reads the
269
+ * decoded (rendered) form. Every downstream scan — segment split,
270
+ * zero-comparison, literal harvest, echo matching — must operate on the same
271
+ * decoded text or a negative clause (`= 0`) poisons the literals of a
272
+ * POSITIVE clause (-ge 3) joined to it. Shared by both plan-discipline
273
+ * scanners so the two gates cannot drift apart again.
274
+ */
275
+ function decodeEntityAmps(s) {
276
+ return s.replace(/&amp;/g, '&');
277
+ }
278
+ /**
279
+ * Split one shell line into &&/|| segments, QUOTE-AWARE (#3611 adversarial
280
+ * review): an operator inside a quoted literal (`grep -c 'a&&b'`) is part of
281
+ * the pattern, not a chain operator — a quote-blind split destroys the
282
+ * literal and silently disarms the gate for exactly the plans that spell
283
+ * patterns with ampersands. Backslash escapes count inside double quotes
284
+ * (POSIX single quotes have none, and over-staying a single-quoted span can
285
+ * only miss a split, never invent one).
286
+ */
287
+ function splitShellSegments(line) {
288
+ const segments = [];
289
+ let current = '';
290
+ let quote = null;
291
+ for (let i = 0; i < line.length; i++) {
292
+ const ch = line[i];
293
+ if (quote === "'") {
294
+ if (ch === "'")
295
+ quote = null;
296
+ current += ch;
297
+ continue;
298
+ }
299
+ if (quote === '"') {
300
+ if (ch === '\\') {
301
+ current += ch + (line[i + 1] ?? '');
302
+ i++;
303
+ continue;
304
+ }
305
+ if (ch === '"')
306
+ quote = null;
307
+ current += ch;
308
+ continue;
309
+ }
310
+ if (ch === "'" || ch === '"') {
311
+ quote = ch;
312
+ current += ch;
313
+ continue;
314
+ }
315
+ if ((ch === '&' && line[i + 1] === '&') || (ch === '|' && line[i + 1] === '|')) {
316
+ segments.push(current.trim());
317
+ current = '';
318
+ i++;
319
+ continue;
320
+ }
321
+ current += ch;
322
+ }
323
+ segments.push(current.trim());
324
+ return segments.filter((s) => s !== '');
325
+ }
265
326
  function scanNegativeGrepCommentEcho(content) {
266
327
  const errors = [];
267
328
  const warnings = [];
268
329
  // Normalize newlines; join backslash line-continuations so a verify command wrapped
269
330
  // across lines (grep ... \ <newline> == 0) is still seen as one segment.
270
- const text = (content || '')
331
+ // #3611: decode entity-escaped ampersands (see decodeEntityAmps) so every
332
+ // downstream scan reads the same decoded text the executing agent reads.
333
+ const text = decodeEntityAmps((content || '')
271
334
  .replace(/\r\n/g, '\n')
272
335
  .replace(/\r/g, '\n')
273
- .replace(/\\\n/g, ' ');
336
+ .replace(/\\\n/g, ' '));
274
337
  // 1. Allowlisted literals: <!-- planner-discipline-allow: LIT -->
275
338
  const allow = new Set();
276
339
  const allowRe = /<!--\s*planner-discipline-allow:\s*(.+?)\s*-->/g;
@@ -313,7 +376,7 @@ function scanNegativeGrepCommentEcho(content) {
313
376
  // poisoning a negative gate (`== 0`) sharing the same physical line.
314
377
  const seenErr = new Set();
315
378
  const seenWarn = new Set();
316
- const segments = text.split('\n').flatMap((line) => line.split(/\s*(?:&&|\|\|)\s*/));
379
+ const segments = text.split('\n').flatMap(splitShellSegments);
317
380
  for (const seg of segments) {
318
381
  if (!/grep(?:\s+-{1,2}[A-Za-z])/.test(seg) || !zeroCmp(seg))
319
382
  continue;
@@ -362,10 +425,13 @@ function scanNegativeGrepCommentEcho(content) {
362
425
  function scanFileWideNegativeGateConflict(content) {
363
426
  const warnings = [];
364
427
  // Normalize newlines; join backslash line-continuations (same as #429).
365
- const text = (content || '')
428
+ // #3611: the SAME entity decode as the #429 scanner — the two gates share
429
+ // the caller and the input; a decode on one side only let an entity-escaped
430
+ // chain poison this detector's harvest exactly the same way.
431
+ const text = decodeEntityAmps((content || '')
366
432
  .replace(/\r\n/g, '\n')
367
433
  .replace(/\r/g, '\n')
368
- .replace(/\\\n/g, ' ');
434
+ .replace(/\\\n/g, ' '));
369
435
  // Allowlisted patterns: <!-- planner-region-allow: PAT -->
370
436
  const allow = new Set();
371
437
  const allowRe = /<!--\s*planner-region-allow:\s*(.+?)\s*-->/g;
@@ -499,8 +565,8 @@ function scanFileWideNegativeGateConflict(content) {
499
565
  // For each task A, scan gate text for file-wide negative grep bans.
500
566
  for (let ai = 0; ai < tasks.length; ai++) {
501
567
  const taskA = tasks[ai];
502
- // Split gate text into shell segments (split on && / || within lines).
503
- const segments = taskA.gateText.split('\n').flatMap(line => line.split(/\s*(?:&&|\|\|)\s*/));
568
+ // Split gate text into shell segments (quote-aware, shared with #429 — #3611).
569
+ const segments = taskA.gateText.split('\n').flatMap(splitShellSegments);
504
570
  for (const seg of segments) {
505
571
  if (!/grep/.test(seg))
506
572
  continue;
@@ -1242,30 +1308,6 @@ function cmdVerifyKeyLinks(cwd, planFilePath, raw) {
1242
1308
  links: results,
1243
1309
  }, raw, allVerified ? 'valid' : 'invalid');
1244
1310
  }
1245
- function listMilestoneArchiveDirs(planBase) {
1246
- const milestonesDir = node_path_1.default.join(planBase, 'milestones');
1247
- try {
1248
- return node_fs_1.default
1249
- .readdirSync(milestonesDir, { withFileTypes: true })
1250
- .filter((e) => e.isDirectory() && validate_cjs_1.MILESTONE_ARCHIVE_DIR_RE.test(e.name))
1251
- .map((e) => node_path_1.default.join(milestonesDir, e.name))
1252
- .sort((a, b) => node_path_1.default.basename(a).localeCompare(node_path_1.default.basename(b), undefined, { numeric: true }));
1253
- }
1254
- catch (err) {
1255
- // #1883: distinguish genuine absence from a permission/I-O failure. ENOENT
1256
- // (no milestones/ dir yet) keeps the long-standing [] contract callers of
1257
- // this function depend on for "no archives"; every other error (EACCES,
1258
- // EIO, …) must propagate — otherwise an unreadable milestones/ dir is
1259
- // silently reported as "no archives" and archived-phase resolution
1260
- // misbehaves. As of Phase 12 (#3310), `cmdValidateConsistency`'s own
1261
- // caller of this function (`collectPhaseRoots` -> `getActiveMilestoneArchiveDir`)
1262
- // was migrated onto `buildPlanningSnapshot` and deleted; this function is
1263
- // retained solely for its `_listMilestoneArchiveDirs` test seam below.
1264
- if (err.code === 'ENOENT')
1265
- return [];
1266
- throw err;
1267
- }
1268
- }
1269
1311
  /**
1270
1312
  * Wrapper-level fix-text table for `cmdValidateHealth`'s migrated
1271
1313
  * `HealthDiagnostic` -> `IssueEntry` mapping (Phase 11, #3309). Rules cannot
@@ -1397,8 +1439,10 @@ function cmdValidateHealth(cwd, options, raw) {
1397
1439
  }, raw);
1398
1440
  return;
1399
1441
  }
1400
- // rootBase always resolves to .planning/ (shared root — PROJECT.md, config.json live here)
1401
- const rootBase = planningRoot(cwd);
1442
+ // rootBase resolves to the PROJECT-scoped planning root (`.planning[/<project>]`
1443
+ // — PROJECT.md, config.json live here; #3749). Workstream-free by construction:
1444
+ // planningDir(cwd, null) honors GSD_PROJECT and suppresses GSD_WORKSTREAM.
1445
+ const rootBase = planningDir(cwd, null);
1402
1446
  const _slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
1403
1447
  const slash = (name) => (0, runtime_slash_cjs_1.formatGsdSlash)(name, _slashRuntime);
1404
1448
  // Second (and last) pre-check that stays OUTSIDE the rule table entirely
@@ -1523,6 +1567,21 @@ function cmdValidateAgents(cwd, raw) {
1523
1567
  // model or an orphaned reasoning-effort pin in a Codex agent .toml), not just
1524
1568
  // presence. checkAgentsInstalled above is untouched.
1525
1569
  const codexPosture = checkCodexModelPosture(runtime, cwd);
1570
+ // #3897 rung 3 (ADR-3473 §8.3 criterion 3) — sibling posture check, additive
1571
+ // alongside codex_posture (never nested inside it, never replacing it): a
1572
+ // TOML's `sandbox_mode` disagreeing with its role's derived expectation is a
1573
+ // different defect class than an Anthropic-flavored model. Same short-circuits,
1574
+ // same read-only posture as checkCodexModelPosture above; checkAgentsInstalled
1575
+ // and codex_posture are both left untouched.
1576
+ //
1577
+ // Failure semantics: matches the checkCodexModelPosture precedent exactly.
1578
+ // Neither posture check makes `validate agents` exit non-zero on its own —
1579
+ // both are report-only fields inspected by the caller (or a human) via the
1580
+ // JSON payload. A `validate` verb whose two posture checks disagreed on
1581
+ // fatality would be its own defect (#3897 dispatch note); this keeps them
1582
+ // consistent rather than inventing new exit-code behavior for only one of
1583
+ // the two siblings.
1584
+ const sandboxPosture = checkCodexSandboxPosture(runtime, cwd);
1526
1585
  output({
1527
1586
  agents_dir: agentStatus.agents_dir,
1528
1587
  agents_found: agentStatus.agents_installed,
@@ -1531,6 +1590,7 @@ function cmdValidateAgents(cwd, raw) {
1531
1590
  incomplete: agentStatus.incomplete_agents,
1532
1591
  expected,
1533
1592
  codex_posture: codexPosture,
1593
+ sandbox_posture: sandboxPosture,
1534
1594
  }, raw);
1535
1595
  }
1536
1596
  function cmdVerifySchemaDrift(cwd, phaseArg, skipFlag, raw) {
@@ -1765,9 +1825,4 @@ module.exports = {
1765
1825
  cmdVerifySchemaDrift,
1766
1826
  cmdVerifyCodebaseDrift,
1767
1827
  STATE_HEAD_ADVISORY_COMMITS,
1768
- // Test seam (#1883): listMilestoneArchiveDirs is private and exercised through
1769
- // the validate command, which runs in a subprocess — an fs monkeypatch in the
1770
- // test process cannot reach it. Exposed under a leading underscore so the
1771
- // permission-error path can be unit-tested directly (no chmod 0o000).
1772
- _listMilestoneArchiveDirs: listMilestoneArchiveDirs,
1773
1828
  };
@@ -6,6 +6,24 @@
6
6
  * byte-for-behaviour from the prior hand-written .cjs; only types are added.
7
7
  *
8
8
  * Used by active-workstream-store.cjs, planning-workspace.cjs, workstream.cjs.
9
+ *
10
+ * #3883 (ADR-3473 §8.3): toWorkstreamSlug delegates to core-utils.cjs's
11
+ * generateSlugInternal (the canonical slug formula), passing `maxLen: null`
12
+ * to preserve this site's pre-migration untruncated contract — the 60-char
13
+ * default collided distinct >60-char workstream names onto the same slug
14
+ * (verified: `"a".repeat(60)+"alpha"` and `"a".repeat(60)+"beta"` truncated
15
+ * identically). core-utils.cjs already
16
+ * requires (transitively, at module-init time) THIS module:
17
+ * core-utils.cjs -> planning-workspace.cjs -> active-workstream-store.cjs ->
18
+ * workstream-name-policy.cjs. A top-level require of core-utils.cjs here
19
+ * would therefore close that cycle and — per this codebase's compiled-.cjs
20
+ * convention of a single `module.exports = {...}` reassignment at the bottom
21
+ * of core-utils.cjs — capture a stale, still-empty exports object forever
22
+ * (verified live: "generateSlugInternal is not a function" whichever module
23
+ * loads first). The require is deferred (lazy, inside toWorkstreamSlug's
24
+ * body) instead, mirroring the same cycle-break already used by
25
+ * core-utils.cts's own getPhaseFileStats/plan-scan.cjs seam and by
26
+ * phase-id.cts's toDir/getPhaseDirFromPhaseId.
9
27
  */
10
28
  Object.defineProperty(exports, "__esModule", { value: true });
11
29
  exports.INVALID_ACTIVE_WORKSTREAM_NAME_MESSAGE = void 0;
@@ -68,10 +86,13 @@ function validateWorkstreamName(name) {
68
86
  * Lowercases, collapses non-alphanumeric runs to hyphens, strips leading/trailing hyphens.
69
87
  */
70
88
  function toWorkstreamSlug(name) {
71
- return String(name ?? '')
72
- .toLowerCase()
73
- .replace(/[^a-z0-9]+/g, '-')
74
- .replace(/^-+|-+$/g, '');
89
+ // #3883 (ADR-3473 §8.3): delegate to the canonical slug formula
90
+ // (generateSlugInternal, core-utils.cts) rather than re-implementing it —
91
+ // this call site previously diverged from it (no transliteration, no
92
+ // 60-char truncation). Lazy require to break the core-utils.cjs cycle
93
+ // (see the module dependency doc comment above).
94
+ // eslint-disable-next-line @typescript-eslint/no-require-imports, @typescript-eslint/no-unsafe-member-access, @typescript-eslint/no-unsafe-call
95
+ return require('./core-utils.cjs').generateSlugInternal(String(name ?? ''), null) ?? '';
75
96
  }
76
97
  /**
77
98
  * Returns true when `name` is a valid active workstream name:
@@ -94,10 +94,13 @@ function parseJsonc(text) {
94
94
  }
95
95
  // ─── Message constants (verbatim — downstream docs/tests depend on these) ─────
96
96
  function buildMsgDiverged(headSha, forkRef, forkSha) {
97
- return `⚠ Worktree base mismatch: HEAD (${shortSha(headSha)}) differs from ${forkRef} (${shortSha(forkSha)}). Running this phase sequentially on the main working tree. To keep parallel worktrees, set worktree.baseRef:"head" in .claude/settings.local.json (or run: gsd-tools worktree set-baseref). See #683.`;
97
+ return `⚠ Worktree base mismatch: HEAD (${shortSha(headSha)}) differs from ${forkRef} (${shortSha(forkSha)}). Running this phase sequentially on the main working tree. Parallel worktrees return once HEAD is merged/pushed so ${forkRef} matches it. (worktree.baseRef:"head" applies only where GSD itself creates the worktree — the runtime harness does not read it; #48, #3659.)`;
98
98
  }
99
- const MSG_UNKNOWN = `⚠ Cannot determine the worktree fork base (origin/HEAD unresolved). Running this phase sequentially on the main working tree to avoid a base mismatch. To keep parallel worktrees, set worktree.baseRef:"head" in .claude/settings.local.json (or run: gsd-tools worktree set-baseref). See #683.`;
100
- const MSG_HEAD_UNRESOLVABLE = `⚠ Cannot determine the worktree base (git rev-parse HEAD did not return a definitive answer). Running this phase sequentially on the main working tree to avoid an unverified base mismatch. Note: worktree.baseRef:"head" would silence this check without verifying the base — it skips the comparison rather than resolving it. Retry; if it persists, check for a stalled filesystem mount or a stale git index lock (.git/index.lock). See #683, #3050.`;
99
+ const MSG_UNKNOWN = `⚠ Cannot determine the worktree fork base (origin/HEAD unresolved). Running this phase sequentially on the main working tree to avoid a base mismatch. Parallel worktrees return once origin/HEAD resolves and matches HEAD. See #683, #3659.`;
100
+ function buildMsgBaserefHeadIgnored(headSha, forkRef, forkSha) {
101
+ return `⚠ Worktree base mismatch: worktree.baseRef:"head" is set, but the runtime harness does not honor it for isolated dispatch — the fork base stays ${forkRef} (${shortSha(forkSha)}) while HEAD is ${shortSha(headSha)} (#48; upstream claude-code#44965). Running this phase sequentially on the main working tree. Parallel worktrees return once HEAD is merged/pushed so ${forkRef} matches it, or on runtimes where GSD itself manages worktree creation. See #3659.`;
102
+ }
103
+ const MSG_HEAD_UNRESOLVABLE = `⚠ Cannot determine the worktree base (git rev-parse HEAD did not return a definitive answer). Running this phase sequentially on the main working tree to avoid an unverified base mismatch. Note: worktree.baseRef:"head" silences this check only where GSD itself creates the worktree (orchestrator-managed runtimes) — in harness mode it never applied (#48, #3659). Retry; if it persists, check for a stalled filesystem mount or a stale git index lock (.git/index.lock). See #683, #3050.`;
101
104
  /**
102
105
  * Returns true when an execGit result indicates the subprocess was killed by
103
106
  * a timeout. A timeout means the command genuinely could not complete — it
@@ -228,7 +231,20 @@ function resolveEffectiveBaseRef(claudeDir, deps, userClaudeDir) {
228
231
  * deps.userClaudeDir overrides the user/global config directory resolution
229
232
  * (default: getGlobalConfigDir('claude'), which honours CLAUDE_CONFIG_DIR).
230
233
  */
231
- function cmdWorktreeBaseCheck(cwd, _args, deps) {
234
+ function cmdWorktreeBaseCheck(cwd, args, deps) {
235
+ // --mode threads the dispatch isolation mode through to the evaluation
236
+ // (#3659): 'harness-worktree' (default) or 'orchestrator-worktree'. Invalid
237
+ // or missing values after --mode fail closed — a silently defaulted typo
238
+ // would re-open the hole the flag exists to close.
239
+ let isolationMode = 'harness-worktree';
240
+ const modeIdx = args.indexOf('--mode');
241
+ if (modeIdx !== -1) {
242
+ const value = args[modeIdx + 1];
243
+ if (value !== 'harness-worktree' && value !== 'orchestrator-worktree') {
244
+ throw new Error(`worktree base-check: --mode must be harness-worktree or orchestrator-worktree, got ${JSON.stringify(value ?? null)}`);
245
+ }
246
+ isolationMode = value;
247
+ }
232
248
  const claudeDir = node_path_1.default.join(cwd, '.claude');
233
249
  const userClaudeDir = Object.prototype.hasOwnProperty.call(deps ?? {}, 'userClaudeDir')
234
250
  ? deps.userClaudeDir
@@ -238,8 +254,22 @@ function cmdWorktreeBaseCheck(cwd, _args, deps) {
238
254
  cwd,
239
255
  effectiveBaseRef,
240
256
  execGit: deps?.execGit,
257
+ isolationMode,
258
+ });
259
+ // Default emit goes through fs.writeSync(1, …), NOT process.stdout.write:
260
+ // the CLI's --pick capture intercepts writeSync, and command substitution
261
+ // is a pipe — via process.stdout.write a `$(gsd-tools … --pick x)` capture
262
+ // received the full JSON instead of the picked field, so the workflow
263
+ // auto-degrade guards never matched (#3659 review). Short-count loop per
264
+ // io.cjs writeAllSync's rationale (a non-blocking pipe can accept partial
265
+ // writes).
266
+ const write = deps?.write ?? ((s) => {
267
+ const buf = Buffer.from(s, 'utf8');
268
+ let offset = 0;
269
+ while (offset < buf.length) {
270
+ offset += node_fs_1.default.writeSync(1, buf, offset, buf.length - offset);
271
+ }
241
272
  });
242
- const write = deps?.write ?? ((s) => process.stdout.write(s));
243
273
  write(JSON.stringify(result, null, 2) + '\n');
244
274
  return result;
245
275
  }
@@ -295,7 +325,20 @@ function cmdWorktreeSetBaseRef(cwd, _args, deps) {
295
325
  baseRef: 'head',
296
326
  file,
297
327
  };
298
- const write = deps?.write ?? ((s) => process.stdout.write(s));
328
+ // Default emit goes through fs.writeSync(1, …), NOT process.stdout.write:
329
+ // the CLI's --pick capture intercepts writeSync, and command substitution
330
+ // is a pipe — via process.stdout.write a `$(gsd-tools … --pick x)` capture
331
+ // received the full JSON instead of the picked field, so the workflow
332
+ // auto-degrade guards never matched (#3659 review). Short-count loop per
333
+ // io.cjs writeAllSync's rationale (a non-blocking pipe can accept partial
334
+ // writes).
335
+ const write = deps?.write ?? ((s) => {
336
+ const buf = Buffer.from(s, 'utf8');
337
+ let offset = 0;
338
+ while (offset < buf.length) {
339
+ offset += node_fs_1.default.writeSync(1, buf, offset, buf.length - offset);
340
+ }
341
+ });
299
342
  write(JSON.stringify(output, null, 2) + '\n');
300
343
  return output;
301
344
  }
@@ -311,12 +354,19 @@ function evaluateWorktreeBaseDegrade(deps) {
311
354
  const execGit = deps?.execGit ?? shell_command_projection_cjs_1.execGit;
312
355
  const cwd = deps?.cwd;
313
356
  const cwdOpts = cwd ? { cwd } : {};
314
- // a. If baseRef is explicitly 'head' the harness forks from HEAD — no mismatch possible.
315
- // Claude Code's worktree.baseRef accepts only "fresh" (= origin/HEAD, the default) or "head".
316
- // Therefore special-casing "head" here and otherwise comparing HEAD against origin/HEAD is
317
- // complete: any non-"head" value (including "fresh" and absent/null) has fresh/origin-HEAD
318
- // semantics and must be evaluated against origin/HEAD. (Reference: Claude Code worktrees docs, #683.)
319
- if (deps?.effectiveBaseRef === 'head') {
357
+ // a. baseRef 'head' suppresses ONLY where GSD controls the fork start-point
358
+ // (#3659). The former unconditional suppress trusted the harness to honor the
359
+ // setting; #48 verified 5/5 that the Agent-isolation dispatch path never
360
+ // routes through project settings (upstream claude-code#44965), so in
361
+ // harness mode the fork base is always origin/HEAD and 'head' must fall
362
+ // through to the same comparison the fresh path runs. In
363
+ // orchestrator-worktree mode GSD itself runs `git worktree add <path>
364
+ // <start-point>` with the orchestrator HEAD — 'head' is honored by
365
+ // construction there and the suppress is correct. Any non-"head" value
366
+ // (including "fresh" and absent/null) has fresh/origin-HEAD semantics and is
367
+ // evaluated against origin/HEAD as before. (Reference: #683, #48, #3659.)
368
+ const headIgnoredByHarness = deps?.effectiveBaseRef === 'head';
369
+ if (headIgnoredByHarness && (deps?.isolationMode ?? 'harness-worktree') === 'orchestrator-worktree') {
320
370
  return { shouldDegrade: false, reason: 'baseref-head', message: null, headSha: null, forkRef: null, forkSha: null, headAbsenceVerified: null };
321
371
  }
322
372
  // b. Resolve HEAD sha.
@@ -389,6 +439,10 @@ function evaluateWorktreeBaseDegrade(deps) {
389
439
  if (forkSha === headSha) {
390
440
  return { shouldDegrade: false, reason: 'head-matches-fork', message: null, headSha, forkRef, forkSha, headAbsenceVerified: null };
391
441
  }
442
+ if (headIgnoredByHarness) {
443
+ const message = buildMsgBaserefHeadIgnored(headSha, forkRef, forkSha);
444
+ return { shouldDegrade: true, reason: 'baseref-head-ignored-by-harness', message, headSha, forkRef, forkSha, headAbsenceVerified: null };
445
+ }
392
446
  const message = buildMsgDiverged(headSha, forkRef, forkSha);
393
447
  return { shouldDegrade: true, reason: 'head-diverged-from-fork', message, headSha, forkRef, forkSha, headAbsenceVerified: null };
394
448
  }