@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
@@ -8,7 +8,25 @@ const path = require('path');
8
8
  const os = require('os');
9
9
  const { spawn } = require('child_process');
10
10
 
11
- const { updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs');
11
+ // #3582: gsd-core/bin/lib/package-identity.cjs is a tsc build artifact
12
+ // (ADR-457), gitignored and absent on a raw plugin-marketplace / git-clone
13
+ // install that never ran `npm run build:lib`. This SessionStart hook must
14
+ // DEGRADE (fall back to a generic cache filename) rather than crash session
15
+ // start. gsd-check-update-worker.js — the process this hook spawns — degrades
16
+ // identically and independently, so the shared fallback literal keeps the
17
+ // cache path consistent between writer and reader even in the (rare)
18
+ // doubly-degraded case. This try/require/ensureRuntimeBuild/require/catch
19
+ // shape is deliberately duplicated (not extracted to hooks/lib/) — see
20
+ // gsd-check-update-worker.js's identical #3582 comment for why.
21
+ let updateCacheFileName = 'gsd-update-check.json';
22
+ try {
23
+ const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
24
+ ensureRuntimeBuild();
25
+ ({ updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs'));
26
+ } catch (e) {
27
+ // Runtime library missing/broken and could not self-build — degrade to the
28
+ // fallback filename above rather than crash the SessionStart hook.
29
+ }
12
30
 
13
31
  const homeDir = os.homedir();
14
32
  const cwd = process.cwd();
@@ -22,9 +22,6 @@
22
22
 
23
23
  'use strict';
24
24
 
25
- const fs = require('fs');
26
- const path = require('path');
27
-
28
25
  const WRITE_TOOL_RE = /write|edit|replace|create|delete|remove|append|apply|patch|insert|mkdir/i;
29
26
  const PATH_KEY_RE = /^(path|file|file_?path|filepath|target_?path|target|dir|directory|uri|filename)$/i;
30
27
  const PLANNING_PATH_RE = /(^|[\\/])\.planning([\\/]|$)/;
@@ -62,6 +62,15 @@ const os = require('os');
62
62
  // writeCursorHooksJson so the require always resolves post-install.
63
63
  const { resolveStatePath } = require('./lib/cursor-workspace.js');
64
64
  const { readSentinel, VALID_ISOLATION, extractDispatchIdentifiers, sentinelAppliesToDispatch } = require('./lib/isolation-sentinel.js');
65
+ const { REASON_CODE } = require('./lib/isolation-deny-reason.js');
66
+ // #3582: gsd-core/bin/lib/*.cjs (runtime-homes.cjs, worktree-safety.cjs,
67
+ // runtime-name-policy.cjs, capability-registry.cjs — required below, inside
68
+ // resolveIsolationEvidence and resolveFallbackIsolation) are tsc build
69
+ // artifacts (ADR-457), gitignored and absent on a raw plugin-marketplace /
70
+ // git-clone install that never ran `npm run build:lib`. Self-heal once, in
71
+ // evaluateRootIsolation, before any of those four requires run — see the
72
+ // call site below. This module itself depends on nothing under ./lib.
73
+ const { ensureRuntimeBuild, RuntimeBuildError } = require('../gsd-core/bin/ensure-runtime-build.cjs');
65
74
 
66
75
  const MSG_PRESENT =
67
76
  'GSD: Subagent session started — review .planning/STATE.md for the current phase and any blockers before acting.';
@@ -333,6 +342,37 @@ function resolveIsolationDecision(data, { clock = Date, realpath = fs.realpathSy
333
342
  return { action: 'allow' };
334
343
  }
335
344
 
345
+ // ─── #3566: per-install runtime marker ────────────────────────────────────────
346
+ // Same contract as hooks/gsd-agent-isolation-guard.js's readInstallRuntimeMarker
347
+ // (mirroring src/model-resolver.cts #2297): bin/install.js writes
348
+ // `<install>/gsd-core/.gsd-runtime` beside VERSION for every runtime install;
349
+ // this hook ships at `<install>/hooks/`, so the marker is the `gsd-core` sibling
350
+ // of this file's own directory — the same sibling-layout assumption the
351
+ // require('../gsd-core/bin/lib/…') calls below already make. Epic #3473 B3 owns
352
+ // consolidating every marker reader into one shared seam.
353
+ let _installMarkerCache; // undefined = unread; null = known absent; string = value
354
+
355
+ function readInstallRuntimeMarker() {
356
+ if (_installMarkerCache !== undefined) return _installMarkerCache;
357
+ try {
358
+ const markerPath = path.join(__dirname, '..', 'gsd-core', '.gsd-runtime');
359
+ const raw = fs.readFileSync(markerPath, 'utf-8').trim();
360
+ _installMarkerCache = raw || null;
361
+ } catch {
362
+ // No marker: dev/source tree, or an install predating #2297 — "no signal
363
+ // from this rung", never a resolution failure.
364
+ _installMarkerCache = null;
365
+ }
366
+ return _installMarkerCache;
367
+ }
368
+
369
+ // Test seam — same contract as model-resolver.cts's #2297 seam; the dev/source
370
+ // tree has no marker file, so spawned-hook tests (fresh process, no marker)
371
+ // are unaffected.
372
+ function _setInstallRuntimeMarkerForTests(value) {
373
+ _installMarkerCache = value;
374
+ }
375
+
336
376
  /**
337
377
  * Conservative fallback resolution used when the #3045 sentinel is absent or
338
378
  * stale for `root`: re-derive isolation from the registry CAPABILITY, gated
@@ -355,7 +395,8 @@ function resolveIsolationDecision(data, { clock = Date, realpath = fs.realpathSy
355
395
  * otherwise legitimate dispatches, unlike `hooks/gsd-agent-isolation-guard.js`,
356
396
  * which degrades an undeterminable runtime to inert (#3045 MAJOR 2). Aligned
357
397
  * here: an explicit signal is now required — `GSD_RUNTIME` > config.json
358
- * `runtime` key > `~/.gsd/defaults.json` `runtime` (mirrors the Claude hook's
398
+ * `runtime` key > the per-install `.gsd-runtime` marker (#3566) >
399
+ * `~/.gsd/defaults.json` `runtime` (mirrors the Claude hook's
359
400
  * `resolveRuntimeIdentity`; `bin/install.js`'s `writeNonClaudeDefaults`
360
401
  * persists the installed runtime there for every non-Claude install,
361
402
  * including Cursor, so a REAL Cursor+GSD install still resolves confidently
@@ -372,6 +413,13 @@ function resolveFallbackIsolation(root, configPath) {
372
413
  if (!runtimeId && parsedConfig && typeof parsedConfig === 'object' && 'runtime' in parsedConfig) {
373
414
  runtimeId = resolveRuntimeNameFromCandidates(parsedConfig.runtime) || null;
374
415
  }
416
+ if (!runtimeId) {
417
+ // #3566: the per-install marker, above the host-wide defaults — same fix as
418
+ // hooks/gsd-agent-isolation-guard.js's resolveRuntimeIdentity. defaults.json
419
+ // is host-wide and names whichever runtime installed LAST (#2840's poison);
420
+ // the marker describes THIS install (written for every runtime since #2297).
421
+ runtimeId = resolveRuntimeNameFromCandidates(readInstallRuntimeMarker()) || null;
422
+ }
375
423
  if (!runtimeId) {
376
424
  try {
377
425
  const defaultsPath = path.join(os.homedir(), '.gsd', 'defaults.json');
@@ -419,6 +467,28 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
419
467
  }
420
468
  if (!isGsdProject) return { action: 'allow' };
421
469
 
470
+ // #3582: self-heal the compiled runtime library BEFORE any of its four
471
+ // downstream requires (resolveFallbackIsolation's two, resolveIsolationEvidence's
472
+ // two — reached only below this point). Checked separately from the
473
+ // sentinel/fallback try block below so a build failure surfaces its own
474
+ // actionable RuntimeBuildError message rather than being folded into the
475
+ // generic "could not read or resolve ... configuration" deny reason (the
476
+ // #3050 misreport this issue exists to fix). Still fails closed either way.
477
+ try {
478
+ ensureRuntimeBuild();
479
+ } catch (err) {
480
+ return {
481
+ action: 'deny',
482
+ reason:
483
+ `GSD subagent isolation guard: cannot resolve this project's dispatch-isolation ` +
484
+ `configuration because the GSD runtime library failed to self-build. ` +
485
+ `${err instanceof RuntimeBuildError ? err.message : String(err && err.message || err)} ` +
486
+ `Refusing to allow this subagent to spawn until the runtime library is built — a guard ` +
487
+ `that cannot verify must not answer "safe" (#3050).`,
488
+ reasonCode: REASON_CODE.RUNTIME_BUILD_FAILED,
489
+ };
490
+ }
491
+
422
492
  let declaredIsolation;
423
493
  try {
424
494
  // #3045 BLOCKER fix: a fresh sentinel is authoritative for THIS
@@ -439,6 +509,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
439
509
  `Refusing to allow this subagent to spawn without being able to verify whether ` +
440
510
  `isolation is required — a guard that cannot verify must not answer "safe" (#3050). ` +
441
511
  `Retry once the project configuration is readable.`,
512
+ reasonCode: REASON_CODE.CONFIG_UNREADABLE,
442
513
  };
443
514
  }
444
515
 
@@ -455,6 +526,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
455
526
  `"harness-worktree", but the subagentStart payload for this dispatch carries no usable ` +
456
527
  `subagent_type. Refusing to allow it to spawn without being able to confirm whether it ` +
457
528
  `is a GSD executor — a guard that cannot verify must not answer "safe" (#3050).`,
529
+ reasonCode: REASON_CODE.NO_SUBAGENT_TYPE,
458
530
  };
459
531
  }
460
532
 
@@ -471,6 +543,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
471
543
  `could not be determined (git did not respond). Refusing to allow subagent_type=` +
472
544
  `"${subagentType}" to spawn without being able to verify isolation — a guard that ` +
473
545
  `cannot verify must not answer "safe" (#3050). Retry once git is responsive.`,
546
+ reasonCode: REASON_CODE.CANNOT_DETERMINE_ISOLATION,
474
547
  };
475
548
  }
476
549
 
@@ -483,6 +556,7 @@ function evaluateRootIsolation(root, subagentType, { clock = Date, dispatchIds =
483
556
  `directly, with no consent and no warning. Start an isolated session first (the ` +
484
557
  `"--worktree" CLI flag or the "/worktree" chat command; Cursor manages these worktrees ` +
485
558
  `under "~/.cursor/worktrees/") and retry.`,
559
+ reasonCode: REASON_CODE.NOT_ISOLATED_WORKTREE,
486
560
  };
487
561
  }
488
562
 
@@ -532,7 +606,7 @@ function main() {
532
606
  decision = { action: 'allow' };
533
607
  }
534
608
  if (decision.action === 'deny') {
535
- const out = { permission: 'deny', user_message: decision.reason };
609
+ const out = { permission: 'deny', user_message: decision.reason, reason_code: decision.reasonCode };
536
610
  if (additionalContext !== null) out.additional_context = additionalContext;
537
611
  process.stdout.write(JSON.stringify(out));
538
612
  return;
@@ -557,4 +631,5 @@ module.exports = {
557
631
  resolveFallbackIsolation,
558
632
  resolveIsolationEvidence,
559
633
  getWorkspaceRoots,
634
+ _setInstallRuntimeMarkerForTests,
560
635
  };
@@ -20,13 +20,14 @@
20
20
 
21
21
  'use strict';
22
22
 
23
- let raw = '';
24
23
  const stdinTimeout = setTimeout(() => {
25
24
  process.exit(0);
26
25
  }, 10000);
27
26
 
28
27
  process.stdin.setEncoding('utf8');
29
- process.stdin.on('data', (chunk) => { raw += chunk; });
28
+ // Drain stdin (puts the stream into flowing mode so 'end' fires); the
29
+ // payload itself is unused — this hook responds unconditionally.
30
+ process.stdin.on('data', () => {});
30
31
  process.stdin.on('end', () => {
31
32
  clearTimeout(stdinTimeout);
32
33
  try {
@@ -11,26 +11,14 @@
11
11
  // The goal is to surface suspicious content so the orchestrator can inspect it,
12
12
  // not to create false-positive deadlocks.
13
13
 
14
- const fs = require('fs');
15
14
  const path = require('path');
16
15
 
17
- // Prompt injection patterns (subset of security.cjs patterns, inlined for hook independence)
18
- const INJECTION_PATTERNS = [
19
- /ignore\s+(all\s+)?previous\s+instructions/i,
20
- /ignore\s+(all\s+)?above\s+instructions/i,
21
- /disregard\s+(all\s+)?previous/i,
22
- /forget\s+(all\s+)?(your\s+)?instructions/i,
23
- /override\s+(system|previous)\s+(prompt|instructions)/i,
24
- /you\s+are\s+now\s+(?:a|an|the)\s+/i,
25
- /act\s+as\s+(?:a|an|the)\s+(?!plan|phase|wave)/i,
26
- /pretend\s+(?:you(?:'re| are)\s+|to\s+be\s+)/i,
27
- /from\s+now\s+on,?\s+you\s+(?:are|will|should|must)/i,
28
- /(?:print|output|reveal|show|display|repeat)\s+(?:your\s+)?(?:system\s+)?(?:prompt|instructions)/i,
29
- /<\/?(?:system|assistant|human)>/i,
30
- /\[SYSTEM\]/i,
31
- /\[INST\]/i,
32
- /<<\s*SYS\s*>>/i,
33
- ];
16
+ // Prompt injection patterns — shared with gsd-read-injection-scanner.js via
17
+ // hooks/lib/injection-patterns.js so the two surfaces cannot drift (#3504).
18
+ // Deliberately a subset of security.cjs's set: hooks stay loadable without the
19
+ // compiled lib tree. Staging of the lib helper is allowlisted in
20
+ // GSD_HOOK_LIB_FILES (bin/install.js).
21
+ const { INJECTION_PATTERNS } = require('./lib/injection-patterns.js');
34
22
 
35
23
  // #2304: Kimi's native hook bus delivers Kimi's tool vocabulary in the payload
36
24
  // (Write → WriteFile, Edit/MultiEdit → StrReplaceFile) while the [[hooks]]
@@ -153,8 +141,21 @@ process.stdin.on('end', () => {
153
141
  process.exit(0);
154
142
  }
155
143
 
156
- // Get the content being written
157
- const content = data.tool_input?.content || data.tool_input?.new_string || '';
144
+ // Get the content being written. #3504 (isolated review finding 3): the
145
+ // bare `||` chain handed a NON-STRING truthy `content` straight to
146
+ // pattern.test(), where ToString can throw ("Cannot convert object to a
147
+ // primitive value" for `{"toString": null}`) into the outer catch — the
148
+ // exact crash-to-allow class #2547/#2595 hardened inside
149
+ // normalizeKimiPayload, unreached on this read. Guarded selection: take
150
+ // the first field that is a string, or String-coerces without throwing,
151
+ // so a poisoned `content` no longer shadows a real `new_string`.
152
+ let content = '';
153
+ for (const candidate of [data.tool_input?.content, data.tool_input?.new_string]) {
154
+ if (typeof candidate === 'string' && candidate) { content = candidate; break; }
155
+ if (candidate && typeof candidate !== 'string') {
156
+ try { const s = String(candidate); if (s) { content = s; break; } } catch { /* keep looking */ }
157
+ }
158
+ }
158
159
  if (!content) {
159
160
  process.exit(0);
160
161
  }
@@ -72,23 +72,10 @@ const MARKDOWN_LINK_PATTERNS = [
72
72
  },
73
73
  ];
74
74
 
75
- // Standard injection patterns — mirrors gsd-prompt-guard.js, inlined for hook independence.
76
- const INJECTION_PATTERNS = [
77
- /ignore\s+(all\s+)?previous\s+instructions/i,
78
- /ignore\s+(all\s+)?above\s+instructions/i,
79
- /disregard\s+(all\s+)?previous/i,
80
- /forget\s+(all\s+)?(your\s+)?instructions/i,
81
- /override\s+(system|previous)\s+(prompt|instructions)/i,
82
- /you\s+are\s+now\s+(?:a|an|the)\s+/i,
83
- /act\s+as\s+(?:a|an|the)\s+(?!plan|phase|wave)/i,
84
- /pretend\s+(?:you(?:'re| are)\s+|to\s+be\s+)/i,
85
- /from\s+now\s+on,?\s+you\s+(?:are|will|should|must)/i,
86
- /(?:print|output|reveal|show|display|repeat)\s+(?:your\s+)?(?:system\s+)?(?:prompt|instructions)/i,
87
- /<\/?(?:system|assistant|human)>/i,
88
- /\[SYSTEM\]/i,
89
- /\[INST\]/i,
90
- /<<\s*SYS\s*>>/i,
91
- ];
75
+ // Standard injection patterns — shared with gsd-prompt-guard.js via
76
+ // hooks/lib/injection-patterns.js so the two surfaces cannot drift (#3504).
77
+ // Staging of the lib helper is allowlisted in GSD_HOOK_LIB_FILES (bin/install.js).
78
+ const { INJECTION_PATTERNS } = require('./lib/injection-patterns.js');
92
79
 
93
80
  const ALL_PATTERNS = [...INJECTION_PATTERNS, ...SUMMARISATION_PATTERNS];
94
81
 
@@ -98,6 +85,16 @@ const ALL_PATTERNS = [...INJECTION_PATTERNS, ...SUMMARISATION_PATTERNS];
98
85
  // construction. Normalized to forward slashes to match `p` below.
99
86
  const OWN_BUNDLE_PREFIX = __dirname.replace(/\\/g, '/').replace(/\/+$/, '') + '/';
100
87
 
88
+ // Synthetic rule ids for the finding classes that have no entry in
89
+ // MARKDOWN_LINK_PATTERNS. Frozen and referenced from BOTH the push sites and
90
+ // renderFinding so the two can never drift — a bare literal repeated at each
91
+ // site is how a rename silently falls through to the generic render branch.
92
+ const RULE_IDS = Object.freeze({
93
+ INJECTION_PATTERN: 'INJECTION-PATTERN',
94
+ INVISIBLE_UNICODE: 'INVISIBLE-UNICODE',
95
+ UNICODE_TAG_BLOCK: 'UNICODE-TAG-BLOCK',
96
+ });
97
+
101
98
  function isExcludedPath(filePath) {
102
99
  const p = filePath.replace(/\\/g, '/');
103
100
  return (
@@ -267,12 +264,19 @@ process.stdin.on('end', () => {
267
264
  process.exit(0);
268
265
  }
269
266
 
267
+ // Typed findings IR — single source of truth for both the machine-readable
268
+ // `findings` array and the rendered advisory prose. Never build these as two
269
+ // parallel arrays: that invites the generative-fix-divergence defect class
270
+ // where the rendered text and the structured data silently drift apart.
270
271
  const findings = [];
271
272
 
272
273
  for (const pattern of ALL_PATTERNS) {
273
274
  if (pattern.test(content)) {
274
275
  // Trim pattern source for readable output
275
- findings.push(pattern.source.replace(/\\s\+/g, '-').replace(/[()\\]/g, '').substring(0, 50));
276
+ findings.push({
277
+ ruleId: RULE_IDS.INJECTION_PATTERN,
278
+ match: pattern.source.replace(/\\s\+/g, '-').replace(/[()\\]/g, '').substring(0, 50),
279
+ });
276
280
  }
277
281
  }
278
282
 
@@ -284,19 +288,19 @@ process.stdin.on('end', () => {
284
288
  const m = line.match(entry.pattern);
285
289
  if (!m) continue;
286
290
  if (entry.safePredicate && entry.safePredicate(line)) continue;
287
- findings.push(`${entry.ruleId}:${m[0].substring(0, 40)}`);
291
+ findings.push({ ruleId: entry.ruleId, match: m[0].substring(0, 40) });
288
292
  }
289
293
  }
290
294
 
291
295
  // Invisible Unicode (zero-width, RTL override, soft hyphen, BOM)
292
296
  if (/[\u200B-\u200F\u2028-\u202F\uFEFF\u00AD\u2060-\u2069]/.test(content)) {
293
- findings.push('invisible-unicode');
297
+ findings.push({ ruleId: RULE_IDS.INVISIBLE_UNICODE, match: null });
294
298
  }
295
299
 
296
300
  // Unicode tag block U+E0000–E007F (invisible instruction injection vector)
297
301
  try {
298
302
  if (/[\u{E0000}-\u{E007F}]/u.test(content)) {
299
- findings.push('unicode-tag-block');
303
+ findings.push({ ruleId: RULE_IDS.UNICODE_TAG_BLOCK, match: null });
300
304
  }
301
305
  } catch {
302
306
  // Engine does not support Unicode property escapes — skip this check
@@ -306,6 +310,16 @@ process.stdin.on('end', () => {
306
310
  process.exit(0);
307
311
  }
308
312
 
313
+ // Renders one finding back into the exact prose fragment the advisory has
314
+ // always embedded. Kept as the ONLY place that maps IR -> text, so the
315
+ // `additionalContext` string and the `findings` array can never diverge.
316
+ function renderFinding(f) {
317
+ if (f.ruleId === RULE_IDS.INVISIBLE_UNICODE) return 'invisible-unicode';
318
+ if (f.ruleId === RULE_IDS.UNICODE_TAG_BLOCK) return 'unicode-tag-block';
319
+ if (f.ruleId === RULE_IDS.INJECTION_PATTERN) return f.match;
320
+ return `${f.ruleId}:${f.match}`;
321
+ }
322
+
309
323
  const severity = findings.length >= 3 ? 'HIGH' : 'LOW';
310
324
  const label = toolName === 'Read' ? path.basename(source) : source;
311
325
  const detail = severity === 'HIGH'
@@ -313,7 +327,7 @@ process.stdin.on('end', () => {
313
327
  : 'Single pattern match may be a false positive (e.g., documentation). Proceed with awareness.';
314
328
  const advisory =
315
329
  `\u26a0\ufe0f INJECTION SCAN [${severity}] (${toolName}): "${label}" triggered ` +
316
- `${findings.length} pattern(s): ${findings.join(', ')}. ` +
330
+ `${findings.length} pattern(s): ${findings.map(renderFinding).join(', ')}. ` +
317
331
  `This content is now in your conversation context. ${detail} Source: ${source}`;
318
332
 
319
333
  // Opt-in blocking: only when configured AND high-confidence
@@ -330,8 +344,8 @@ process.stdin.on('end', () => {
330
344
  const output = blocking
331
345
  ? { decision: 'block',
332
346
  reason: `Prompt-injection blocked (${toolName}). ${advisory}`,
333
- hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: advisory } }
334
- : { hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: advisory } };
347
+ hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: advisory, findings } }
348
+ : { hookSpecificOutput: { hookEventName: 'PostToolUse', additionalContext: advisory, findings } };
335
349
 
336
350
  process.stdout.write(JSON.stringify(output));
337
351
  } catch {
@@ -9,6 +9,24 @@ const os = require('os');
9
9
  // Namespace (not destructured) so tests can inject spawn failures by
10
10
  // monkeypatching childProcess.execFileSync.
11
11
  const childProcess = require('child_process');
12
+ // #3582: gsd-core/bin/lib/*.cjs (semver-compare.cjs, state-document.cjs,
13
+ // active-workstream-store.cjs, planning-workspace.cjs — required below) and
14
+ // package-identity.cjs are tsc build artifacts (ADR-457), gitignored and
15
+ // absent on a raw plugin-marketplace / git-clone install that never ran
16
+ // `npm run build:lib`. The statusline renders on EVERY prompt, so a build
17
+ // failure here must DEGRADE (print nothing, exit 0) rather than crash
18
+ // Claude Code's per-render statusline hook. Scoped to the spawned-as-a-script
19
+ // path (`require.main === module`) — a test `require()` of this module for
20
+ // its pure helpers assumes a built tree, same as every other hook test.
21
+ if (require.main === module) {
22
+ try {
23
+ const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
24
+ ensureRuntimeBuild();
25
+ } catch (e) {
26
+ process.stdout.write('');
27
+ process.exit(0);
28
+ }
29
+ }
12
30
  const { isSemverNewer } = require('../gsd-core/bin/lib/semver-compare.cjs');
13
31
  const { PACKAGE_NAME, updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs');
14
32
  const { normalizeStateStatus } = require('../gsd-core/bin/lib/state-document.cjs');
@@ -15,7 +15,28 @@
15
15
  const fs = require('fs');
16
16
  const path = require('path');
17
17
  const os = require('os');
18
- const { PACKAGE_NAME, updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs');
18
+
19
+ // #3582: gsd-core/bin/lib/package-identity.cjs is a tsc build artifact
20
+ // (ADR-457), gitignored and absent on a raw plugin-marketplace / git-clone
21
+ // install that never ran `npm run build:lib`. This is an opt-in SessionStart
22
+ // hook — a build failure here must DEGRADE, not crash session start. With
23
+ // PACKAGE_NAME left null, buildBannerOutput's own lineage guard
24
+ // (`!cache.package_name || cache.package_name !== PACKAGE_NAME`) always
25
+ // treats the cache as untrusted, so main() falls through to its existing
26
+ // silent "print nothing" path below — no separate degrade branch needed.
27
+ // This try/require/ensureRuntimeBuild/require/catch shape is deliberately
28
+ // duplicated (not extracted to hooks/lib/) — see
29
+ // gsd-check-update-worker.js's identical #3582 comment for why.
30
+ let PACKAGE_NAME = null;
31
+ let updateCacheFileName = 'gsd-update-check.json';
32
+ try {
33
+ const { ensureRuntimeBuild } = require('../gsd-core/bin/ensure-runtime-build.cjs');
34
+ ensureRuntimeBuild();
35
+ ({ PACKAGE_NAME, updateCacheFileName } = require('../gsd-core/bin/lib/package-identity.cjs'));
36
+ } catch (e) {
37
+ // Runtime library missing/broken and could not self-build — degrade to the
38
+ // fallbacks above rather than crash the SessionStart hook.
39
+ }
19
40
 
20
41
  // Suppress repeat parse-error banners for 24 hours so a genuinely broken
21
42
  // cache file doesn't nag the user every session.