@opengsd/gsd-core 1.10.0 → 1.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (544) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/agents/gsd-code-fixer.md +1 -1
  4. package/agents/gsd-debug-session-manager.md +12 -1
  5. package/agents/gsd-debugger.md +1 -1
  6. package/agents/gsd-doc-synthesizer.md +2 -4
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +22 -14
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +5 -3
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +23 -2
  15. package/agents/gsd-plan-checker.md +50 -53
  16. package/agents/gsd-planner.md +50 -50
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +63 -4
  21. package/agents/gsd-ui-researcher.md +41 -3
  22. package/agents/gsd-user-profiler.md +3 -0
  23. package/agents/gsd-verifier.md +13 -4
  24. package/bin/install.js +1448 -1103
  25. package/commands/gsd/code-review.md +1 -1
  26. package/commands/gsd/discuss-phase.md +1 -1
  27. package/commands/gsd/execute-phase.md +1 -1
  28. package/commands/gsd/import.md +1 -1
  29. package/commands/gsd/map-codebase.md +1 -1
  30. package/commands/gsd/mempalace-capture.md +1 -1
  31. package/commands/gsd/mempalace-recall.md +1 -1
  32. package/commands/gsd/new-milestone.md +1 -1
  33. package/commands/gsd/quick.md +9 -5
  34. package/commands/gsd/review-backlog.md +2 -1
  35. package/commands/gsd/verify-work.md +1 -1
  36. package/gsd-core/bin/gsd-tools.cjs +1035 -138
  37. package/gsd-core/bin/lib/active-workstream-store.cjs +146 -22
  38. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  39. package/gsd-core/bin/lib/agent-install-check.cjs +392 -32
  40. package/gsd-core/bin/lib/api-coverage.cjs +33 -14
  41. package/gsd-core/bin/lib/artifacts.cjs +5 -0
  42. package/gsd-core/bin/lib/assumption-delta.cjs +32 -15
  43. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  44. package/gsd-core/bin/lib/audit.cjs +1026 -268
  45. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  46. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  47. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  48. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  49. package/gsd-core/bin/lib/capability-registry.cjs +845 -130
  50. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  51. package/gsd-core/bin/lib/capability-state.cjs +18 -3
  52. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  53. package/gsd-core/bin/lib/capability-validator.cjs +700 -40
  54. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  55. package/gsd-core/bin/lib/check-command-router.cjs +216 -42
  56. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  57. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  58. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  59. package/gsd-core/bin/lib/codex-agent-toml.cjs +735 -0
  60. package/gsd-core/bin/lib/command-aliases.cjs +22 -0
  61. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  62. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  63. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  64. package/gsd-core/bin/lib/commands.cjs +1172 -108
  65. package/gsd-core/bin/lib/commonjs-marker.cjs +12 -6
  66. package/gsd-core/bin/lib/complexity-trigger.cjs +1192 -0
  67. package/gsd-core/bin/lib/config-loader.cjs +187 -23
  68. package/gsd-core/bin/lib/config.cjs +102 -3
  69. package/gsd-core/bin/lib/configuration.cjs +129 -37
  70. package/gsd-core/bin/lib/core-utils.cjs +208 -33
  71. package/gsd-core/bin/lib/decisions.cjs +23 -0
  72. package/gsd-core/bin/lib/edge-probe.cjs +9 -1
  73. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  74. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  75. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  76. package/gsd-core/bin/lib/frontmatter.cjs +899 -229
  77. package/gsd-core/bin/lib/gap-checker.cjs +95 -10
  78. package/gsd-core/bin/lib/git-base-branch.cjs +276 -39
  79. package/gsd-core/bin/lib/gsd2-import.cjs +10 -1
  80. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  81. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  82. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +149 -0
  83. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  84. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  85. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  86. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +268 -0
  87. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  88. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  89. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +187 -0
  90. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  91. package/gsd-core/bin/lib/health-diagnostic.cjs +451 -0
  92. package/gsd-core/bin/lib/host-integration.cjs +39 -6
  93. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  94. package/gsd-core/bin/lib/init-command-router.cjs +118 -21
  95. package/gsd-core/bin/lib/init.cjs +439 -168
  96. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  97. package/gsd-core/bin/lib/install-engine.cjs +811 -259
  98. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  99. package/gsd-core/bin/lib/install-model-override-resolver.cjs +235 -0
  100. package/gsd-core/bin/lib/install-profiles.cjs +212 -61
  101. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  102. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  103. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  104. package/gsd-core/bin/lib/installer-migration-report.cjs +3 -0
  105. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  106. package/gsd-core/bin/lib/installer-migrations.cjs +148 -38
  107. package/gsd-core/bin/lib/intel.cjs +101 -26
  108. package/gsd-core/bin/lib/io.cjs +170 -15
  109. package/gsd-core/bin/lib/learnings.cjs +85 -14
  110. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  111. package/gsd-core/bin/lib/markdown-sectionizer.cjs +2 -1
  112. package/gsd-core/bin/lib/markdown-table.cjs +183 -22
  113. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  114. package/gsd-core/bin/lib/milestone.cjs +842 -73
  115. package/gsd-core/bin/lib/model-catalog.cjs +232 -16
  116. package/gsd-core/bin/lib/model-resolver.cjs +193 -68
  117. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  118. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  119. package/gsd-core/bin/lib/pattern.cjs +122 -0
  120. package/gsd-core/bin/lib/phase-estimation.cjs +18 -9
  121. package/gsd-core/bin/lib/phase-id.cjs +514 -40
  122. package/gsd-core/bin/lib/phase-lifecycle.cjs +52 -19
  123. package/gsd-core/bin/lib/phase-locator.cjs +262 -34
  124. package/gsd-core/bin/lib/phase.cjs +1038 -214
  125. package/gsd-core/bin/lib/plan-dependency-graph.cjs +72 -1
  126. package/gsd-core/bin/lib/plan-document.cjs +263 -0
  127. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  128. package/gsd-core/bin/lib/plan-scan.cjs +98 -3
  129. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  130. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  131. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  132. package/gsd-core/bin/lib/planning-snapshot.cjs +894 -0
  133. package/gsd-core/bin/lib/planning-workspace.cjs +112 -6
  134. package/gsd-core/bin/lib/probe-core.cjs +5 -2
  135. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  136. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  137. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  138. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  139. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +766 -0
  140. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +11 -6
  141. package/gsd-core/bin/lib/review-lane-descriptor.cjs +22 -13
  142. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  143. package/gsd-core/bin/lib/review-lane-runner.cjs +421 -66
  144. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  145. package/gsd-core/bin/lib/roadmap-command-router.cjs +59 -11
  146. package/gsd-core/bin/lib/roadmap-parser.cjs +1006 -184
  147. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  148. package/gsd-core/bin/lib/roadmap.cjs +442 -96
  149. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +702 -52
  150. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  151. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +459 -55
  152. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  153. package/gsd-core/bin/lib/runtime-homes.cjs +69 -3
  154. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +402 -58
  155. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  156. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  157. package/gsd-core/bin/lib/runtime-slash.cjs +96 -8
  158. package/gsd-core/bin/lib/security.cjs +104 -5
  159. package/gsd-core/bin/lib/shell-command-projection.cjs +342 -7
  160. package/gsd-core/bin/lib/smart-entry.cjs +133 -23
  161. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  162. package/gsd-core/bin/lib/state-command-router.cjs +52 -19
  163. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  164. package/gsd-core/bin/lib/state-document.cjs +338 -8
  165. package/gsd-core/bin/lib/state-md-schema.cjs +221 -0
  166. package/gsd-core/bin/lib/state-transition.cjs +846 -176
  167. package/gsd-core/bin/lib/state.cjs +2589 -369
  168. package/gsd-core/bin/lib/surface.cjs +33 -11
  169. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  170. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  171. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  172. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  173. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  174. package/gsd-core/bin/lib/uat-predicate.cjs +67 -23
  175. package/gsd-core/bin/lib/uat.cjs +1761 -167
  176. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  177. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  178. package/gsd-core/bin/lib/ui-safety-gate.cjs +51 -12
  179. package/gsd-core/bin/lib/unusable-input.cjs +37 -0
  180. package/gsd-core/bin/lib/update-context.cjs +8 -2
  181. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  182. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  183. package/gsd-core/bin/lib/validate.cjs +20 -6
  184. package/gsd-core/bin/lib/vendor/README.md +75 -0
  185. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  186. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  187. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  188. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  189. package/gsd-core/bin/lib/verification.cjs +272 -9
  190. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  191. package/gsd-core/bin/lib/verify.cjs +453 -918
  192. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +53 -32
  193. package/gsd-core/bin/lib/workstream-inventory.cjs +63 -10
  194. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  195. package/gsd-core/bin/lib/workstream.cjs +2 -2
  196. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  197. package/gsd-core/bin/lib/worktree-safety.cjs +341 -18
  198. package/gsd-core/bin/shared/config-defaults.manifest.json +8 -1
  199. package/gsd-core/bin/shared/config-schema.manifest.json +12 -1
  200. package/gsd-core/bin/shared/exit-codes.json +8 -0
  201. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  202. package/gsd-core/bin/shared/model-catalog.json +8 -1
  203. package/gsd-core/references/agent-contracts.md +44 -26
  204. package/gsd-core/references/api-coverage.md +24 -2
  205. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  206. package/gsd-core/references/checkpoints.md +39 -21
  207. package/gsd-core/references/context-budget.md +1 -1
  208. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  209. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  210. package/gsd-core/references/doc-conflict-engine.md +1 -1
  211. package/gsd-core/references/edge-probe.md +8 -0
  212. package/gsd-core/references/execute-mvp-tdd.md +4 -6
  213. package/gsd-core/references/execute-phase-between-wave-reset.md +15 -14
  214. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  215. package/gsd-core/references/execute-phase-response-language.md +1 -1
  216. package/gsd-core/references/execute-phase-wave-guard.md +17 -11
  217. package/gsd-core/references/failing-direction.md +78 -0
  218. package/gsd-core/references/gate-prompts.md +1 -1
  219. package/gsd-core/references/git-integration.md +5 -5
  220. package/gsd-core/references/git-planning-commit.md +5 -4
  221. package/gsd-core/references/gsd-run-resolver.md +1 -1
  222. package/gsd-core/references/loop-hook-dispatch.md +61 -2
  223. package/gsd-core/references/model-profiles.md +12 -4
  224. package/gsd-core/references/mvp-concepts.md +9 -9
  225. package/gsd-core/references/nyquist-compliance.md +74 -0
  226. package/gsd-core/references/offer-next.md +3 -5
  227. package/gsd-core/references/phase-argument-parsing.md +3 -3
  228. package/gsd-core/references/planner-failing-direction.md +53 -0
  229. package/gsd-core/references/planner-guidance.md +3 -9
  230. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  231. package/gsd-core/references/planner-preconditions.md +1 -1
  232. package/gsd-core/references/planner-reviews.md +1 -1
  233. package/gsd-core/references/planner-revision.md +1 -1
  234. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  235. package/gsd-core/references/planning-config.md +44 -13
  236. package/gsd-core/references/reviewer-instances.md +31 -0
  237. package/gsd-core/references/revision-loop.md +1 -1
  238. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  239. package/gsd-core/references/specless-probe-fallback.md +1 -1
  240. package/gsd-core/references/tdd.md +1 -3
  241. package/gsd-core/references/ui-brand.md +65 -21
  242. package/gsd-core/references/ui-consideration-probe.md +1 -1
  243. package/gsd-core/references/universal-anti-patterns.md +5 -5
  244. package/gsd-core/references/verifier-phase-gates.md +192 -0
  245. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  246. package/gsd-core/references/verify-mvp-mode.md +2 -2
  247. package/gsd-core/references/workstream-flag.md +33 -17
  248. package/gsd-core/templates/README.md +1 -1
  249. package/gsd-core/templates/SECURITY.md +3 -3
  250. package/gsd-core/templates/UI-SPEC.md +25 -3
  251. package/gsd-core/templates/VALIDATION.md +3 -3
  252. package/gsd-core/templates/discussion-log.md +1 -1
  253. package/gsd-core/templates/phase-prompt.md +5 -4
  254. package/gsd-core/templates/state.md +11 -4
  255. package/gsd-core/templates/verification-report.md +9 -1
  256. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  257. package/gsd-core/workflows/add-backlog.md +1 -1
  258. package/gsd-core/workflows/add-phase.md +3 -3
  259. package/gsd-core/workflows/add-tests.md +3 -8
  260. package/gsd-core/workflows/add-todo.md +1 -1
  261. package/gsd-core/workflows/ai-integration-phase.md +13 -20
  262. package/gsd-core/workflows/audit-fix.md +12 -3
  263. package/gsd-core/workflows/audit-milestone.md +9 -9
  264. package/gsd-core/workflows/audit-uat.md +17 -2
  265. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  266. package/gsd-core/workflows/autonomous.md +11 -27
  267. package/gsd-core/workflows/check-todos.md +1 -1
  268. package/gsd-core/workflows/cleanup.md +64 -5
  269. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +14 -4
  270. package/gsd-core/workflows/code-review-fix.md +38 -11
  271. package/gsd-core/workflows/code-review.md +159 -52
  272. package/gsd-core/workflows/complete-milestone.md +151 -23
  273. package/gsd-core/workflows/debug.md +12 -8
  274. package/gsd-core/workflows/diagnose-issues.md +47 -15
  275. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  276. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -8
  277. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  278. package/gsd-core/workflows/discuss-phase/modes/text.md +1 -1
  279. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  280. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  281. package/gsd-core/workflows/discuss-phase.md +1 -1
  282. package/gsd-core/workflows/do.md +3 -6
  283. package/gsd-core/workflows/docs-update.md +5 -4
  284. package/gsd-core/workflows/edit-phase.md +27 -2
  285. package/gsd-core/workflows/eval-review.md +7 -14
  286. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +1 -1
  287. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +142 -15
  288. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  289. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  290. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  291. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +24 -4
  292. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  293. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  294. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -2
  295. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  296. package/gsd-core/workflows/execute-phase.md +72 -100
  297. package/gsd-core/workflows/execute-plan.md +52 -15
  298. package/gsd-core/workflows/explore.md +131 -4
  299. package/gsd-core/workflows/extract-learnings.md +1 -1
  300. package/gsd-core/workflows/fast.md +10 -2
  301. package/gsd-core/workflows/forensics.md +1 -1
  302. package/gsd-core/workflows/graduation.md +5 -5
  303. package/gsd-core/workflows/health.md +76 -10
  304. package/gsd-core/workflows/import.md +18 -15
  305. package/gsd-core/workflows/inbox.md +4 -5
  306. package/gsd-core/workflows/ingest-docs.md +49 -16
  307. package/gsd-core/workflows/insert-phase.md +5 -5
  308. package/gsd-core/workflows/list-seeds.md +5 -3
  309. package/gsd-core/workflows/list-workspaces.md +1 -1
  310. package/gsd-core/workflows/manager.md +12 -23
  311. package/gsd-core/workflows/map-codebase.md +1 -1
  312. package/gsd-core/workflows/milestone-summary.md +1 -1
  313. package/gsd-core/workflows/mvp-phase.md +8 -5
  314. package/gsd-core/workflows/new-milestone.md +22 -29
  315. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  316. package/gsd-core/workflows/new-project.md +26 -40
  317. package/gsd-core/workflows/new-workspace.md +1 -1
  318. package/gsd-core/workflows/next.md +14 -2
  319. package/gsd-core/workflows/pause-work.md +1 -1
  320. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  321. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  322. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -4
  323. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +3 -3
  324. package/gsd-core/workflows/plan-phase.md +162 -59
  325. package/gsd-core/workflows/plan-review-convergence.md +96 -11
  326. package/gsd-core/workflows/plant-seed.md +2 -2
  327. package/gsd-core/workflows/pr-branch.md +187 -51
  328. package/gsd-core/workflows/profile-user.md +16 -14
  329. package/gsd-core/workflows/progress.md +61 -18
  330. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  331. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +5 -7
  332. package/gsd-core/workflows/quick/steps/quick-verification.md +28 -9
  333. package/gsd-core/workflows/quick/steps/research-phase.md +4 -6
  334. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  335. package/gsd-core/workflows/quick.md +55 -44
  336. package/gsd-core/workflows/remove-phase.md +4 -4
  337. package/gsd-core/workflows/remove-workspace.md +2 -2
  338. package/gsd-core/workflows/resume-project.md +8 -12
  339. package/gsd-core/workflows/review.md +219 -20
  340. package/gsd-core/workflows/scan.md +1 -1
  341. package/gsd-core/workflows/secure-phase.md +3 -3
  342. package/gsd-core/workflows/session-report.md +2 -1
  343. package/gsd-core/workflows/settings-advanced.md +7 -9
  344. package/gsd-core/workflows/settings-integrations.md +64 -31
  345. package/gsd-core/workflows/settings.md +69 -7
  346. package/gsd-core/workflows/ship.md +116 -50
  347. package/gsd-core/workflows/sketch-wrap-up.md +11 -17
  348. package/gsd-core/workflows/sketch.md +12 -18
  349. package/gsd-core/workflows/smart-entry.md +3 -5
  350. package/gsd-core/workflows/spec-phase.md +53 -13
  351. package/gsd-core/workflows/spike-wrap-up.md +7 -11
  352. package/gsd-core/workflows/spike.md +20 -31
  353. package/gsd-core/workflows/stats.md +2 -2
  354. package/gsd-core/workflows/sync-skills.md +64 -9
  355. package/gsd-core/workflows/thread.md +11 -7
  356. package/gsd-core/workflows/transition.md +49 -14
  357. package/gsd-core/workflows/ui-phase.md +15 -21
  358. package/gsd-core/workflows/ui-review.md +8 -12
  359. package/gsd-core/workflows/ultraplan-phase.md +5 -13
  360. package/gsd-core/workflows/undo.md +8 -16
  361. package/gsd-core/workflows/update.md +7 -11
  362. package/gsd-core/workflows/validate-phase.md +3 -3
  363. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  364. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  365. package/gsd-core/workflows/verify-work.md +66 -25
  366. package/hooks/dist/gsd-agent-isolation-guard.js +158 -30
  367. package/hooks/dist/gsd-check-update-worker.js +56 -13
  368. package/hooks/dist/gsd-check-update.js +19 -1
  369. package/hooks/dist/gsd-config-reload.js +18 -12
  370. package/hooks/dist/gsd-context-monitor.js +19 -10
  371. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  372. package/hooks/dist/gsd-cursor-pre-tool.js +2 -3
  373. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  374. package/hooks/dist/gsd-cursor-stop.js +2 -1
  375. package/hooks/dist/gsd-cursor-subagent-start.js +83 -3
  376. package/hooks/dist/gsd-cursor-subagent-stop.js +6 -3
  377. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  378. package/hooks/dist/gsd-graphify-update.sh +22 -18
  379. package/hooks/dist/gsd-node-runner.sh +76 -0
  380. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  381. package/hooks/dist/gsd-prompt-guard.js +37 -27
  382. package/hooks/dist/gsd-read-guard.js +16 -7
  383. package/hooks/dist/gsd-read-injection-scanner.js +55 -32
  384. package/hooks/dist/gsd-session-state.sh +1 -0
  385. package/hooks/dist/gsd-statusline.js +231 -24
  386. package/hooks/dist/gsd-update-banner.js +22 -1
  387. package/hooks/dist/gsd-validate-commit.sh +80 -6
  388. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  389. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  390. package/hooks/dist/gsd-workflow-guard.js +162 -46
  391. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  392. package/hooks/dist/gsd-write-guard.js +35 -25
  393. package/hooks/dist/lib/cli-exit.js +560 -0
  394. package/hooks/dist/lib/exit-code-registry.js +98 -0
  395. package/hooks/dist/lib/git-cmd.js +92 -59
  396. package/hooks/dist/lib/git-probe.js +84 -0
  397. package/hooks/dist/lib/hook-exit.js +81 -0
  398. package/hooks/dist/lib/injection-patterns.js +45 -0
  399. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  400. package/hooks/dist/lib/isolation-sentinel.js +9 -0
  401. package/hooks/dist/managed-hooks-registry.cjs +3 -0
  402. package/hooks/gsd-agent-isolation-guard.js +158 -30
  403. package/hooks/gsd-check-update-worker.js +56 -13
  404. package/hooks/gsd-check-update.js +19 -1
  405. package/hooks/gsd-config-reload.js +18 -12
  406. package/hooks/gsd-context-monitor.js +19 -10
  407. package/hooks/gsd-cursor-post-tool.js +3 -1
  408. package/hooks/gsd-cursor-pre-tool.js +2 -3
  409. package/hooks/gsd-cursor-session-start.js +2 -1
  410. package/hooks/gsd-cursor-stop.js +2 -1
  411. package/hooks/gsd-cursor-subagent-start.js +83 -3
  412. package/hooks/gsd-cursor-subagent-stop.js +6 -3
  413. package/hooks/gsd-ensure-canonical-path.js +2 -1
  414. package/hooks/gsd-graphify-update.sh +22 -18
  415. package/hooks/gsd-node-runner.sh +76 -0
  416. package/hooks/gsd-phase-boundary.sh +1 -0
  417. package/hooks/gsd-prompt-guard.js +37 -27
  418. package/hooks/gsd-read-guard.js +16 -7
  419. package/hooks/gsd-read-injection-scanner.js +55 -32
  420. package/hooks/gsd-session-state.sh +1 -0
  421. package/hooks/gsd-statusline.js +231 -24
  422. package/hooks/gsd-update-banner.js +22 -1
  423. package/hooks/gsd-validate-commit.sh +80 -6
  424. package/hooks/gsd-windsurf-pre-command.js +16 -11
  425. package/hooks/gsd-windsurf-pre-write.js +22 -13
  426. package/hooks/gsd-workflow-guard.js +162 -46
  427. package/hooks/gsd-worktree-path-guard.js +36 -21
  428. package/hooks/gsd-write-guard.js +35 -25
  429. package/hooks/lib/cli-exit.js +560 -0
  430. package/hooks/lib/exit-code-registry.js +98 -0
  431. package/hooks/lib/git-cmd.js +92 -59
  432. package/hooks/lib/git-probe.js +84 -0
  433. package/hooks/lib/hook-exit.js +81 -0
  434. package/hooks/lib/injection-patterns.js +45 -0
  435. package/hooks/lib/isolation-deny-reason.js +39 -0
  436. package/hooks/lib/isolation-sentinel.js +9 -0
  437. package/hooks/managed-hooks-registry.cjs +3 -0
  438. package/package.json +28 -11
  439. package/pi/gsd.cjs +19 -5
  440. package/scripts/base64-scan.sh +74 -12
  441. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  442. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  443. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  444. package/scripts/build-hooks.js +5 -0
  445. package/scripts/changeset/lint.cjs +60 -5
  446. package/scripts/check-alias-drift.cjs +7 -43
  447. package/scripts/check-contract-drift.cjs +297 -0
  448. package/scripts/check-glossary-refs.cjs +77 -15
  449. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  450. package/scripts/ci-check-job-near-cap.cjs +49 -0
  451. package/scripts/ci-pr-mergeability.cjs +262 -0
  452. package/scripts/ci-test-scope.cjs +64 -14
  453. package/scripts/ci-timeout-report.cjs +230 -0
  454. package/scripts/command-contract-helpers.cjs +903 -1
  455. package/scripts/docs-guard-registry.cjs +396 -0
  456. package/scripts/gen-adr-index.cjs +728 -38
  457. package/scripts/gen-capability-registry.cjs +11 -21
  458. package/scripts/gen-context-index.cjs +2 -11
  459. package/scripts/gen-exit-code-docs.cjs +318 -0
  460. package/scripts/gen-exit-code-registry.cjs +891 -0
  461. package/scripts/gen-features.cjs +836 -0
  462. package/scripts/gen-health-docs.cjs +390 -0
  463. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  464. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  465. package/scripts/gen-inventory-manifest.cjs +50 -4
  466. package/scripts/gen-loop-host-contract.cjs +138 -25
  467. package/scripts/gen-registry.cjs +3 -14
  468. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  469. package/scripts/gen-state-md-docs.cjs +727 -0
  470. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  471. package/scripts/lib/alias-drift-families.cjs +46 -0
  472. package/scripts/lib/ci-job-timing.cjs +72 -0
  473. package/scripts/lib/cli-exit.cjs +546 -44
  474. package/scripts/lib/drift-scan.cjs +308 -0
  475. package/scripts/lib/exit-code-registry.cjs +98 -0
  476. package/scripts/lib/ndjson-reporter.cjs +119 -0
  477. package/scripts/lint-allow-test-rule-refs.allowlist.json +1 -26
  478. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  479. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  480. package/scripts/lint-canary-version-leak.cjs +73 -0
  481. package/scripts/lint-command-contract.cjs +96 -13
  482. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  483. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  484. package/scripts/lint-default-flip-documentation.cjs +193 -0
  485. package/scripts/lint-docs-guard-registration.cjs +495 -0
  486. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +193 -0
  487. package/scripts/lint-eslint-glob-coverage.allowlist.json +38 -0
  488. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  489. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  490. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  491. package/scripts/lint-health-diagnostic-rule-table.cjs +461 -0
  492. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  493. package/scripts/lint-milestone-window-drift.cjs +468 -0
  494. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  495. package/scripts/lint-phase-enumeration-drift.cjs +492 -0
  496. package/scripts/lint-plan-count-drift.cjs +318 -0
  497. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  498. package/scripts/lint-planning-prompt-drift.cjs +471 -0
  499. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  500. package/scripts/lint-regression-test-names.cjs +15 -13
  501. package/scripts/lint-removed-but-needed.cjs +488 -0
  502. package/scripts/lint-seam-enforcement.cjs +182 -0
  503. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  504. package/scripts/lint-source-test-name-collision.cjs +241 -0
  505. package/scripts/lint-state-field-drift.cjs +805 -0
  506. package/scripts/lint-state-write-path-drift.cjs +950 -0
  507. package/scripts/lint-test-file-count.allowlist.json +137 -8
  508. package/scripts/lint-test-file-count.cjs +25 -3
  509. package/scripts/lint-unreachable-guard-drift.cjs +830 -0
  510. package/scripts/lint-vendored-deps.cjs +297 -0
  511. package/scripts/mutation-matrix.cjs +599 -50
  512. package/scripts/pr-changed-files.cjs +63 -0
  513. package/scripts/pr-template-policy.cjs +14 -4
  514. package/scripts/prompt-injection-scan.sh +100 -14
  515. package/scripts/require-issue-link-policy.cjs +192 -0
  516. package/scripts/secret-scan.sh +75 -13
  517. package/scripts/select-docs-guards.cjs +56 -0
  518. package/scripts/sync-runtime-launcher.cjs +24 -7
  519. package/skills/gsd-autonomous/SKILL.md +0 -1
  520. package/skills/gsd-code-review/SKILL.md +1 -1
  521. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  522. package/skills/gsd-execute-phase/SKILL.md +1 -2
  523. package/skills/gsd-import/SKILL.md +1 -1
  524. package/skills/gsd-map-codebase/SKILL.md +1 -1
  525. package/skills/gsd-mempalace-capture/SKILL.md +1 -1
  526. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  527. package/skills/gsd-new-milestone/SKILL.md +1 -1
  528. package/skills/gsd-next/SKILL.md +0 -1
  529. package/skills/gsd-plan-phase/SKILL.md +0 -1
  530. package/skills/gsd-progress/SKILL.md +0 -1
  531. package/skills/gsd-quick/SKILL.md +9 -5
  532. package/skills/gsd-review-backlog/SKILL.md +2 -1
  533. package/skills/gsd-stats/SKILL.md +0 -1
  534. package/skills/gsd-verify-work/SKILL.md +1 -1
  535. package/vscode/package.json +1 -1
  536. package/bin/lib/ui-safety-gate.cjs +0 -107
  537. package/gsd-core/workflows/discovery-phase.md +0 -298
  538. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  539. package/gsd-core/workflows/verify-phase.md +0 -574
  540. package/scripts/affected-tests-lib.cjs +0 -554
  541. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  542. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  543. package/scripts/run-affected-tests.cjs +0 -7
  544. package/scripts/run-tests.cjs +0 -1051
@@ -0,0 +1,234 @@
1
+ "use strict";
2
+ /**
3
+ * Runtime identity — prove which package's `gsd-tools` actually executed (#3146).
4
+ *
5
+ * The predecessor package `get-shit-done-cc` publishes a colliding `gsd-tools`
6
+ * bin (verified 2026-08-24 against 1.42.3: `bin.gsd-tools -> bin/gsd-sdk.js`),
7
+ * and exposes verbs of the same name with different semantics. #3129 is the
8
+ * worked example: `phases.clear` archives here and **deletes** there, and the
9
+ * output is success-shaped either way, so 43 phase directories went missing
10
+ * with no warning.
11
+ *
12
+ * That collision is now prevented in the resolver rather than detected here:
13
+ * the launcher's PATH branch resolves `gsd_run`, which only this package
14
+ * publishes and which self-locates to its sibling shim, so a foreign binary is
15
+ * unreachable from PATH. The path-based branches — a project-local install, a
16
+ * runtime config directory — have no such structural guarantee: they trust
17
+ * their configured location. #3841 closes that remaining route by making the
18
+ * launcher preamble PROBE the resolved tool once, before any verb runs, and
19
+ * warn when it cannot prove it is this package. This module backs both the
20
+ * `runtime-identity` verb the preamble calls and the vocabulary that gate
21
+ * reports in (`IDENTITY_STATUS`).
22
+ *
23
+ * The rollout is warn-then-fail (the #3146 maintainer ruling): the preamble
24
+ * prints one actionable line and proceeds. It cannot hard-fail yet because
25
+ * `no_identity_verb` does not distinguish a foreign package from an
26
+ * `@opengsd/gsd-core` older than the verb — and during rollout the old-version
27
+ * case is the common one, so a hard failure would stop working installs.
28
+ *
29
+ * Parsing is deliberately STRICT. The whole value of the check is telling "us"
30
+ * from "not us"; a lenient parse that accepts the predecessor's usage text as
31
+ * close-enough would reproduce the exact silent success #3129 already produced.
32
+ * Liberality is spent on visibility instead — distinct reason codes, each
33
+ * naming what actually happened.
34
+ *
35
+ * The classifier and the shell preamble are held in agreement by a
36
+ * cross-surface parity test (#3841): both read stdout only and must reach the
37
+ * same verdict for the same payload, or the preamble's warning and this
38
+ * module's diagnostic would silently diverge on the same input.
39
+ */
40
+ Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.IDENTITY_RAW_PREFIX = exports.IDENTITY_STATUS = exports.EXPECTED_PACKAGE_NAME = void 0;
42
+ exports.classifyIdentityProbe = classifyIdentityProbe;
43
+ exports.statusForVerdict = statusForVerdict;
44
+ exports.buildIdentityPayload = buildIdentityPayload;
45
+ exports.explainVerdict = explainVerdict;
46
+ exports.cmdRuntimeIdentity = cmdRuntimeIdentity;
47
+ const package_identity_cjs_1 = require("./package-identity.cjs");
48
+ const capability_loader_cjs_1 = require("./capability-loader.cjs");
49
+ /** The package name a legitimate GSD runtime reports. Baked at build time (#498). */
50
+ exports.EXPECTED_PACKAGE_NAME = package_identity_cjs_1.packageName;
51
+ /**
52
+ * The two-valued status the launcher preamble exports as `GSD_IDENTITY_STATUS`
53
+ * (#3841). Frozen so it is a VALUE a test can assert on: CONTRIBUTING forbids
54
+ * proving the gate fired by matching its human-readable warning prose.
55
+ *
56
+ * Two values, not five: the shell has no use for the distinction between
57
+ * `no_identity_verb` and `unparseable` — it either holds proof or it does not.
58
+ * The five-way {@link IdentityReason} stays the diagnostic vocabulary;
59
+ * {@link statusForVerdict} is the only bridge between them.
60
+ */
61
+ exports.IDENTITY_STATUS = Object.freeze({
62
+ OK: 'ok',
63
+ UNVERIFIED: 'unverified',
64
+ });
65
+ /**
66
+ * The exact byte prefix the preamble anchors its `case` pattern to.
67
+ *
68
+ * ANCHORED, never a substring search. `JSON.stringify` emits keys in insertion
69
+ * order and `buildIdentityPayload` inserts `packageName` first, so a genuine
70
+ * `--raw` payload always STARTS with this. An unanchored match would verify the
71
+ * decoy `{"packageName":"get-shit-done-cc","note":"@opengsd/gsd-core"}`, which
72
+ * is exactly the shape a colliding package could publish.
73
+ *
74
+ * The preamble anchors at BOTH ends: its `case` pattern is this prefix, then
75
+ * anything, then a literal `}`, so a truncated payload fails too. That is safe
76
+ * for any future additive field — a JSON object's own closing brace is always
77
+ * the last character, whatever the last value's type. It also pairs the literal
78
+ * `{` for the raw-text brace guards the preamble is inlined into (#3841); the
79
+ * pairing is a consequence of the stronger check, not the reason for it.
80
+ */
81
+ exports.IDENTITY_RAW_PREFIX = `{"packageName":"${exports.EXPECTED_PACKAGE_NAME}"`;
82
+ /** Cap on echoed probe output so a warning can never dump a whole usage screen. */
83
+ const EVIDENCE_MAX_CHARS = 200;
84
+ function excerpt(text) {
85
+ const flat = text.replace(/\s+/g, ' ').trim();
86
+ return flat.length > EVIDENCE_MAX_CHARS ? `${flat.slice(0, EVIDENCE_MAX_CHARS)}…` : flat;
87
+ }
88
+ /**
89
+ * `stdout` as a plain JSON object, or `null` when it is not one.
90
+ *
91
+ * `JSON.parse` accepts `0`, `"str"`, `[]`, `null` and `true`; arrays and `null`
92
+ * are also `object` to `typeof`. All are rejected here so no downstream
93
+ * truthiness check can mistake `[]` for a verified identity.
94
+ */
95
+ function parseIdentityRecord(stdout) {
96
+ let parsed;
97
+ try {
98
+ parsed = JSON.parse(stdout);
99
+ }
100
+ catch {
101
+ return null;
102
+ }
103
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed))
104
+ return null;
105
+ return parsed;
106
+ }
107
+ /**
108
+ * Pure classifier: probe result -> verdict. Total — never throws, for any input.
109
+ *
110
+ * Non-object JSON (`0`, `"str"`, `[]`, `null`, `true`) is `unparseable`, not
111
+ * `ok` and not a crash: `JSON.parse` accepts all of them, so a naive truthiness
112
+ * check would let `[]` through as a verified identity.
113
+ */
114
+ function classifyIdentityProbe(probe, expected = exports.EXPECTED_PACKAGE_NAME) {
115
+ if (probe.spawnFailed || probe.timedOut) {
116
+ return {
117
+ reason: 'probe_failed',
118
+ expected,
119
+ detail: probe.timedOut ? 'identity probe timed out' : 'identity probe could not be spawned',
120
+ };
121
+ }
122
+ // PARSE BEFORE CONSULTING THE EXIT CODE (#3841).
123
+ //
124
+ // The exit-code rule below is right for its stated reason -- the predecessor
125
+ // prints a usage screen and exits 1, and that is "something else answered",
126
+ // not a parse problem -- but it used to run FIRST, so it also caught a tool
127
+ // that had already proved its identity and merely exited non-zero afterwards.
128
+ // The shell preamble this module speaks for reads stdout ONLY (its command
129
+ // substitution discards the status), so the two surfaces disagreed on exactly
130
+ // that input: shell `ok`, classifier `no_identity_verb`. Measured against the
131
+ // real snippet, not inferred. Since this classifier is the engine for the
132
+ // announced hard-fail phase, such an install would have flipped from verified
133
+ // to refused at rollout, in the phase where that stops the run.
134
+ //
135
+ // The predecessor defence is unaffected: a usage screen yields no usable
136
+ // payload, so it still falls through to the exit-code branch below.
137
+ const record = parseIdentityRecord(probe.stdout);
138
+ if (record !== null) {
139
+ const actual = record.packageName;
140
+ if (typeof actual === 'string' && actual.length > 0) {
141
+ // Unknown keys are ignored so a future payload addition cannot fail an
142
+ // older check. The shell anchor is deliberately open in the middle for the
143
+ // same reason, and the parity suite pins both halves of that (case P10).
144
+ const version = typeof record.version === 'string' ? record.version : undefined;
145
+ if (actual !== expected)
146
+ return { reason: 'identity_mismatch', expected, actual, version };
147
+ // ANCHOR PARITY (#3841). The shell preamble cannot parse JSON: it matches
148
+ // a `case` pattern anchored at the START of stdout, so a payload only
149
+ // verifies there when it begins at the first byte and serializes
150
+ // `packageName` first. A purely structural parse would accept
151
+ // `{"note":"x","packageName":"<us>"}` and a leading-whitespace payload
152
+ // that the shell rejects -- a FAIL-OPEN disagreement between the two
153
+ // surfaces.
154
+ //
155
+ // Reproduce those two properties SEMANTICALLY rather than byte-matching
156
+ // IDENTITY_RAW_PREFIX. The prefix describes the `--raw` wire format, but
157
+ // this classifier is also handed the DEFAULT pretty serialization
158
+ // (`runtime-identity` with no `--raw`, which is two-space indented) --
159
+ // byte-matching the compact prefix rejected that, which the remote matrix
160
+ // caught. `startsWith('{')` and a first-key check hold for both.
161
+ const firstKey = Object.keys(record)[0];
162
+ if (!probe.stdout.startsWith('{') || firstKey !== 'packageName') {
163
+ return {
164
+ reason: 'unparseable',
165
+ expected,
166
+ detail: 'payload names this package but is not in the anchored wire shape',
167
+ };
168
+ }
169
+ return { reason: 'ok', expected, actual, version };
170
+ }
171
+ }
172
+ // Nothing was proved. NOW the exit status is the discriminator: a non-zero
173
+ // exit means a binary that does not implement this verb answered.
174
+ if (probe.exitCode !== 0) {
175
+ return {
176
+ reason: 'no_identity_verb',
177
+ expected,
178
+ detail: `exit ${String(probe.exitCode)}: ${excerpt(probe.stdout)}`,
179
+ };
180
+ }
181
+ return {
182
+ reason: 'unparseable',
183
+ expected,
184
+ detail: record === null ? excerpt(probe.stdout) : 'payload has no usable packageName',
185
+ };
186
+ }
187
+ /**
188
+ * Collapse a five-way verdict onto the two-valued shell status.
189
+ *
190
+ * Total by construction: anything that is not `ok` is `unverified`. A future
191
+ * sixth reason code therefore fails CLOSED here rather than silently verifying.
192
+ */
193
+ function statusForVerdict(verdict) {
194
+ return verdict.reason === 'ok' ? exports.IDENTITY_STATUS.OK : exports.IDENTITY_STATUS.UNVERIFIED;
195
+ }
196
+ /**
197
+ * Build this runtime's identity payload.
198
+ *
199
+ * Version is REPORTED but not asserted in the warn phase, so a dev tree
200
+ * reporting readHostVersion()'s fail-closed `0.0.0` still verifies. Carrying it
201
+ * now means the hard-fail phase can gate on compatibility without a payload
202
+ * change (and therefore without a second round of Hyrum's-Law exposure).
203
+ */
204
+ function buildIdentityPayload(deps = {}) {
205
+ const readVersion = deps.readVersion ?? (() => (0, capability_loader_cjs_1.readHostVersion)());
206
+ const readName = deps.readPackageName ?? (() => exports.EXPECTED_PACKAGE_NAME);
207
+ return { packageName: readName(), version: readVersion() };
208
+ }
209
+ /** Render a verdict as the one-line actionable warning the preamble prints. */
210
+ function explainVerdict(verdict, resolvedPath) {
211
+ const head = `WARNING: "${resolvedPath}" did not report a ${verdict.expected} identity.`;
212
+ const why = {
213
+ ok: 'identity verified',
214
+ identity_mismatch: `it reported "${verdict.actual ?? '(unknown)'}" instead`,
215
+ no_identity_verb: 'it does not implement the runtime-identity verb — either a different package, or a gsd-core predating the verb',
216
+ unparseable: 'its response could not be parsed as an identity payload',
217
+ probe_failed: 'the identity probe could not be run',
218
+ };
219
+ const tail = 'A workflow shipped by this package may be running against a different tool. ' +
220
+ 'See docs/how-to/diagnose-a-foreign-gsd-tools.md';
221
+ const evidence = verdict.detail ? ` (${verdict.detail})` : '';
222
+ return `${head} Reason: ${why[verdict.reason]}${evidence}. ${tail}`;
223
+ }
224
+ /**
225
+ * CLI arm for `gsd-tools runtime-identity`.
226
+ *
227
+ * Kept on the CJS fast path for the same reason as `current-timestamp`: it is a
228
+ * pure local read, and the launcher preamble spawns it once per workflow run,
229
+ * so SDK bridge startup would be a per-run tax on every workflow.
230
+ */
231
+ function cmdRuntimeIdentity(raw = false, deps = {}) {
232
+ const payload = buildIdentityPayload(deps);
233
+ process.stdout.write(`${JSON.stringify(payload, null, raw ? 0 : 2)}\n`);
234
+ }
@@ -193,7 +193,9 @@ exports.NO_LOCAL_CONFIG_DIR_SENTINEL = '(no-local-config-dir)';
193
193
  * Pure runtime-identity projection. Relocated from `bin/install.js` per
194
194
  * ADR-1508 (epic #1507, #1510 Phase 1) so the Runtime Artifact Conversion
195
195
  * Module's rewrite engine can consume it without importing the installer.
196
- * `bin/install.js` re-exports this same function for back-compat.
196
+ * Consumers import it from here. `bin/install.js` used to re-export it for
197
+ * back-compat; that re-export was retired in #2876, once an audit found the
198
+ * compatibility spine had no production consumer at all.
197
199
  */
198
200
  function getDirName(runtime) {
199
201
  if (!runtime)
@@ -29,6 +29,10 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
29
29
  };
30
30
  Object.defineProperty(exports, "__esModule", { value: true });
31
31
  exports.formatGsdSlash = formatGsdSlash;
32
+ exports.readInstallRuntimeMarker = readInstallRuntimeMarker;
33
+ exports._setInstallRuntimeMarkerForTests = _setInstallRuntimeMarkerForTests;
34
+ exports._resetInstallRuntimeMarkerCacheForTests = _resetInstallRuntimeMarkerCacheForTests;
35
+ exports.resolveExplicitRuntime = resolveExplicitRuntime;
32
36
  exports.resolveRuntime = resolveRuntime;
33
37
  exports.formatGsdSlashFor = formatGsdSlashFor;
34
38
  const node_fs_1 = __importDefault(require("node:fs"));
@@ -74,20 +78,74 @@ function formatGsdSlash(commandName, runtime) {
74
78
  }
75
79
  return `/gsd-${token}${tail}`;
76
80
  }
81
+ // ─── #3897 rung 2: the per-install `.gsd-runtime` marker, promoted from ──────
82
+ // model-resolver.cts (#2297) to this module — the canonical owner of runtime
83
+ // identity (resolveRuntime, formatGsdSlash). `bin/install.js` writes
84
+ // `<install>/gsd-core/.gsd-runtime` beside VERSION for every runtime install
85
+ // (#2297). This module compiles to `gsd-core/bin/lib/runtime-slash.cjs`
86
+ // (ADR-457) — the SAME directory `model-resolver.cjs` compiles to — so the
87
+ // `__dirname`-relative path below is unchanged from the promoted original.
88
+ //
89
+ // Cache + test-seam shape, byte-for-behaviour preserved from
90
+ // model-resolver.cts:65-77 (N5): module-level `let`, `try/catch -> null`,
91
+ // `_setInstallRuntimeMarkerForTests` / `_resetInstallRuntimeMarkerCacheForTests`.
92
+ // `model-resolver.cts` now imports this implementation instead of holding its
93
+ // own copy; the two `hooks/*.js` marker readers delegate here too (through
94
+ // `ensureRuntimeBuild()`, per `scripts/lint-hooks-runtime-build-seam.cjs`).
95
+ let _installMarkerCache;
77
96
  /**
78
- * Resolve the effective runtime for a project directory.
97
+ * Read the per-install runtime marker, cached for the process lifetime.
98
+ * Never throws (N4) — an absent/unreadable marker (dev/source tree, an
99
+ * install predating #2297, EACCES, hostile content, ...) is "no signal",
100
+ * never a resolution failure; a throw here would break a session in
101
+ * `gsd-agent-isolation-guard.js`, which is worse than resolving the wrong
102
+ * runtime.
103
+ *
104
+ * The raw marker string is returned as-is (trimmed) — callers MUST route it
105
+ * through `resolveRuntimeNameFromCandidates` before trusting it (N1); this
106
+ * function itself does not normalize so its cached value matches exactly
107
+ * what #2297's tests already assert about the underlying file read.
108
+ */
109
+ function readInstallRuntimeMarker() {
110
+ if (_installMarkerCache !== undefined)
111
+ return _installMarkerCache;
112
+ try {
113
+ const markerPath = node_path_1.default.join(__dirname, '..', '..', '.gsd-runtime');
114
+ const raw = node_fs_1.default.readFileSync(markerPath, 'utf8').trim();
115
+ _installMarkerCache = raw || null;
116
+ }
117
+ catch {
118
+ // No marker: dev/source tree, or an install predating #2297. Fall through
119
+ // to the 'claude' default (keeps tier aliases — never worse than the bug).
120
+ _installMarkerCache = null;
121
+ }
122
+ return _installMarkerCache;
123
+ }
124
+ // Test seams for the install-marker rung (the dev/source tree has no marker,
125
+ // so the file read always bottoms out at 'claude' — these let tests exercise
126
+ // the third precedence rung and reset the module-level cache between cases).
127
+ function _setInstallRuntimeMarkerForTests(value) {
128
+ _installMarkerCache = value;
129
+ }
130
+ function _resetInstallRuntimeMarkerCacheForTests() {
131
+ _installMarkerCache = undefined;
132
+ }
133
+ /**
134
+ * Resolve the explicit runtime for a project directory, from the two
135
+ * explicit sources only — no default is applied.
79
136
  *
80
- * process.env.GSD_RUNTIME > config.runtime > 'claude'
137
+ * env.GSD_RUNTIME > config.runtime > null
81
138
  *
82
- * Mirrors the precedence already used by profile-output.cjs and the rest of
83
- * the runtime resolution chain. Returns a lowercased string so downstream
84
- * comparisons can be case-blind.
139
+ * Returns null when neither explicit source is set, so a caller can
140
+ * distinguish "config said claude" from "nothing was set" (needed by
141
+ * #3245's host-detection rung, which must only run when this returns null).
85
142
  *
86
143
  * @param projectDir - path to the project directory, or null/undefined
87
- * @returns the resolved runtime name
144
+ * @param env - environment variables to read GSD_RUNTIME from; defaults to process.env
145
+ * @returns the resolved runtime name, or null if neither source is set
88
146
  */
89
- function resolveRuntime(projectDir) {
90
- const envRuntime = (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(process.env['GSD_RUNTIME']);
147
+ function resolveExplicitRuntime(projectDir, env = process.env) {
148
+ const envRuntime = (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(env['GSD_RUNTIME']);
91
149
  if (envRuntime)
92
150
  return envRuntime;
93
151
  if (projectDir) {
@@ -113,6 +171,36 @@ function resolveRuntime(projectDir) {
113
171
  // runtime output formatting.
114
172
  }
115
173
  }
174
+ return null;
175
+ }
176
+ /**
177
+ * Resolve the effective runtime for a project directory.
178
+ *
179
+ * process.env.GSD_RUNTIME > config.runtime > install marker > 'claude'
180
+ *
181
+ * Mirrors the precedence already used by profile-output.cjs and the rest of
182
+ * the runtime resolution chain. Returns a lowercased string so downstream
183
+ * comparisons can be case-blind.
184
+ *
185
+ * #3897 rung 2: the per-install `.gsd-runtime` marker is the THIRD rung,
186
+ * between the two explicit sources and the 'claude' default — matching
187
+ * model-resolver.cts's own description of it as "the third precedence rung"
188
+ * before this reader was promoted here. The marker's raw contents are never
189
+ * trusted verbatim (N1): they are routed through the SAME
190
+ * `resolveRuntimeNameFromCandidates` normalization the env rung uses, so an
191
+ * unknown or hostile marker value degrades exactly like an unknown
192
+ * GSD_RUNTIME value would.
193
+ *
194
+ * @param projectDir - path to the project directory, or null/undefined
195
+ * @returns the resolved runtime name
196
+ */
197
+ function resolveRuntime(projectDir) {
198
+ const explicit = resolveExplicitRuntime(projectDir);
199
+ if (explicit)
200
+ return explicit;
201
+ const markerRuntime = (0, runtime_name_policy_cjs_1.resolveRuntimeNameFromCandidates)(readInstallRuntimeMarker());
202
+ if (markerRuntime)
203
+ return markerRuntime;
116
204
  return 'claude';
117
205
  }
118
206
  /**
@@ -29,6 +29,7 @@ exports.requireSafePath = requireSafePath;
29
29
  exports.scanForInjection = scanForInjection;
30
30
  exports.sanitizeForPrompt = sanitizeForPrompt;
31
31
  exports.sanitizeForDisplay = sanitizeForDisplay;
32
+ exports.sanitizeLabel = sanitizeLabel;
32
33
  exports.validateShellArg = validateShellArg;
33
34
  exports.safeJsonParse = safeJsonParse;
34
35
  exports.validatePhaseNumber = validatePhaseNumber;
@@ -73,13 +74,52 @@ function validatePath(filePath, baseDir, opts = {}) {
73
74
  resolvedPath = node_fs_1.default.realpathSync(resolvedPath);
74
75
  }
75
76
  catch {
76
- const parentDir = node_path_1.default.dirname(resolvedPath);
77
+ // realpathSync failed — either resolvedPath doesn't exist at all, or it's
78
+ // a dangling symlink (the link itself exists but its target doesn't).
79
+ // lstat (unlike stat/realpath) stats the link itself and does NOT follow
80
+ // it, so it succeeds for a dangling symlink and throws ENOENT for a
81
+ // genuinely absent path. That's the discriminator: without it, a dangling
82
+ // symlink to a non-existent OUTSIDE path would fall through to the
83
+ // parent-resolution fallback below and be re-accepted as an in-project
84
+ // path, while a symlink to an EXISTING outside path is correctly
85
+ // rejected via the realpathSync success branch above — a state
86
+ // difference an attacker can use as an existence oracle for arbitrary
87
+ // absolute paths.
77
88
  try {
78
- const realParent = node_fs_1.default.realpathSync(parentDir);
79
- resolvedPath = node_path_1.default.join(realParent, node_path_1.default.basename(resolvedPath));
89
+ if (node_fs_1.default.lstatSync(resolvedPath).isSymbolicLink()) {
90
+ return { safe: false, resolved: '', error: 'Path is an unresolvable symbolic link' };
91
+ }
80
92
  }
81
93
  catch {
82
- // Parent doesn't exist either — keep the resolved path as-is
94
+ // lstat also threw — resolvedPath (and its would-be link) genuinely
95
+ // doesn't exist. Fall through to ancestor resolution below.
96
+ }
97
+ // Walk up to the nearest ancestor that exists and realpath THAT, then
98
+ // re-append the remaining (not-yet-created) segments. This canonicalizes
99
+ // resolvedPath the same way resolvedBase was canonicalized above,
100
+ // regardless of how many leading directories are missing — a single
101
+ // parent-only check would leave resolvedPath un-canonicalized whenever
102
+ // the parent is also missing, which breaks the startsWith comparison
103
+ // below on any non-canonical cwd (e.g. macOS /var/... vs
104
+ // /private/var/...).
105
+ let ancestor = node_path_1.default.dirname(resolvedPath);
106
+ const remainder = [node_path_1.default.basename(resolvedPath)];
107
+ for (;;) {
108
+ try {
109
+ const realAncestor = node_fs_1.default.realpathSync(ancestor);
110
+ resolvedPath = node_path_1.default.join(realAncestor, ...remainder);
111
+ break;
112
+ }
113
+ catch {
114
+ const parent = node_path_1.default.dirname(ancestor);
115
+ if (parent === ancestor) {
116
+ // Reached filesystem root without finding an existing ancestor —
117
+ // keep resolvedPath as-is.
118
+ break;
119
+ }
120
+ remainder.unshift(node_path_1.default.basename(ancestor));
121
+ ancestor = parent;
122
+ }
83
123
  }
84
124
  }
85
125
  const normalizedBase = resolvedBase + node_path_1.default.sep;
@@ -193,7 +233,7 @@ exports.INJECTION_PATTERNS = [
193
233
  /override\s+(system|previous)\s+(prompt|instructions)/i,
194
234
  // Role/identity manipulation
195
235
  /you\s+are\s+now\s+(?:a|an|the)\s+/i,
196
- /act\s+as\s+(?:a|an|the)\s+(?!plan|phase|wave)/i,
236
+ /\bact\s+as\s+(?:a|an|the)\s+(?!plan|phase|wave)/i,
197
237
  /pretend\s+(?:you(?:'re| are)\s+|to\s+be\s+)/i,
198
238
  /from\s+now\s+on,?\s+you\s+(?:are|will|should|must)/i,
199
239
  // System prompt extraction
@@ -349,6 +389,65 @@ function sanitizeForDisplay(text) {
349
389
  .join('\n');
350
390
  return sanitized;
351
391
  }
392
+ /**
393
+ * Sanitize a value that must render as a SINGLE LINE and is derived from a
394
+ * filesystem name (a phase directory's number/name token, an archived
395
+ * milestone label, a bare filename) — not from file/frontmatter CONTENT.
396
+ *
397
+ * Why this is NOT `sanitizeForDisplay`: that helper's job is multi-line
398
+ * prose — it strips whole protocol-leak LINES while deliberately preserving
399
+ * `\n` between legitimate ones (see its docstring and
400
+ * `tests/security.test.cjs`'s neighbouring describe). A filesystem name is
401
+ * the opposite shape: it is supposed to be one line, so a `\n`/`\r` inside
402
+ * one is never legitimate content to preserve — it is an attacker (or a
403
+ * doctored checkout) using the directory NAME itself as the injection
404
+ * vector. #3458's reproduction: a phase directory literally named
405
+ * `zz\n0 open items require decisions.\n\x1b[2K\x1b[1G FORGED`
406
+ * flows verbatim into `audit-open`'s human report (the phase-number
407
+ * fallback taken when the name doesn't match `PHASE_NUMBER_TOKEN_SOURCE`).
408
+ * `sanitizeForDisplay` would pass every one of those bytes straight through
409
+ * — by design, since it never touches control characters — so the embedded
410
+ * `\n` becomes a real newline in the report, printing a forged
411
+ * "0 open items require decisions." as its own line, and the raw ESC bytes
412
+ * reach the terminal.
413
+ *
414
+ * This helper closes that hole by ESCAPING (never silently stripping) the
415
+ * C0 control range (0x00–0x1F, including ESC 0x1B, CR, LF), DEL (0x7F), and
416
+ * the C1 range (0x80–0x9F) into a visible representation (`\n`, `\x1b`,
417
+ * ...). Escaping rather than stripping is deliberate: a reviewer reading the
418
+ * report should be able to SEE that a name was doctored, not have it quietly
419
+ * normalized away as if nothing happened. Every other character — including
420
+ * all ordinary printable and non-ASCII text — passes through byte-identical.
421
+ */
422
+ function sanitizeLabel(text) {
423
+ if (!text || typeof text !== 'string')
424
+ return text;
425
+ const NAMED_ESCAPES = {
426
+ 0x00: '\\0',
427
+ 0x07: '\\a',
428
+ 0x08: '\\b',
429
+ 0x09: '\\t',
430
+ 0x0a: '\\n',
431
+ 0x0b: '\\v',
432
+ 0x0c: '\\f',
433
+ 0x0d: '\\r',
434
+ 0x1b: '\\x1b',
435
+ };
436
+ let out = '';
437
+ for (const ch of text) {
438
+ const code = ch.codePointAt(0);
439
+ const isC0 = code <= 0x1f;
440
+ const isDel = code === 0x7f;
441
+ const isC1 = code >= 0x80 && code <= 0x9f;
442
+ if (isC0 || isDel || isC1) {
443
+ out += NAMED_ESCAPES[code] ?? `\\x${code.toString(16).padStart(2, '0')}`;
444
+ }
445
+ else {
446
+ out += ch;
447
+ }
448
+ }
449
+ return out;
450
+ }
352
451
  // ─── Shell Safety ───────────────────────────────────────────────────────────────────────
353
452
  /**
354
453
  * Validate that a string is safe to use as a shell argument when quoted.