@opengsd/gsd-core 1.10.0 → 1.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (328) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-debug-session-manager.md +11 -0
  4. package/agents/gsd-doc-synthesizer.md +2 -4
  5. package/agents/gsd-executor.md +5 -5
  6. package/agents/gsd-mempalace-curator.md +5 -2
  7. package/agents/gsd-phase-researcher.md +20 -1
  8. package/agents/gsd-plan-checker.md +37 -0
  9. package/agents/gsd-planner.md +44 -46
  10. package/agents/gsd-user-profiler.md +3 -0
  11. package/agents/gsd-verifier.md +12 -3
  12. package/bin/install.js +841 -971
  13. package/bin/lib/ui-safety-gate.cjs +2 -0
  14. package/commands/gsd/code-review.md +1 -1
  15. package/commands/gsd/execute-phase.md +1 -1
  16. package/commands/gsd/map-codebase.md +1 -1
  17. package/commands/gsd/mempalace-capture.md +1 -1
  18. package/commands/gsd/mempalace-recall.md +1 -1
  19. package/commands/gsd/new-milestone.md +1 -1
  20. package/commands/gsd/quick.md +1 -1
  21. package/commands/gsd/review-backlog.md +2 -1
  22. package/commands/gsd/verify-work.md +1 -1
  23. package/gsd-core/bin/gsd-tools.cjs +469 -88
  24. package/gsd-core/bin/lib/active-workstream-store.cjs +138 -22
  25. package/gsd-core/bin/lib/agent-install-check.cjs +230 -32
  26. package/gsd-core/bin/lib/api-coverage.cjs +3 -5
  27. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  28. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  29. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  30. package/gsd-core/bin/lib/audit.cjs +876 -240
  31. package/gsd-core/bin/lib/broken-windows.cjs +1 -1
  32. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  33. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  34. package/gsd-core/bin/lib/capability-registry.cjs +575 -101
  35. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  36. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  37. package/gsd-core/bin/lib/capability-validator.cjs +495 -22
  38. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  39. package/gsd-core/bin/lib/check-command-router.cjs +71 -37
  40. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  41. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  42. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  43. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  44. package/gsd-core/bin/lib/commands.cjs +651 -86
  45. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  46. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  47. package/gsd-core/bin/lib/config-loader.cjs +75 -0
  48. package/gsd-core/bin/lib/config.cjs +10 -1
  49. package/gsd-core/bin/lib/core-utils.cjs +127 -29
  50. package/gsd-core/bin/lib/decisions.cjs +23 -0
  51. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  52. package/gsd-core/bin/lib/frontmatter.cjs +155 -20
  53. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  54. package/gsd-core/bin/lib/git-base-branch.cjs +102 -0
  55. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  56. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  57. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  58. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  59. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  60. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  61. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  62. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  63. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  64. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  65. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  66. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  67. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  68. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  69. package/gsd-core/bin/lib/init.cjs +321 -129
  70. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  71. package/gsd-core/bin/lib/install-engine.cjs +745 -258
  72. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  73. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  74. package/gsd-core/bin/lib/install-profiles.cjs +134 -57
  75. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  76. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  77. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  78. package/gsd-core/bin/lib/installer-migrations.cjs +138 -31
  79. package/gsd-core/bin/lib/io.cjs +10 -0
  80. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  81. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  82. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  83. package/gsd-core/bin/lib/milestone.cjs +754 -70
  84. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  85. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  86. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  87. package/gsd-core/bin/lib/pattern.cjs +122 -0
  88. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  89. package/gsd-core/bin/lib/phase-id.cjs +444 -36
  90. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  91. package/gsd-core/bin/lib/phase-locator.cjs +125 -18
  92. package/gsd-core/bin/lib/phase.cjs +646 -143
  93. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  94. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  95. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  96. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  97. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  98. package/gsd-core/bin/lib/planning-workspace.cjs +56 -6
  99. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  100. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  101. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  102. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  103. package/gsd-core/bin/lib/review-lane-descriptor.cjs +13 -4
  104. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  105. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  106. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  107. package/gsd-core/bin/lib/roadmap-command-router.cjs +34 -0
  108. package/gsd-core/bin/lib/roadmap-parser.cjs +943 -184
  109. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  110. package/gsd-core/bin/lib/roadmap.cjs +385 -94
  111. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +608 -46
  112. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  113. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +426 -55
  114. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  115. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  116. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +115 -3
  117. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  118. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  119. package/gsd-core/bin/lib/security.cjs +104 -5
  120. package/gsd-core/bin/lib/shell-command-projection.cjs +275 -3
  121. package/gsd-core/bin/lib/smart-entry.cjs +142 -22
  122. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  123. package/gsd-core/bin/lib/state-document.cjs +152 -8
  124. package/gsd-core/bin/lib/state-transition.cjs +371 -117
  125. package/gsd-core/bin/lib/state.cjs +1794 -357
  126. package/gsd-core/bin/lib/surface.cjs +23 -9
  127. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  128. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  129. package/gsd-core/bin/lib/uat-predicate.cjs +9 -3
  130. package/gsd-core/bin/lib/uat.cjs +399 -56
  131. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  132. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  133. package/gsd-core/bin/lib/unusable-input.cjs +24 -0
  134. package/gsd-core/bin/lib/update-context.cjs +8 -2
  135. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  136. package/gsd-core/bin/lib/validate.cjs +20 -6
  137. package/gsd-core/bin/lib/vendor/README.md +37 -0
  138. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  139. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  140. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  141. package/gsd-core/bin/lib/verification.cjs +258 -8
  142. package/gsd-core/bin/lib/verify.cjs +368 -888
  143. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  144. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  145. package/gsd-core/bin/lib/workstream.cjs +2 -2
  146. package/gsd-core/bin/lib/worktree-safety.cjs +176 -9
  147. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  148. package/gsd-core/bin/shared/config-schema.manifest.json +7 -1
  149. package/gsd-core/references/agent-contracts.md +43 -26
  150. package/gsd-core/references/checkpoints.md +2 -2
  151. package/gsd-core/references/context-budget.md +1 -1
  152. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  153. package/gsd-core/references/doc-conflict-engine.md +1 -1
  154. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  155. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  156. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  157. package/gsd-core/references/execute-phase-response-language.md +1 -1
  158. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  159. package/gsd-core/references/gate-prompts.md +1 -1
  160. package/gsd-core/references/git-planning-commit.md +2 -1
  161. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  162. package/gsd-core/references/model-profiles.md +12 -4
  163. package/gsd-core/references/mvp-concepts.md +9 -9
  164. package/gsd-core/references/planner-guidance.md +3 -9
  165. package/gsd-core/references/planner-preconditions.md +1 -1
  166. package/gsd-core/references/planner-reviews.md +1 -1
  167. package/gsd-core/references/planning-config.md +8 -6
  168. package/gsd-core/references/revision-loop.md +1 -1
  169. package/gsd-core/references/specless-probe-fallback.md +1 -1
  170. package/gsd-core/references/universal-anti-patterns.md +3 -3
  171. package/gsd-core/references/verifier-phase-gates.md +192 -0
  172. package/gsd-core/references/verify-mvp-mode.md +1 -1
  173. package/gsd-core/references/workstream-flag.md +22 -6
  174. package/gsd-core/templates/discussion-log.md +1 -1
  175. package/gsd-core/templates/phase-prompt.md +2 -4
  176. package/gsd-core/templates/state.md +4 -4
  177. package/gsd-core/templates/verification-report.md +9 -1
  178. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  179. package/gsd-core/workflows/autonomous.md +1 -1
  180. package/gsd-core/workflows/cleanup.md +62 -3
  181. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +13 -3
  182. package/gsd-core/workflows/code-review-fix.md +37 -10
  183. package/gsd-core/workflows/code-review.md +38 -12
  184. package/gsd-core/workflows/complete-milestone.md +141 -18
  185. package/gsd-core/workflows/debug.md +7 -5
  186. package/gsd-core/workflows/diagnose-issues.md +35 -9
  187. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  188. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  189. package/gsd-core/workflows/discuss-phase-assumptions.md +2 -1
  190. package/gsd-core/workflows/edit-phase.md +26 -1
  191. package/gsd-core/workflows/eval-review.md +3 -5
  192. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +31 -6
  193. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  194. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +2 -0
  195. package/gsd-core/workflows/execute-phase.md +38 -50
  196. package/gsd-core/workflows/execute-plan.md +36 -4
  197. package/gsd-core/workflows/explore.md +131 -4
  198. package/gsd-core/workflows/fast.md +10 -2
  199. package/gsd-core/workflows/health.md +73 -4
  200. package/gsd-core/workflows/import.md +4 -4
  201. package/gsd-core/workflows/ingest-docs.md +5 -5
  202. package/gsd-core/workflows/mvp-phase.md +6 -3
  203. package/gsd-core/workflows/new-milestone.md +14 -9
  204. package/gsd-core/workflows/new-project.md +14 -14
  205. package/gsd-core/workflows/next.md +12 -0
  206. package/gsd-core/workflows/plan-phase.md +41 -17
  207. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  208. package/gsd-core/workflows/progress.md +34 -6
  209. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -4
  210. package/gsd-core/workflows/quick/steps/quick-verification.md +27 -6
  211. package/gsd-core/workflows/quick/steps/research-phase.md +2 -2
  212. package/gsd-core/workflows/quick.md +35 -15
  213. package/gsd-core/workflows/review.md +26 -5
  214. package/gsd-core/workflows/secure-phase.md +1 -1
  215. package/gsd-core/workflows/session-report.md +2 -1
  216. package/gsd-core/workflows/settings.md +66 -2
  217. package/gsd-core/workflows/ship.md +104 -44
  218. package/gsd-core/workflows/spec-phase.md +30 -12
  219. package/gsd-core/workflows/sync-skills.md +63 -8
  220. package/gsd-core/workflows/transition.md +46 -11
  221. package/gsd-core/workflows/ui-phase.md +5 -5
  222. package/gsd-core/workflows/ui-review.md +2 -2
  223. package/gsd-core/workflows/update.md +1 -1
  224. package/gsd-core/workflows/validate-phase.md +1 -1
  225. package/gsd-core/workflows/verify-work.md +9 -7
  226. package/hooks/dist/gsd-agent-isolation-guard.js +103 -14
  227. package/hooks/dist/gsd-check-update-worker.js +56 -13
  228. package/hooks/dist/gsd-check-update.js +19 -1
  229. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  230. package/hooks/dist/gsd-cursor-subagent-start.js +77 -2
  231. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  232. package/hooks/dist/gsd-prompt-guard.js +21 -20
  233. package/hooks/dist/gsd-read-injection-scanner.js +38 -24
  234. package/hooks/dist/gsd-statusline.js +18 -0
  235. package/hooks/dist/gsd-update-banner.js +22 -1
  236. package/hooks/dist/gsd-workflow-guard.js +134 -36
  237. package/hooks/dist/lib/git-cmd.js +92 -59
  238. package/hooks/dist/lib/injection-patterns.js +45 -0
  239. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  240. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  241. package/hooks/gsd-agent-isolation-guard.js +103 -14
  242. package/hooks/gsd-check-update-worker.js +56 -13
  243. package/hooks/gsd-check-update.js +19 -1
  244. package/hooks/gsd-cursor-pre-tool.js +0 -3
  245. package/hooks/gsd-cursor-subagent-start.js +77 -2
  246. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  247. package/hooks/gsd-prompt-guard.js +21 -20
  248. package/hooks/gsd-read-injection-scanner.js +38 -24
  249. package/hooks/gsd-statusline.js +18 -0
  250. package/hooks/gsd-update-banner.js +22 -1
  251. package/hooks/gsd-workflow-guard.js +134 -36
  252. package/hooks/lib/git-cmd.js +92 -59
  253. package/hooks/lib/injection-patterns.js +45 -0
  254. package/hooks/lib/isolation-deny-reason.js +39 -0
  255. package/hooks/lib/isolation-sentinel.js +9 -0
  256. package/package.json +21 -9
  257. package/pi/gsd.cjs +19 -5
  258. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  259. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  260. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  261. package/scripts/changeset/lint.cjs +60 -5
  262. package/scripts/check-alias-drift.cjs +7 -43
  263. package/scripts/check-contract-drift.cjs +297 -0
  264. package/scripts/ci-test-scope.cjs +19 -2
  265. package/scripts/command-contract-helpers.cjs +903 -1
  266. package/scripts/gen-adr-index.cjs +728 -38
  267. package/scripts/gen-capability-registry.cjs +3 -15
  268. package/scripts/gen-context-index.cjs +2 -11
  269. package/scripts/gen-health-docs.cjs +390 -0
  270. package/scripts/gen-inventory-manifest.cjs +50 -4
  271. package/scripts/gen-loop-host-contract.cjs +4 -24
  272. package/scripts/gen-registry.cjs +3 -14
  273. package/scripts/lib/alias-drift-families.cjs +46 -0
  274. package/scripts/lib/drift-scan.cjs +278 -0
  275. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  276. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  277. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  278. package/scripts/lint-canary-version-leak.cjs +73 -0
  279. package/scripts/lint-command-contract.cjs +96 -13
  280. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  281. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  282. package/scripts/lint-default-flip-documentation.cjs +193 -0
  283. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  284. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  285. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  286. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  287. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  288. package/scripts/lint-milestone-window-drift.cjs +468 -0
  289. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  290. package/scripts/lint-plan-count-drift.cjs +318 -0
  291. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  292. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  293. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  294. package/scripts/lint-regression-test-names.cjs +15 -13
  295. package/scripts/lint-removed-but-needed.cjs +320 -0
  296. package/scripts/lint-state-field-drift.cjs +805 -0
  297. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  298. package/scripts/lint-test-file-count.allowlist.json +21 -10
  299. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  300. package/scripts/lint-vendored-deps.cjs +124 -0
  301. package/scripts/pr-changed-files.cjs +63 -0
  302. package/scripts/pr-template-policy.cjs +14 -4
  303. package/scripts/prompt-injection-scan.sh +25 -0
  304. package/scripts/require-issue-link-policy.cjs +192 -0
  305. package/scripts/state-write-path-drift-baseline.json +19 -0
  306. package/scripts/sync-runtime-launcher.cjs +2 -4
  307. package/skills/gsd-autonomous/SKILL.md +0 -1
  308. package/skills/gsd-code-review/SKILL.md +1 -1
  309. package/skills/gsd-execute-phase/SKILL.md +1 -2
  310. package/skills/gsd-map-codebase/SKILL.md +1 -1
  311. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  312. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  313. package/skills/gsd-new-milestone/SKILL.md +1 -1
  314. package/skills/gsd-next/SKILL.md +0 -1
  315. package/skills/gsd-plan-phase/SKILL.md +0 -1
  316. package/skills/gsd-progress/SKILL.md +0 -1
  317. package/skills/gsd-quick/SKILL.md +1 -1
  318. package/skills/gsd-review-backlog/SKILL.md +2 -1
  319. package/skills/gsd-stats/SKILL.md +0 -1
  320. package/skills/gsd-verify-work/SKILL.md +1 -1
  321. package/vscode/package.json +1 -1
  322. package/gsd-core/workflows/discovery-phase.md +0 -298
  323. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  324. package/gsd-core/workflows/verify-phase.md +0 -574
  325. package/scripts/affected-tests-lib.cjs +0 -554
  326. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  327. package/scripts/run-affected-tests.cjs +0 -7
  328. package/scripts/run-tests.cjs +0 -1051
@@ -11,7 +11,7 @@ Sync managed `gsd-*` skill directories from one canonical runtime's skills root
11
11
  | Flag | Required | Default | Description |
12
12
  |------|----------|---------|-------------|
13
13
  | `--from <runtime>` | Yes | *(none)* | Source runtime — the canonical runtime to copy from |
14
- | `--to <runtime\|all>` | Yes | *(none)* | Destination runtime or `all` supported runtimes |
14
+ | `--to <runtime\|all>` | Yes | *(none)* | Destination runtime or `all` supported runtimes. **Must equal `--from`** — cross-runtime sync is refused (#3025: skill content/layout is runtime-specific and produced by the installer's per-runtime converters; use the installer for a different runtime). |
15
15
  | `--dry-run` | No | *on by default* | Preview changes without writing anything |
16
16
  | `--apply` | No | *off* | Execute the diff (overrides dry-run) |
17
17
 
@@ -50,6 +50,61 @@ fi
50
50
  - If `--from` is missing or unrecognized: print error and exit
51
51
  - If `--to` is missing or unrecognized: print error and exit
52
52
  - If `--from` == `--to` (single destination): print `[no-op: source and destination are the same runtime]` and exit
53
+ - If any `--to` destination differs from `--from` (cross-runtime): REFUSE with the installer pointer below and exit. sync only supports identity sync — see the guard.
54
+ - If `--from` or any `--to` value is not a runtime-id shape (`^[a-z0-9][a-z0-9-]*$`): REFUSE and exit — runtime ids are lowercase alphanumeric (+ hyphen); this rejects shell metacharacters before any interpolation (see security guard).
55
+
56
+ **#3025 — Runtime-id shape validation (security: run BEFORE any interpolation):**
57
+
58
+ `--from`/`--to` are interpolated into later `echo`/heredoc/`[[ ]]` contexts. Reject any value that is not a runtime-id shape BEFORE it reaches them, so a hostile value (e.g. `--to '$(cmd)'`, captured wholesale by the parser) cannot execute via command substitution in an error message.
59
+
60
+ ```bash
61
+ # #3025 (security): runtime ids are lowercase alphanumeric (+ hyphen). Reject
62
+ # anything else BEFORE any echo/heredoc/[[ ]] so a hostile --from/--to value
63
+ # cannot execute via command substitution in a later error message.
64
+ is_runtime_id() { [[ "$1" =~ ^[a-z0-9][a-z0-9-]*$ ]]; }
65
+ if ! is_runtime_id "$FROM_RUNTIME"; then
66
+ echo "error: invalid --from runtime id (not lowercase alphanumeric): '$FROM_RUNTIME'" >&2
67
+ exit 1
68
+ fi
69
+ for DEST in "${TO_RUNTIMES[@]}"; do
70
+ if ! is_runtime_id "$DEST"; then
71
+ echo "error: invalid --to runtime id (not lowercase alphanumeric): '$DEST'" >&2
72
+ exit 1
73
+ fi
74
+ done
75
+ ```
76
+
77
+ **#3025 — Cross-runtime refuse guard (run BEFORE Step 2 resolution / Step 5 copy):**
78
+
79
+ Skill content and directory layout are runtime-specific. The installer applies per-runtime
80
+ converters, adapter headers, brand swaps, and layout rules at install time, and two runtimes
81
+ (`grok`, `gemini`) resolve to ANOTHER runtime's skills root. A verbatim copy from one runtime's
82
+ skills root therefore produces content the installer would never have written for the destination,
83
+ and can damage a runtime the user never named. Every cross-runtime pair is unsafe (content and/or
84
+ layout and/or aliasing); only identity (`--from` == `--to`) is safe. Refuse cross-runtime and point
85
+ the user at the installer — the only path that produces correctly converted skills.
86
+
87
+ ```bash
88
+ # #3025: refuse cross-runtime skill sync before any resolution or copy.
89
+ for DEST in "${TO_RUNTIMES[@]}"; do
90
+ if [[ "$DEST" != "$FROM_RUNTIME" ]]; then
91
+ cat >&2 <<EOF
92
+ error: cross-runtime skill sync is not supported (--from $FROM_RUNTIME --to $DEST).
93
+ Skill content and directory layout are runtime-specific: the installer applies
94
+ per-runtime converters, adapter headers, brand swaps, and layout rules that a
95
+ verbatim copy cannot reproduce, and some runtimes share another runtime's skills
96
+ root — so a cross-runtime sync can damage a runtime you did not name.
97
+ To install correctly-converted skills for the '$DEST' runtime, run the GSD
98
+ installer for that runtime (not sync):
99
+ npx -y @opengsd/gsd-core@latest --global --<runtime>
100
+ (grok and gemini have no dedicated installer flag — they alias the codex and
101
+ claude skills roots respectively, which is itself why sync refuses them.)
102
+ sync only supports identity sync, where --from and --to are the same runtime.
103
+ EOF
104
+ exit 1
105
+ fi
106
+ done
107
+ ```
53
108
 
54
109
  ---
55
110
 
@@ -175,12 +230,12 @@ DEST_ROOT=$(gsd_run query skills-root "$DEST_RUNTIME" --raw)
175
230
 
176
231
  mkdir -p "$DEST_ROOT"
177
232
 
178
- # #3025: verbatim cp -r copies the SOURCE runtime's converted skill form, which
179
- # corrupts skills for destinations that need a different conversion (e.g. Claude
180
- # SKILL.md → Codex TOML agent). Until the installer exposes a per-skill conversion
181
- # CLI, sync is limited to runtime pairs that share the same skill format.
182
- # Run `gsd install --<dest-runtime> --local` to get correctly converted skills
183
- # for a destination that uses a different format.
233
+ # #3025: cross-runtime sync is refused in Step 1's guard (skill content/layout is
234
+ # runtime-specific; a verbatim copy corrupts destinations and can alias another
235
+ # runtime's root). This loop is therefore reached only for IDENTITY sync, where
236
+ # every skill is SKIP (source == destination) and the create/update lists are
237
+ # empty. If per-runtime conversion is ever wired in, this is where it would go;
238
+ # until then the cp -r must never run for a destination != source.
184
239
 
185
240
  for SKILL in $CREATE_LIST $UPDATE_LIST; do
186
241
  rm -rf "$DEST_ROOT/$SKILL"
@@ -215,6 +270,6 @@ Sync complete: <N> skills synced to <M> runtime(s).
215
270
 
216
271
  ## Limitations
217
272
 
218
- - Sync copies files verbatim and does not apply runtime-specific content transformations. Use the GSD installer directly for runtimes that require format conversion.
273
+ - Sync copies files verbatim and does not apply runtime-specific content transformations. **Cross-runtime sync is refused** (#3025): skill content and layout are runtime-specific, and some runtimes alias another runtime's skills root, so a verbatim cross-runtime copy corrupts the destination (and can damage a runtime you did not name). Only identity sync (`--from` == `--to`) is supported. To install skills for a different runtime, run the GSD installer for that runtime (`npx -y @opengsd/gsd-core@latest --global --<runtime>`).
219
274
  - Cross-project skills (`.agents/skills/`) are out of scope — this command only touches global runtime skills roots.
220
275
  - Bidirectional sync is not supported. Choose one canonical source with `--from`.
@@ -36,6 +36,31 @@ Mark current phase complete and advance to next. This is the natural point where
36
36
 
37
37
  <process>
38
38
 
39
+ <step name="post_completion_mode" priority="first">
40
+
41
+ **Invocation mode — read this FIRST.** This workflow runs two ways:
42
+
43
+ 1. **Standalone transition** (normal path): the phase is being marked complete AND
44
+ transitioned by this workflow. Run EVERY step below in order — `verify_completion`,
45
+ `update_roadmap_and_state` (which calls `gsd_run query phase.complete`), then the
46
+ post-processing.
47
+
48
+ 2. **Post-completion delegation** (invoked by `execute-phase` after its auto-chain
49
+ completion — #1526): `phase.complete` was already called by execute-phase's
50
+ `update_roadmap` step and verification already passed in execute-phase's
51
+ `verify_phase_goal`. SKIP `verify_completion` and `update_roadmap_and_state`
52
+ (re-running `phase.complete` would double-write STATE.md/ROADMAP.md). Run
53
+ `cleanup_handoff` (stale `.continue-here` handoffs are still cleared post-completion),
54
+ then BEGIN at `evolve_project` and run every step from there through
55
+ `offer_next_phase` (this is the post-processing parity set: graduation scan,
56
+ session-continuity, project-reference, accumulated-context, current-position/progress).
57
+ `archive_prompts` is a documented no-op in either mode.
58
+
59
+ Detect post-completion mode when the caller states that phase completion and
60
+ verification have already run. When in doubt, run standalone (mode 1) — it is
61
+ idempotent enough to be safe, just slower.
62
+ </step>
63
+
39
64
  <step name="load_project_state" priority="first">
40
65
 
41
66
  Before transition, read project state:
@@ -77,12 +102,22 @@ cat .planning/config.json 2>/dev/null || true
77
102
  **Check for verification debt in this phase:**
78
103
 
79
104
  ```bash
80
- # Run a preliminary frontmatter check via awk — the runtime launcher is not yet
81
- # defined at this step, so avoid any runtime tool calls here.
105
+ _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
106
+ # #3492: resolve THIS phase's own report through the single shared seam
107
+ # (src/verification.cts resolveVerificationFile) instead of a blind
108
+ # `*-VERIFICATION.md` glob — a stray ad-hoc worksheet (e.g.
109
+ # `03-CORRECTION-VERIFICATION.md`) alphabetically outranks the real report
110
+ # and previously fed this awk parse the wrong file.
111
+ VERIFICATION_FILE=$(gsd_run query verification.resolve-file .planning/phases/XX-current --raw 2>/dev/null)
82
112
  # awk extracts only the status: field between the two --- fences to avoid
83
113
  # false positives from historical body text (e.g. previous_status: gaps_found).
84
- VERIFY_STATUS=$(awk 'NR==1&&/^---$/{in_fm=1;next}in_fm&&/^---$/{exit}in_fm&&/^status: /{print $2}' \
85
- .planning/phases/XX-current/*-VERIFICATION.md 2>/dev/null | head -1)
114
+ # FNR (not NR) re-arms the frontmatter scan per input file: NR only ever arms
115
+ # on the very FIRST line of the very first file, so a multi-file input would
116
+ # silently read empty status for every file after the first. The resolver
117
+ # above always hands back a single path, but the parse stays correct even if
118
+ # that ever changes.
119
+ VERIFY_STATUS=$(awk 'FNR==1&&/^---$/{in_fm=1;next}in_fm&&/^---$/{exit}in_fm&&/^status: /{print $2}' \
120
+ "$VERIFICATION_FILE" 2>/dev/null | head -1)
86
121
  ```
87
122
 
88
123
  **If VERIFY_STATUS is not `passed`:**
@@ -95,10 +130,10 @@ Verification incomplete: ${VERIFY_STATUS:-missing}
95
130
  Resolve before transition. Review: `/gsd:audit-uat`
96
131
  ```
97
132
 
98
- This preliminary check blocks obviously unresolved verification before the
99
- launcher is available. `gsd-tools.cjs query phase.complete` remains the
100
- authoritative stale-aware gate and fail-closes unless canonical verification
101
- status is `passed`.
133
+ This preliminary check blocks obviously unresolved verification early, ahead
134
+ of the authoritative gate below. `gsd-tools.cjs query phase.complete` (in
135
+ `update_roadmap_and_state`) remains the authoritative stale-aware gate and
136
+ fail-closes unless canonical verification status is `passed`.
102
137
 
103
138
  **If all plans complete:**
104
139
 
@@ -165,7 +200,6 @@ If found, delete them — phase is complete, handoffs are stale.
165
200
  **Delegate ROADMAP.md and STATE.md updates to `gsd-tools.cjs query phase.complete`:**
166
201
 
167
202
  ```bash
168
- _GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; if [ -f "$GSD_TOOLS" ]; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif command -v gsd-tools >/dev/null 2>&1; then GSD_TOOLS="$(command -v gsd-tools)"; gsd_run() { "$GSD_TOOLS" "$@"; }; elif [ -f "${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLAUDE_CONFIG_DIR:-$HOME/.claude}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${HERMES_HOME:-$HOME/.hermes}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CURSOR_CONFIG_DIR:-$HOME/.cursor}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEX_HOME:-$HOME/.codex}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GEMINI_CONFIG_DIR:-$HOME/.gemini}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${COPILOT_CONFIG_DIR:-$HOME/.copilot}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${AUGMENT_CONFIG_DIR:-$HOME/.augment}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${TRAE_CONFIG_DIR:-$HOME/.trae}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${QWEN_CONFIG_DIR:-$HOME/.qwen}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${CLINE_CONFIG_DIR:-$HOME/.cline}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${GROK_AGENTS_HOME:-$HOME/.agents}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; elif [ -f "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}" ]; then GSD_TOOLS="${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}/gsd-core/bin/${_GSD_SHIM_NAME}"; gsd_run() { node "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and gsd-tools is not on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
169
203
  TRANSITION=$(gsd_run query phase.complete "${current_phase}")
170
204
  ```
171
205
 
@@ -194,7 +228,8 @@ Evolve PROJECT.md to reflect learnings from completed phase.
194
228
  **Read phase summaries:**
195
229
 
196
230
  ```bash
197
- cat .planning/phases/XX-current/*-SUMMARY.md
231
+ _SUMMARIES=( .planning/phases/XX-current/*-SUMMARY.md )
232
+ if [ -e "${_SUMMARIES[0]}" ]; then cat "${_SUMMARIES[@]}"; fi
198
233
  ```
199
234
 
200
235
  **Assess requirement changes:**
@@ -417,7 +452,7 @@ ROADMAP=$(gsd_run query roadmap.analyze)
417
452
 
418
453
  This returns all phases with goals, disk status, and completion info.
419
454
 
420
- **Section-manifest gate (#2994):** `gsd_run` is already established above (`update_roadmap_and_state` step) — fetch the dedicated `init.transition` bundle for the workstream-collision-check gate below:
455
+ **Section-manifest gate (#2994):** `gsd_run` is already established above (`verify_completion` step) — fetch the dedicated `init.transition` bundle for the workstream-collision-check gate below:
421
456
 
422
457
  ```bash
423
458
  INIT_TRANSITION=$(gsd_run query init.transition)
@@ -132,14 +132,14 @@ Create UI design contract for Phase {phase_number}: {phase_name}
132
132
  Answer: "What visual and interaction contracts does this phase need?"
133
133
  </objective>
134
134
 
135
- <files_to_read>
135
+ <required_reading>
136
136
  - {state_path} (Project State)
137
137
  - {roadmap_path} (Roadmap)
138
138
  - {requirements_path} (Requirements)
139
139
  - {context_path} (USER DECISIONS from /gsd:discuss-phase)
140
140
  - {research_path} (Technical Research — stack decisions)
141
141
  - {SKETCH_FINDINGS_PATH} (Sketch Findings — validated design decisions, CSS patterns, visual direction from /gsd:sketch, if exists)
142
- </files_to_read>
142
+ </required_reading>
143
143
 
144
144
  ${AGENT_SKILLS_UI}
145
145
 
@@ -155,7 +155,7 @@ padded_phase: {padded_phase}
155
155
  </config>
156
156
  ```
157
157
 
158
- Omit null file paths from `<files_to_read>`.
158
+ Omit null file paths from `<required_reading>`.
159
159
 
160
160
  <!-- #2508 runtime-aware-dispatch -->
161
161
 
@@ -205,11 +205,11 @@ Validate UI design contract for Phase {phase_number}: {phase_name}
205
205
  Check all 6 dimensions. Return APPROVED or BLOCKED.
206
206
  </objective>
207
207
 
208
- <files_to_read>
208
+ <required_reading>
209
209
  - {phase_dir}/{padded_phase}-UI-SPEC.md (UI Design Contract — PRIMARY INPUT)
210
210
  - {context_path} (USER DECISIONS — check compliance)
211
211
  - {research_path} (Technical Research — check stack alignment)
212
- </files_to_read>
212
+ </required_reading>
213
213
 
214
214
  ${AGENT_SKILLS_UI_CHECKER}
215
215
 
@@ -84,12 +84,12 @@ Conduct 6-pillar visual audit of Phase {phase_number}: {phase_name}
84
84
  {If no UI-SPEC: "Audit against abstract 6-pillar standards."}
85
85
  </objective>
86
86
 
87
- <files_to_read>
87
+ <required_reading>
88
88
  - {summary_paths} (Execution summaries)
89
89
  - {plan_paths} (Execution plans — what was intended)
90
90
  - {ui_spec_path} (UI Design Contract — audit baseline, if exists)
91
91
  - {context_path} (User decisions, if exists)
92
- </files_to_read>
92
+ </required_reading>
93
93
 
94
94
  ${AGENT_SKILLS_UI_REVIEWER}
95
95
 
@@ -78,7 +78,7 @@ Parse output:
78
78
  - Line 4 = resolved GSD config dir (e.g. `/Users/me/.claude`, `/Users/me/.gemini`); empty if scope is `UNKNOWN`. Capture this as `GSD_DIR` and pass it to subsequent steps so they don't re-derive the runtime path.
79
79
  - If scope is `UNKNOWN`, proceed to install using the `--claude --global` fallback.
80
80
 
81
- `update-context` reproduces the previous detection cascade — preferred-config-dir fast path, local-over-global with same-path dedup (so `CWD=$HOME` does not misdetect as LOCAL), env-var overrides (`CLAUDE_CONFIG_DIR`, `OPENCODE_CONFIG_DIR`, `KILO_CONFIG`, `XDG_CONFIG_HOME`, `CODEX_HOME`, …), and semver validation — but as a tested projection rather than ~280 lines of inline bash. Branch coverage lives in `tests/issue-498-update-context.test.cjs`.
81
+ `update-context` reproduces the previous detection cascade — preferred-config-dir fast path, local-over-global with same-path dedup (so `CWD=$HOME` does not misdetect as LOCAL), env-var overrides (`CLAUDE_CONFIG_DIR`, `OPENCODE_CONFIG_DIR`, `KILO_CONFIG`, `XDG_CONFIG_HOME`, `CODEX_HOME`, …), and semver validation — but as a tested projection rather than ~280 lines of inline bash. Branch coverage lives in `tests/update-context.test.cjs`.
82
82
 
83
83
  If multiple runtime installs are detected and the invoking runtime cannot be determined from execution_context, ask the user which runtime to update before running install.
84
84
 
@@ -110,7 +110,7 @@ Print: `◆ Spawning nyquist auditor... (runs in a subagent — no output until
110
110
  ```
111
111
  Agent(
112
112
  prompt="Read ~/.claude/agents/gsd-nyquist-auditor.md for instructions.\n\n" +
113
- "<files_to_read>{PLAN, SUMMARY, impl files, VALIDATION.md}</files_to_read>" +
113
+ "<required_reading>{PLAN, SUMMARY, impl files, VALIDATION.md}</required_reading>" +
114
114
  "<gaps>{gap list}</gaps>" +
115
115
  "<test_infrastructure>{framework, config, commands}</test_infrastructure>" +
116
116
  "<constraints>Never modify impl files. Max 3 debug iterations. Escalate impl bugs.</constraints>" +
@@ -70,6 +70,8 @@ VERIFY_PRE_HOOKS_JSON=$(gsd_run loop render-hooks verify:pre --raw)
70
70
  PHASE_DIR=$(printf '%s' "$INIT" | jq -r '.phase_dir // empty')
71
71
  ```
72
72
 
73
+ ⚠ **Validate `check` before shell use** (third-party manifest input) — `loop-hook-dispatch.md` § `gate`.
74
+
73
75
  Resolve active gate hooks from `VERIFY_PRE_HOOKS_JSON` where `kind == "gate"`.
74
76
  For each active gate hook, run its declared check (a `check.query` gate runs
75
77
  `gsd_run check ${hook.check.query} "${PHASE_DIR}" --raw`; a `predicate` gate
@@ -570,7 +572,7 @@ If execution verification is waiting only on human UAT and this session recorded
570
572
 
571
573
  ```bash
572
574
  PHASE_DIR=$(printf '%s' "$INIT" | jq -r '.phase_dir // empty')
573
- VERIFICATION_FILE=$(ls "${PHASE_DIR}"/*-VERIFICATION.md 2>/dev/null | head -1)
575
+ VERIFICATION_FILE=$(gsd_run query verification.resolve-file "$PHASE_DIR" --raw 2>/dev/null)
574
576
  VERIFICATION_STATUS=$(gsd_run query verification.status "$PHASE_DIR" 2>/dev/null)
575
577
  VERIFICATION_STATUS_VALUE=$(printf '%s' "$VERIFICATION_STATUS" | jq -r '.status // empty' 2>/dev/null || echo "")
576
578
  PHASE_VERIFICATION_STATUS="$VERIFICATION_STATUS_VALUE"
@@ -704,11 +706,11 @@ Agent(
704
706
  **Phase:** {phase_number}
705
707
  **Mode:** gap_closure
706
708
 
707
- <files_to_read>
709
+ <required_reading>
708
710
  - {phase_dir}/{phase_num}-UAT.md (UAT with diagnoses)
709
711
  - {state_path} (Project State)
710
712
  - {roadmap_path} (Roadmap)
711
- </files_to_read>
713
+ </required_reading>
712
714
 
713
715
  ${AGENT_SKILLS_PLANNER}
714
716
 
@@ -769,9 +771,9 @@ Agent(
769
771
  **Phase:** {phase_number}
770
772
  **Phase Goal:** Close diagnosed gaps from UAT
771
773
 
772
- <files_to_read>
774
+ <required_reading>
773
775
  - {phase_dir}/*-PLAN.md (Plans to verify)
774
- </files_to_read>
776
+ </required_reading>
775
777
 
776
778
  ${AGENT_SKILLS_CHECKER}
777
779
 
@@ -813,9 +815,9 @@ Agent(
813
815
  **Phase:** {phase_number}
814
816
  **Mode:** revision
815
817
 
816
- <files_to_read>
818
+ <required_reading>
817
819
  - {phase_dir}/*-PLAN.md (Existing plans)
818
- </files_to_read>
820
+ </required_reading>
819
821
 
820
822
  ${AGENT_SKILLS_PLANNER}
821
823
 
@@ -43,8 +43,9 @@
43
43
  // authoritative — `none`/`orchestrator-worktree` ALLOW immediately
44
44
  // (sequential/orchestrator-managed dispatch is legitimate, not a bug); an
45
45
  // absent/stale sentinel falls back to a conservative registry+config check
46
- // (GSD_RUNTIME env > .planning/config.json `runtime` — no confident signal
47
- // degrades to inert rather than guessing 'claude', see resolveRegistryIsolation)
46
+ // (GSD_RUNTIME env > .planning/config.json `runtime` > the per-install
47
+ // `.gsd-runtime` marker, #3566 — no confident signal degrades to inert rather
48
+ // than guessing 'claude', see resolveRegistryIsolation)
48
49
  // gated additionally by `workflow.use_worktrees` — read directly, in-process,
49
50
  // no subprocess spawn.
50
51
  //
@@ -63,6 +64,15 @@ const fs = require('fs');
63
64
  const path = require('path');
64
65
  const os = require('os');
65
66
  const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
67
+ const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
68
+ // #3582: gsd-core/bin/lib/*.cjs (runtime-name-policy.cjs, capability-registry.cjs
69
+ // below) are tsc build artifacts (ADR-457), gitignored and absent on a raw
70
+ // plugin-marketplace / git-clone install that never ran `npm run build:lib`.
71
+ // Self-heal before the first such require (resolveRegistryIsolation, below) —
72
+ // see ensureRuntimeBuild's own header for the full rationale. This module
73
+ // itself (gsd-core/bin/ensure-runtime-build.cjs) depends on nothing under
74
+ // ./lib, so requiring it here is always safe.
75
+ const { ensureRuntimeBuild, RuntimeBuildError } = require('../gsd-core/bin/ensure-runtime-build.cjs');
66
76
 
67
77
  // No other executor-shaped subagent_type exists in agents/ today
68
78
  // (verified: only agents/gsd-executor.md). A Set, not a bare string compare,
@@ -85,6 +95,42 @@ function parseHarnessFlag(flag) {
85
95
  return { param: m[1], value: m[2] };
86
96
  }
87
97
 
98
+ // ─── #3566: per-install runtime marker ────────────────────────────────────────
99
+ // bin/install.js writes `<install>/gsd-core/.gsd-runtime` for EVERY runtime
100
+ // install (#2297), co-located with VERSION. Unlike `~/.gsd/defaults.json` —
101
+ // which is host-wide and names whichever runtime's install ran LAST, the exact
102
+ // leakage #2840's config.cjs change exists to prevent — the marker describes
103
+ // THIS install, which is the property runtime identity needs on a machine
104
+ // with 2+ runtimes. Mirrors readInstallRuntimeMarker in src/model-resolver.cts
105
+ // (same cache + test-seam shape); this hook cannot import that module without
106
+ // dragging the whole model-resolution stack into a PreToolUse hot path, so the
107
+ // 5-line read lives here against the same sibling-layout assumption the hook's
108
+ // own require('../gsd-core/bin/lib/…') already makes. Epic #3473 B3 owns
109
+ // consolidating every marker reader into one shared seam.
110
+ let _installMarkerCache; // undefined = unread; null = known absent; string = value
111
+
112
+ function readInstallRuntimeMarker() {
113
+ if (_installMarkerCache !== undefined) return _installMarkerCache;
114
+ try {
115
+ const markerPath = path.join(__dirname, '..', 'gsd-core', '.gsd-runtime');
116
+ const raw = fs.readFileSync(markerPath, 'utf-8').trim();
117
+ _installMarkerCache = raw || null;
118
+ } catch {
119
+ // No marker: dev/source tree, or an install predating #2297 — "no signal
120
+ // from this rung", never a resolution failure. Falls through to the
121
+ // defaults rung below.
122
+ _installMarkerCache = null;
123
+ }
124
+ return _installMarkerCache;
125
+ }
126
+
127
+ // Test seam for the marker rung (the dev/source tree has no marker file, so
128
+ // the read always bottoms out at null there — same seam contract as
129
+ // model-resolver.cts's _setInstallRuntimeMarkerForTests, #2297).
130
+ function _setInstallRuntimeMarkerForTests(value) {
131
+ _installMarkerCache = value;
132
+ }
133
+
88
134
  /**
89
135
  * Resolve this project's declared `runtime` identity WITHOUT defaulting to
90
136
  * 'claude' when no explicit signal exists (#3045 MAJOR 2).
@@ -100,9 +146,10 @@ function parseHarnessFlag(flag) {
100
146
  *
101
147
  * Returns `{ runtimeId, confident }`. `confident` is true only when an
102
148
  * explicit signal exists (GSD_RUNTIME env override, a `runtime` key literally
103
- * present in config.json, or a `runtime` persisted to `~/.gsd/defaults.json`
104
- * by the installer — see below); false means "cannot determine" and callers
105
- * must NOT silently substitute 'claude' — see resolveRegistryIsolation.
149
+ * present in config.json, the per-install `.gsd-runtime` marker, or a
150
+ * `runtime` persisted to `~/.gsd/defaults.json` by the installer — see
151
+ * below); false means "cannot determine" and callers must NOT silently
152
+ * substitute 'claude' — see resolveRegistryIsolation.
106
153
  *
107
154
  * #3045 BLOCKER 2 fix: precedence is GSD_RUNTIME env > config.json `runtime`
108
155
  * key > `~/.gsd/defaults.json` `runtime`. The first two are unchanged; the
@@ -117,6 +164,17 @@ function parseHarnessFlag(flag) {
117
164
  * (`nativeModelAliases` short-circuits it) and therefore correctly still rely
118
165
  * on config.json/env. Reading the installer's own persisted signal makes
119
166
  * "confident" the common case instead.
167
+ *
168
+ * #3566: the per-install `.gsd-runtime` marker now sits BETWEEN config.json
169
+ * and defaults.json. defaults.json is host-wide and names whichever runtime
170
+ * installed LAST — on a 2-runtime machine that confidently resolves the WRONG
171
+ * runtime (a Codex install's `runtime:"codex"` leaking into Claude projects),
172
+ * and when the wrong runtime declares no harnessIsolationFlag the guard goes
173
+ * silently inert. The marker describes THIS install (written for every
174
+ * runtime since #2297), which is the source #2840's config.cjs change names
175
+ * as correct. defaults.json stays as the final rung so single-runtime default
176
+ * installs and pre-#2297 installs (no marker on disk) keep the #3045
177
+ * BLOCKER 2 behavior.
120
178
  */
121
179
  function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidates) {
122
180
  const envRuntime = resolveRuntimeNameFromCandidates(process.env.GSD_RUNTIME);
@@ -132,6 +190,13 @@ function resolveRuntimeIdentity(cwd, configPath, resolveRuntimeNameFromCandidate
132
190
  if (configRuntime) return { runtimeId: configRuntime, confident: true };
133
191
  }
134
192
 
193
+ // #3566: the per-install marker, above the host-wide defaults — see the
194
+ // block comment on readInstallRuntimeMarker. An empty/whitespace-only file
195
+ // or an unknown value degrades exactly like the other rungs (no signal /
196
+ // future-runtime tolerance via resolveRuntimeNameFromCandidates).
197
+ const markerRuntime = resolveRuntimeNameFromCandidates(readInstallRuntimeMarker());
198
+ if (markerRuntime) return { runtimeId: markerRuntime, confident: true };
199
+
135
200
  // #3045 BLOCKER 2: fall back to the installer-persisted default. Read
136
201
  // defensively — an absent/corrupt/non-object defaults.json is "no signal",
137
202
  // never a resolution failure (this function only ever throws for the
@@ -183,6 +248,15 @@ function resolveHarnessFlag(runtimeId, runtimes) {
183
248
  * run, e.g. a manual Agent() call before any sentinel has been written).
184
249
  */
185
250
  function resolveRegistryIsolation(cwd, configPath) {
251
+ // #3582: self-heal the compiled runtime library BEFORE either require
252
+ // below — this is the only reaching path to both (resolveRegistryIsolation
253
+ // is the sole caller of each), so one call here covers both. Throws
254
+ // RuntimeBuildError on an unbuildable tree; the caller (resolveIsolationState)
255
+ // already wraps this whole function in try/catch and folds any error into
256
+ // its fail-closed `error` result — evaluateDispatch below distinguishes a
257
+ // RuntimeBuildError there so it surfaces this seam's actionable message
258
+ // instead of being misreported as an unreadable config.json (#3050 lesson).
259
+ ensureRuntimeBuild();
186
260
  const { resolveRuntimeNameFromCandidates } = require('../gsd-core/bin/lib/runtime-name-policy.cjs');
187
261
  const { runtimes } = require('../gsd-core/bin/lib/capability-registry.cjs');
188
262
 
@@ -360,13 +434,27 @@ function evaluateDispatch(data, { clock = Date } = {}) {
360
434
  if (!state.gsdProject) return { action: 'allow' };
361
435
 
362
436
  if (state.error) {
363
- const reason =
364
- `Agent isolation guard: could not read or resolve this project's dispatch-isolation ` +
365
- `configuration ('.planning/config.json' under '${cwd}'). Refusing to dispatch ` +
366
- `subagent_type="${subagentType}" without being able to verify whether isolation is ` +
367
- `required — a guard that cannot verify must not answer "safe" (#3050). Retry once the ` +
368
- `project configuration is readable.`;
369
- return { action: 'block', reason };
437
+ // #3582: a missing/unbuildable compiled runtime library (RuntimeBuildError,
438
+ // thrown by ensureRuntimeBuild in resolveRegistryIsolation) is a DIFFERENT,
439
+ // actionable failure from an unreadable/unparsable config.json — surface
440
+ // its own message instead of misreporting it as the generic
441
+ // "could not read or resolve ... configuration" text (the exact #3050
442
+ // misreport this issue exists to fix). Both cases still fail closed
443
+ // (block); only the message differs.
444
+ const isBuildFailure = state.error instanceof RuntimeBuildError;
445
+ const reason = isBuildFailure
446
+ ? `Agent isolation guard: cannot resolve this project's dispatch-isolation ` +
447
+ `configuration because the GSD runtime library failed to self-build. ` +
448
+ `${state.error.message} Refusing to dispatch subagent_type="${subagentType}" until ` +
449
+ `the runtime library is built — a guard that cannot verify must not answer "safe" ` +
450
+ `(#3050).`
451
+ : `Agent isolation guard: could not read or resolve this project's dispatch-isolation ` +
452
+ `configuration ('.planning/config.json' under '${cwd}'). Refusing to dispatch ` +
453
+ `subagent_type="${subagentType}" without being able to verify whether isolation is ` +
454
+ `required — a guard that cannot verify must not answer "safe" (#3050). Retry once the ` +
455
+ `project configuration is readable.`;
456
+ const reasonCode = isBuildFailure ? REASON_CODE.RUNTIME_BUILD_FAILED : REASON_CODE.CONFIG_UNREADABLE;
457
+ return { action: 'block', reason, reasonCode };
370
458
  }
371
459
 
372
460
  if (state.isolation !== 'harness-worktree') return { action: 'allow' };
@@ -382,7 +470,7 @@ function evaluateDispatch(data, { clock = Date } = {}) {
382
470
  `${parsed.param}="${parsed.value}". Add ${parsed.param}="${parsed.value}" to the Agent() ` +
383
471
  `call so the executor runs in an isolated worktree instead of the primary checkout ` +
384
472
  `(gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md).`;
385
- return { action: 'block', reason };
473
+ return { action: 'block', reason, reasonCode: REASON_CODE.HARNESS_FLAG_MISSING };
386
474
  }
387
475
 
388
476
  /* istanbul ignore next -- stdin adapter, exercised via spawnSync in tests */
@@ -397,7 +485,7 @@ function main() {
397
485
  const data = JSON.parse(input);
398
486
  const decision = evaluateDispatch(data);
399
487
  if (decision.action === 'block') {
400
- const out = { decision: 'block', reason: decision.reason };
488
+ const out = { decision: 'block', reason: decision.reason, reason_code: decision.reasonCode };
401
489
  process.stdout.write(JSON.stringify(out));
402
490
  // Kimi feeds stderr (not stdout) back to the model on exit 2.
403
491
  process.stderr.write(decision.reason);
@@ -425,4 +513,5 @@ module.exports = {
425
513
  resolveHarnessFlag,
426
514
  resolveRegistryIsolation,
427
515
  parseHarnessFlag,
516
+ _setInstallRuntimeMarkerForTests,
428
517
  };
@@ -11,18 +11,61 @@
11
11
 
12
12
  const fs = require('fs');
13
13
  const path = require('path');
14
- const { isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs');
15
- // Latest-version lookup is delegated to the single deterministic adapter
16
- // (#498). checkLatestVersion() owns the npm-view call, the timeout/semver
17
- // policy, and the package name — sourced from the baked Package Identity seam.
18
- // The previous `require('../package.json').name` (#378) never yielded a name in
19
- // the installed tree — at the time it resolved to the synthetic
20
- // {"type":"commonjs"} marker GSD wrote at the config root, which has no `.name`,
21
- // so the background check never reported updates. Since #2544 GSD writes no
22
- // marker there at all, so that require would now fail to resolve outright.
23
- // Either way the name must come from the baked seam, never a walk-up.
24
- const { checkLatestVersion } = require('../gsd-core/bin/check-latest-version.cjs');
25
- const { PACKAGE_NAME } = require('../gsd-core/bin/lib/package-identity.cjs');
14
+
15
+ // #3582: gsd-core/bin/lib/semver-compare.cjs and package-identity.cjs (and,
16
+ // transitively, check-latest-version.cjs's own gsd-core/bin/lib/cli-exit.cjs
17
+ // + shell-command-projection.cjs) are tsc build artifacts (ADR-457),
18
+ // gitignored and absent on a raw plugin-marketplace / git-clone install that
19
+ // never ran `npm run build:lib`. This worker is a DETACHED SessionStart
20
+ // background process (spawned with stdio: 'ignore') — a build failure here
21
+ // must DEGRADE to the no-signal fallbacks below (mirroring the
22
+ // managed-hooks-registry.cjs degrade just below) so the worker still runs to
23
+ // completion and writes a result cache record, rather than dying silently
24
+ // with no visible signal and no cache-file write at all.
25
+ //
26
+ // This try/require/ensureRuntimeBuild/require/catch shape repeats (with
27
+ // different destructured names) in hooks/gsd-check-update.js and
28
+ // hooks/gsd-update-banner.js. It is deliberately NOT extracted into a shared
29
+ // hooks/lib/ helper: scripts/lint-hooks-runtime-build-seam.cjs enforces this
30
+ // exact seam textually, PER FILE — it greps each hooks/ file for its OWN
31
+ // literal `require('.../ensure-runtime-build.cjs')` + `ensureRuntimeBuild(`
32
+ // call co-occurring with its OWN literal `require('.../gsd-core/bin/lib/*.cjs')`.
33
+ // A generic helper taking the compiled module's path as a variable would move
34
+ // the literal compiled-lib require OUT of this file and into the helper,
35
+ // called with a non-literal argument — the scan's regex (see that script's
36
+ // "Known limitations") cannot see a require() called with a variable, so this
37
+ // file would then read as "requires nothing" and the lint would stop
38
+ // protecting it. A ceremony-only helper (just the ensureRuntimeBuild call,
39
+ // each caller keeping its own literal compiled-lib require) fails the SAME
40
+ // way from the other side: it would remove this file's own literal
41
+ // `require('.../ensure-runtime-build.cjs')` + `ensureRuntimeBuild(` call,
42
+ // which the lint also requires to be textually present in THIS file. Either
43
+ // shape needs the lint script itself widened to special-case the helper,
44
+ // which is a bigger, riskier change than the ~6 duplicated lines it would
45
+ // save; kept inline instead.
46
+ let isSemverNewer = () => false;
47
+ let checkLatestVersion = () => ({ ok: false });
48
+ let PACKAGE_NAME = null;
49
+ try {
50
+ const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
51
+ ensureRuntimeBuild();
52
+ ({ isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs'));
53
+ // Latest-version lookup is delegated to the single deterministic adapter
54
+ // (#498). checkLatestVersion() owns the npm-view call, the timeout/semver
55
+ // policy, and the package name — sourced from the baked Package Identity seam.
56
+ // The previous `require('../package.json').name` (#378) never yielded a name in
57
+ // the installed tree — at the time it resolved to the synthetic
58
+ // {"type":"commonjs"} marker GSD wrote at the config root, which has no `.name`,
59
+ // so the background check never reported updates. Since #2544 GSD writes no
60
+ // marker there at all, so that require would now fail to resolve outright.
61
+ // Either way the name must come from the baked seam, never a walk-up.
62
+ ({ checkLatestVersion } = require('../gsd-core/bin/check-latest-version.cjs'));
63
+ ({ PACKAGE_NAME } = require('../gsd-core/bin/lib/package-identity.cjs'));
64
+ } catch (e) {
65
+ // Runtime library missing/broken and could not self-build — degrade to the
66
+ // no-signal fallbacks declared above; the worker still writes a result
67
+ // cache record (package_name: null, update_available: false).
68
+ }
26
69
  // Authoritative list of managed hooks — shared with tests to retire source-grep
27
70
  // assertions (pending-migration-to-typed-ir [#455]).
28
71
  // NOTE: managed-hooks-registry.cjs must be in HOOKS_TO_COPY (scripts/build-hooks.js)
@@ -63,7 +106,7 @@ try {
63
106
  // (e.g., gsd-intel-*.js) must be ignored to avoid permanent stale warnings (#1750)
64
107
  // MANAGED_HOOKS is imported from ./managed-hooks-registry.cjs above.
65
108
 
66
- let staleHooks = [];
109
+ const staleHooks = [];
67
110
  if (configDir) {
68
111
  // #3023: the bundle's directory name is runtime-descriptor-driven (pi stages
69
112
  // it as `gsd-hooks/`), so deriving it as `<configDir>/hooks` silently scanned