@opengsd/gsd-core 1.12.0 → 1.14.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 (455) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -40,7 +40,7 @@ const { resolveCapabilityRuntimeState } = capabilityStateModule;
40
40
  // ─── Capability-activation engine (single owner for config-key precedence) ────
41
41
  // eslint-disable-next-line @typescript-eslint/no-require-imports
42
42
  const capabilityActivationModule = require("./capability-activation.cjs");
43
- const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, resolveConfigKey } = capabilityActivationModule;
43
+ const { _getNestedConfigValue, _readRawConfigKey, _resolveActivationValue, _resolvePointGate, resolveConfigKey } = capabilityActivationModule;
44
44
  // ─── Canonical points (derived from LOOP_HOST_CONTRACT — authoritative 12) ───
45
45
  // FIX 2: Derive the authoritative canonical set from LOOP_HOST_CONTRACT so it
46
46
  // cannot drift from the host contract. CANONICAL_POINTS_FALLBACK is kept as an
@@ -125,13 +125,15 @@ function resolveLoopHooks(input) {
125
125
  // Helper: check activation using single-key precedence resolver (FIX 1 + FIX 3)
126
126
  function isActive(hook) {
127
127
  const when = hook['when'];
128
- // No `when` → unconditional hook, always active
129
- if (when === undefined || when === null)
130
- return true;
131
- // FIX 3: `when` present but not a non-empty string → malformed registry data → INACTIVE
132
- if (typeof when !== 'string' || when.length === 0)
133
- return false;
134
- return _resolveActivationValue(when, config, cwd, registry);
128
+ if (when !== undefined && when !== null) {
129
+ // FIX 3: `when` present but not a non-empty string → malformed registry data → INACTIVE
130
+ if (typeof when !== 'string' || when.length === 0)
131
+ return false;
132
+ if (!_resolveActivationValue(when, config, cwd, registry))
133
+ return false;
134
+ }
135
+ // #3661: optional point-selection gate — see capability-activation.cts.
136
+ return _resolvePointGate(hook['pointFrom'], point, config, cwd, registry);
135
137
  }
136
138
  function isCapabilityActive(capId) {
137
139
  if (!capabilityStatesById)
@@ -222,6 +224,9 @@ function resolveLoopHooks(input) {
222
224
  active.consumes = consumes;
223
225
  if (onError !== undefined)
224
226
  active.onError = onError;
227
+ // #4209 DISP-02: only a literal `true` projects; absent/false stay inert.
228
+ if (hook['supportsReviewerLanes'] === true)
229
+ active.supportsReviewerLanes = true;
225
230
  activeHooks.push(active);
226
231
  }
227
232
  // Process contributions
@@ -420,17 +425,20 @@ function sanitizeLoadFailReason(reason) {
420
425
  .slice(0, 300);
421
426
  return cleaned || '(no reason given)';
422
427
  }
423
- function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
424
- if (!point) {
425
- coreError('loop render-hooks requires a <point> argument. Valid points: ' + CANONICAL_POINTS.join(', '));
426
- return;
427
- }
428
- // --active-cap <capId> mode: emit 'true' or 'false' only (scanner-safe, no JSON envelope)
429
- const activeCapId = typeof options['activeCap'] === 'string' ? options['activeCap'] : undefined;
430
- if (activeCapId !== undefined && activeCapId === '') {
431
- coreError('--active-cap requires a <capId> value (e.g. --active-cap tdd)');
432
- return;
433
- }
428
+ /**
429
+ * The full config/registry/capability-state resolution `cmdLoopRenderHooks` performs, minus its
430
+ * CLI-only output formatting — extracted so an in-process caller (e.g. `review-lane dispatch-step`
431
+ * self-verifying a `supportsReviewerLanes` trait) can reach the SAME resolution `gsd_run loop
432
+ * render-hooks <point> --raw` would give it, without spawning a subprocess and re-parsing its
433
+ * stdout (which was subject to `io.cjs`'s `@file:` overflow protocol on the rendered-string
434
+ * envelope — a bug class this in-process call cannot hit, since it never touches that envelope
435
+ * or its rendering at all).
436
+ *
437
+ * Throws on an invalid `point` (mirrors `resolveLoopHooks`); callers convert to their own error
438
+ * channel. Emits the same loud stderr load-failure warnings `cmdLoopRenderHooks` always has,
439
+ * regardless of caller — a skipped gate must never be silently invisible.
440
+ */
441
+ function resolveActiveHooksForPoint(cwd, point, options = {}) {
434
442
  const runtimeConfigDir = typeof options['configDir'] === 'string'
435
443
  ? options['configDir']
436
444
  : undefined;
@@ -466,15 +474,7 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
466
474
  for (const cap of state.capabilities || []) {
467
475
  capabilityStatesById.set(cap.id, cap);
468
476
  }
469
- let resolved;
470
- try {
471
- resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
472
- }
473
- catch (err) {
474
- const msg = (err instanceof Error) ? err.message : String(err);
475
- coreError(msg);
476
- return;
477
- }
477
+ const resolved = resolveLoopHooks({ point, registry, config, cwd, capabilityStatesById });
478
478
  // ── ADR-1244 D2: load-failed capability gates FAIL OPEN with a loud warning ────
479
479
  // Decision (#2009): a capability that failed to LOAD must not block the loop.
480
480
  // The prior behavior injected a BLOCKING synthetic gate (blocking:true,
@@ -523,24 +523,44 @@ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
523
523
  for (const w of loadFailWarnings) {
524
524
  process.stderr.write(`gsd: warning — ${w}\n`);
525
525
  }
526
- // --active-cap mode: print exactly 'true' or 'false' with no envelope
526
+ // Surface capability-state warnings and the #2009 load-failure fail-open warnings together
527
+ // (in addition to the stderr emission above, which is the channel host workflows actually see).
528
+ const combinedWarnings = [...(state.warnings || []), ...loadFailWarnings];
529
+ return { point: resolved.point, activeHooks: resolved.activeHooks, warnings: combinedWarnings };
530
+ }
531
+ function cmdLoopRenderHooks(cwd, point, raw, options = {}) {
532
+ if (!point) {
533
+ coreError('loop render-hooks requires a <point> argument. Valid points: ' + CANONICAL_POINTS.join(', '));
534
+ return;
535
+ }
536
+ // --active-cap <capId> mode: emit 'true' or 'false' only (scanner-safe, no JSON envelope)
537
+ const activeCapId = typeof options['activeCap'] === 'string' ? options['activeCap'] : undefined;
538
+ if (activeCapId !== undefined && activeCapId === '') {
539
+ coreError('--active-cap requires a <capId> value (e.g. --active-cap tdd)');
540
+ return;
541
+ }
542
+ let result;
543
+ try {
544
+ result = resolveActiveHooksForPoint(cwd, point, options);
545
+ }
546
+ catch (err) {
547
+ const msg = (err instanceof Error) ? err.message : String(err);
548
+ coreError(msg);
549
+ return;
550
+ }
527
551
  if (activeCapId !== undefined) {
528
- const isActive = resolved.activeHooks.some((h) => h.capId === activeCapId);
552
+ const isActive = result.activeHooks.some((h) => h.capId === activeCapId);
529
553
  process.stdout.write(isActive ? 'true\n' : 'false\n');
530
554
  return;
531
555
  }
532
- const rendered = renderLoopHooks(resolved);
556
+ const rendered = renderLoopHooks({ point: result.point, activeHooks: result.activeHooks });
533
557
  const envelope = {
534
- point: resolved.point,
535
- activeHooks: resolved.activeHooks,
558
+ point: result.point,
559
+ activeHooks: result.activeHooks,
536
560
  rendered,
537
561
  };
538
- // Surface capability-state warnings and the #2009 load-failure fail-open
539
- // warnings together in the structured `warnings` channel (in addition to the
540
- // stderr emission above, which is the channel host workflows actually see).
541
- const combinedWarnings = [...(state.warnings || []), ...loadFailWarnings];
542
- if (combinedWarnings.length > 0) {
543
- envelope.warnings = combinedWarnings;
562
+ if (result.warnings.length > 0) {
563
+ envelope.warnings = result.warnings;
544
564
  }
545
565
  coreOutput(envelope, raw);
546
566
  }
@@ -548,6 +568,7 @@ module.exports = {
548
568
  resolveLoopHooks,
549
569
  renderLoopHooks,
550
570
  cmdLoopRenderHooks,
571
+ resolveActiveHooksForPoint,
551
572
  // Exported for tests
552
573
  _getNestedConfigValue,
553
574
  _resolveActivationValue,
@@ -555,6 +576,10 @@ module.exports = {
555
576
  // Re-exported for identity parity guard (FIX 2: resolveConfigValues in this module
556
577
  // calls resolveConfigKey; exporting it here makes the single-owner contract testable).
557
578
  resolveConfigKey,
579
+ // #3661: re-exported for the same identity parity guard — isActive calls
580
+ // _resolvePointGate; exporting it here makes the single-owner contract testable
581
+ // (see tests/capability-precedence-parity.test.cjs's identity guard describe block).
582
+ _resolvePointGate,
558
583
  CANONICAL_POINTS_FALLBACK,
559
584
  CANONICAL_POINTS,
560
585
  };
@@ -23,6 +23,7 @@ exports.findTableBySchema = findTableBySchema;
23
23
  exports.findTableWithColumns = findTableWithColumns;
24
24
  exports.escapeCell = escapeCell;
25
25
  exports.appendQuickTaskRow = appendQuickTaskRow;
26
+ exports.migrateQuickTasksTable = migrateQuickTasksTable;
26
27
  exports.resetQuickTaskRows = resetQuickTaskRows;
27
28
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
28
29
  // ─── Schema registry ──────────────────────────────────────────────────────────
@@ -776,6 +777,128 @@ function appendQuickTaskRow(stateContent, fields) {
776
777
  const content = (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody);
777
778
  return { ok: true, value: { content, row, variant: match.label } };
778
779
  }
780
+ // ─── migrateQuickTasksTable (#3730 option b) ───────────────────────────────
781
+ /**
782
+ * Column-name → canonical-cell mapping for migrating a legacy Quick Tasks table
783
+ * (#3730). Name-based, case-insensitive; every column NOT in this map lands in
784
+ * the Description bucket (joined with ' · ' in original column order, unknown
785
+ * names kept as `name: value`) so no historical datum is dropped.
786
+ */
787
+ const QUICK_TASKS_COLUMN_ALIASES = {
788
+ '#': '#', id: '#',
789
+ date: 'Date', when: 'Date',
790
+ commit: 'Commit', sha: 'Commit',
791
+ status: 'Status',
792
+ directory: 'Directory', artifacts: 'Directory', path: 'Directory', dir: 'Directory',
793
+ description: 'Description', task: 'Description', summary: 'Description',
794
+ slug: 'Description', scope: 'Description', title: 'Description',
795
+ };
796
+ /**
797
+ * Migrate STATE.md's "Quick Tasks Completed" table onto the canonical
798
+ * `with-status` schema (#3730, maintainer decision 2026-09-02: option b).
799
+ *
800
+ * The pre-registry prose template licensed arbitrary column shapes GSD itself
801
+ * emitted, and `appendQuickTaskRow` fails loud on every one of them — leaving a
802
+ * project permanently unappendable. This is the supported repair path: the
803
+ * `gsd-tools quick-tasks-migrate` subcommand, plus an automatic check on the
804
+ * first `quick`/`fast` run (the workflow calls this before appending), which is
805
+ * a silent no-op when the section is absent, the table is already canonical, or
806
+ * the user never runs quick at all.
807
+ *
808
+ * Same section-selection, parse, and fail-loud posture as `appendQuickTaskRow`
809
+ * (ADR-2143 §7 upheld — append still rejects; this REPAIRS). Returns
810
+ * `{ok:true, value:{content, migrated:false}}` for the no-op cases so callers
811
+ * can stay silent, and `{migrated:true, from, rows}` when a rewrite happened so
812
+ * the CLI can report exactly what moved.
813
+ */
814
+ function migrateQuickTasksTable(stateContent) {
815
+ const section = selectQuickTasksSection(stateContent);
816
+ if (!section) {
817
+ return { ok: true, value: { content: stateContent, migrated: false } };
818
+ }
819
+ const parsed = parseMarkdownTable(section.body);
820
+ if (!parsed.ok) {
821
+ return { ok: false, reason: `quick-tasks table: ${parsed.reason}` };
822
+ }
823
+ const match = matchTableSchema(parsed.value.columns);
824
+ if (match && match.id === 'QuickTasks') {
825
+ return { ok: true, value: { content: stateContent, migrated: false } };
826
+ }
827
+ const canonical = exports.TABLE_SCHEMAS.QuickTasks.find((v) => v.label === 'with-status').columns;
828
+ // Map each legacy column to its canonical target; unmapped names keep their
829
+ // identity so the Description bucket can render them losslessly.
830
+ const targets = parsed.value.columns.map((c) => QUICK_TASKS_COLUMN_ALIASES[c.trim().toLowerCase()] ?? null);
831
+ const rows = parsed.value.rows.map((row, rowIdx) => {
832
+ const bucket = {
833
+ '#': '', Description: '', Date: '', Commit: '', Status: '', Directory: '',
834
+ };
835
+ const descriptionParts = [];
836
+ // parseMarkdownTable hands back name-keyed records (rows[i][columnName]).
837
+ parsed.value.columns.forEach((col, i) => {
838
+ const raw = (row[col] ?? '').trim();
839
+ const target = targets[i];
840
+ if (target === 'Description') {
841
+ if (raw)
842
+ descriptionParts.push(raw);
843
+ }
844
+ else if (target) {
845
+ // Collision-safe (#3730 review): two legacy columns aliasing the same
846
+ // canonical target must not silently overwrite — the displaced value
847
+ // joins the Description bucket under its own name, same convention as
848
+ // an unknown column, so no historical datum is dropped.
849
+ if (raw && bucket[target]) {
850
+ descriptionParts.push(`${col.trim()}: ${raw}`);
851
+ }
852
+ else if (raw) {
853
+ bucket[target] = raw;
854
+ }
855
+ }
856
+ else if (raw) {
857
+ descriptionParts.push(`${col.trim()}: ${raw}`);
858
+ }
859
+ });
860
+ bucket['#'] = bucket['#'] || String(rowIdx + 1);
861
+ bucket.Description = descriptionParts.join(' · ');
862
+ bucket.Date = bucket.Date || '—';
863
+ bucket.Commit = bucket.Commit || '—';
864
+ bucket.Status = bucket.Status || '—';
865
+ bucket.Directory = bucket.Directory || '—';
866
+ return `| ${canonical.map((c) => escapeCell(bucket[c])).join(' | ')} |`;
867
+ });
868
+ const eol = /\r\n/.test(section.body) ? '\r\n' : '\n';
869
+ const lines = section.body.split(/\r?\n/);
870
+ // CONTIGUOUS-run bound (#3730 review), mirroring resetQuickTaskRows: the
871
+ // section body may carry a SECOND table (a `#### Notes` subsection or a
872
+ // trailing table after a blank line) that parseMarkdownTable never read —
873
+ // scanning for the last pipe line anywhere in the body would splice the
874
+ // rewrite across it and silently destroy it. Only the first CONTIGUOUS run
875
+ // of pipe lines — the table that was parsed — is replaced.
876
+ const firstTableLineIdx = lines.findIndex((l) => l.trim().startsWith('|'));
877
+ let lastTableLineIdx = firstTableLineIdx;
878
+ while (lastTableLineIdx + 1 < lines.length && lines[lastTableLineIdx + 1].trim().startsWith('|')) {
879
+ lastTableLineIdx++;
880
+ }
881
+ const header = `| ${canonical.join(' | ')} |`;
882
+ // Widths match workflows/quick.md's canonical template byte-for-byte
883
+ // (#3730 review): its delimiter is max(3, len+2) per column.
884
+ const delimiter = `| ${canonical.map((c) => '-'.repeat(Math.max(3, c.length + 2))).join(' | ')} |`;
885
+ const newBody = [
886
+ ...lines.slice(0, firstTableLineIdx),
887
+ header,
888
+ delimiter,
889
+ ...rows,
890
+ ...lines.slice(lastTableLineIdx + 1),
891
+ ].join(eol);
892
+ return {
893
+ ok: true,
894
+ value: {
895
+ content: (0, markdown_sectionizer_cjs_1.replaceSection)(stateContent, section, newBody),
896
+ migrated: true,
897
+ from: parsed.value.columns,
898
+ rows: rows.length,
899
+ },
900
+ };
901
+ }
779
902
  // ─── resetQuickTaskRows (#2142) ────────────────────────────────────────────
780
903
  /**
781
904
  * Clear every DATA row from STATE.md's "Quick Tasks Completed" table, leaving
@@ -402,8 +402,8 @@ function classifyUnindexedUri(uri) {
402
402
  }
403
403
  /** Second, independent gate (design "Hostile inputs" gate 2): re-validate an INDEXED entry's relPath against the catalog root, catching a symlink planted after the index was built. */
404
404
  function readIndexedResource(catalog, entry) {
405
- const check = (0, security_cjs_1.validatePath)(entry.relPath, catalog.root);
406
- if (!check.safe) {
405
+ const contained = (0, security_cjs_1.tryWithinRoot)(entry.relPath, catalog.root);
406
+ if (contained === null) {
407
407
  fail(exports.REASON.TRAVERSAL_REFUSED, `indexed resource escapes catalog root: ${entry.relPath}`);
408
408
  }
409
409
  let raw;
@@ -55,7 +55,7 @@ const { scanPhasePlans } = planScanMod;
55
55
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- phase-locator.cjs is an export= CommonJS module
56
56
  const phaseLocatorMod = require("./phase-locator.cjs");
57
57
  const { listMilestonePhaseDirs } = phaseLocatorMod;
58
- const { planningPaths } = planningWorkspace;
58
+ const { planningPaths, resolvePhaseIdConvention } = planningWorkspace;
59
59
  const { extractFrontmatter } = frontmatterMod;
60
60
  // ADR-3408 §8.3 / #3469: `writeStateMd` gets sync and NO preservation — the
61
61
  // same #3374-shaped exposure the milestone-complete write used to carry (a
@@ -702,6 +702,22 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
702
702
  let totalPlans = 0;
703
703
  let totalTasks = 0;
704
704
  const accomplishments = [];
705
+ // #2761 (round-11 BLOCKER): resolved ONCE, ambiently (no explicit `ws` —
706
+ // this function has none of its own and already resolves everything else
707
+ // off `cwd` via planningPaths(cwd), matching the ambient-workstream
708
+ // contract `resolvePhaseIdConvention`/`planningDir` share), and threaded
709
+ // into the single `listMilestonePhaseDirs` call directly below. Left
710
+ // unthreaded, `phaseIdConvention` stays `undefined` and
711
+ // `getMilestonePhaseFilter` silently resolves it FROM CONFIG itself —
712
+ // which is exactly the behavior the changeset originally (and wrongly)
713
+ // claimed this PR does not reach: on a bracket-convention project that
714
+ // lazily-resolved convention changes `milestonePhaseDirs`/
715
+ // `milestonePhaseScope` below, and this function goes on to ARCHIVE
716
+ // (rename/move) whatever that set names. Resolving explicitly here removes
717
+ // the silent-inherit path without changing behavior for `null` /
718
+ // `milestone-prefixed` projects, whose resolved convention is the same
719
+ // value `undefined` would have lazily produced anyway.
720
+ const phaseConvention = resolvePhaseIdConvention(cwd);
705
721
  // #3597 (ADR-3180 Decision 2): SINGLE resolution of "which phase
706
722
  // directories belong to the current milestone" AND the SCOPE discriminator
707
723
  // that resolution came from — shared verbatim by the read-only stats loop
@@ -715,7 +731,11 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
715
731
  // disk, same as before #3597. Only the destructive archive pass (and its
716
732
  // --dry-run preview) refuses to act on a non-COMPLETE (non-answer) scope;
717
733
  // see the guard built from `milestonePhaseScope` further down.
718
- const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, { cwd, versionOverride: version });
734
+ const { value: milestonePhaseDirs, scope: milestonePhaseScope } = listMilestonePhaseDirs(phasesDir, {
735
+ cwd,
736
+ versionOverride: version,
737
+ phaseIdConvention: phaseConvention,
738
+ });
719
739
  try {
720
740
  for (const dir of milestonePhaseDirs) {
721
741
  phaseCount++;
@@ -806,7 +826,7 @@ function cmdMilestoneComplete(cwd, version, options, raw) {
806
826
  // never disagree with what a real run actually archives. Absent
807
827
  // --archive-quick this stays `[]` and nothing on disk is touched either
808
828
  // way (dry-run always returns before any mutation below).
809
- const quickDirsToArchive = options.archiveQuick ? listQuickTaskDirsForArchive(cwd) : [];
829
+ const quickDirsToArchive = options.archiveQuick ? listQuickTaskDirsForArchive(cwd).map((d) => d.name) : [];
810
830
  const dryRunResult = {
811
831
  dry_run: true,
812
832
  version,
@@ -1462,6 +1482,16 @@ function writeQuickArchiveReadme(archiveQuickDir) {
1462
1482
  * written three times, and only the real-run copy applied `requireSafePath`,
1463
1483
  * so a dry-run preview could list a directory the real run would silently
1464
1484
  * skip).
1485
+ *
1486
+ * Returns the proven `ContainedPath` alongside each entry's bare `name`
1487
+ * (`no-unconfined-path-join`'s `discardedContainmentResult` arm — a bare
1488
+ * statement call to `requireSafePath` throws away the exact answer it just
1489
+ * computed). The two dry-run previews only need `name` for display;
1490
+ * `archiveQuickTaskDirectories` deliberately does NOT reuse `abs` for its
1491
+ * rename — it re-derives and re-validates independently as TOCTOU
1492
+ * defense-in-depth (see its own comment), so `abs` exists here only to make
1493
+ * this function's own discard explicit, not to be trusted downstream as a
1494
+ * stale-safe proof.
1465
1495
  */
1466
1496
  function listQuickTaskDirsForArchive(cwd) {
1467
1497
  const planningBase = planningPaths(cwd).planning;
@@ -1474,19 +1504,20 @@ function listQuickTaskDirsForArchive(cwd) {
1474
1504
  // .planning/quick absent or unreadable — nothing to select.
1475
1505
  return [];
1476
1506
  }
1477
- const names = [];
1507
+ const results = [];
1478
1508
  for (const entry of sourceEntries) {
1479
1509
  if (!entry.isDirectory())
1480
1510
  continue; // excludes symlinks too — see MAJOR 3 note above
1511
+ let abs;
1481
1512
  try {
1482
- (0, security_cjs_1.requireSafePath)(node_path_1.default.join(quickDir, entry.name), planningBase, 'quick task dir', { allowAbsolute: true });
1513
+ abs = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(quickDir, entry.name), planningBase, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1483
1514
  }
1484
1515
  catch {
1485
1516
  continue; // symlink/escape attempt — never a candidate, in preview OR real run
1486
1517
  }
1487
- names.push(entry.name);
1518
+ results.push({ name: entry.name, abs });
1488
1519
  }
1489
- return names.sort();
1520
+ return results.sort((a, b) => (a.name < b.name ? -1 : a.name > b.name ? 1 : 0));
1490
1521
  }
1491
1522
  /**
1492
1523
  * #2142: move each DIRECTORY entry under `.planning/quick/` into
@@ -1535,7 +1566,7 @@ function archiveQuickTaskDirectories(cwd, version) {
1535
1566
  // #2142 MAJOR 5 (review): dirNames is the SAME selection
1536
1567
  // `listQuickTaskDirsForArchive` hands to both dry-run previews — this is
1537
1568
  // the real run, so it cannot disagree with what a preview reported.
1538
- const dirNames = listQuickTaskDirsForArchive(cwd);
1569
+ const dirNames = listQuickTaskDirsForArchive(cwd).map((d) => d.name);
1539
1570
  if (dirNames.length === 0) {
1540
1571
  // Boundary 0 (#2142): zero (safe) directory entries (empty dir, only
1541
1572
  // stray files, or every entry excluded by the selection rule) must not
@@ -1556,7 +1587,7 @@ function archiveQuickTaskDirectories(cwd, version) {
1556
1587
  // rename are two separate filesystem observations, and an entry
1557
1588
  // that was a safe real directory at selection time could in theory
1558
1589
  // be swapped for a symlink before this loop reaches it.
1559
- safeSrc = (0, security_cjs_1.requireSafePath)(src, planningBase, 'quick task dir', { allowAbsolute: true });
1590
+ safeSrc = (0, security_cjs_1.requireSafePath)(src, planningBase, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1560
1591
  }
1561
1592
  catch {
1562
1593
  continue; // symlink/escape attempt — skip, not archived
@@ -1649,7 +1680,7 @@ function cmdQuickArchive(cwd, version, options, raw) {
1649
1680
  // `cmdMilestoneComplete`'s own dry-run preview and the real
1650
1681
  // `archiveQuickTaskDirectories` both use, so all three can never disagree.
1651
1682
  if (options.dryRun) {
1652
- const quickDirsToArchive = listQuickTaskDirsForArchive(cwd);
1683
+ const quickDirsToArchive = listQuickTaskDirsForArchive(cwd).map((d) => d.name);
1653
1684
  output({
1654
1685
  dry_run: true,
1655
1686
  version,
@@ -128,6 +128,67 @@ function _resolveRuntimeTier(config, tier) {
128
128
  overrides: config['model_profile_overrides'],
129
129
  });
130
130
  }
131
+ /**
132
+ * #4192 — Resolve the claude-runtime TIER OVERRIDE model for (config, tier).
133
+ *
134
+ * Step 3's runtime-aware resolution deliberately skips the claude runtime to
135
+ * preserve the alias-native posture (#1156/#2297): with no user override, the
136
+ * resolver must keep returning bare tier aliases, and the builtin claude tier
137
+ * map (`opus → claude-opus-4-8`, …) must never force full-ID emission on every
138
+ * default install. But `model_profile_overrides.<runtime>.<tier>` is a
139
+ * documented override point (docs/CONFIGURATION.md § Runtime-Aware Profiles)
140
+ * that `workflows/settings-advanced.md` actively writes for claude-runtime
141
+ * users — and #4192 Finding 1 measured the key inert on this runtime.
142
+ *
143
+ * This helper reads ONLY the user's override entry for the effective claude
144
+ * runtime and tier — never the builtin claude tier map — so an install with no
145
+ * override is byte-identical to before the fix. The runtime is resolved the
146
+ * same way steps 1-3 resolve it (config['runtime'], defaulting to 'claude'),
147
+ * NOT via resolveActiveRuntime (GSD_RUNTIME/marker): the value policy must
148
+ * key off the config the operator wrote, matching mapClaudeOverrideForRuntime.
149
+ *
150
+ * Value policy mirrors the model_overrides path (#2041/#4192): an override
151
+ * value that maps to a current tier alias collapses to that alias
152
+ * (byte-equivalent resolution, alias-form emission); anything else — a pinned
153
+ * older generation (`claude-opus-4-7`), a bare alias/tier repoint (`sonnet`),
154
+ * or a non-Claude vendor id (`openai/o3`) — is emitted verbatim as pinned.
155
+ * Malformed entries (no usable `model` string) return null so the caller falls
156
+ * through to normal alias resolution (ADR-443 D1: invalid values fall through).
157
+ */
158
+ function resolveClaudeTierOverrideModel(configRuntime, tier, overrides) {
159
+ if (!tier || tier === 'inherit')
160
+ return null;
161
+ const effectiveRuntime = configRuntime || 'claude';
162
+ if (effectiveRuntime !== 'claude')
163
+ return null; // non-claude runtimes resolve at step 3
164
+ const overridesMap = overrides;
165
+ if (!overridesMap || typeof overridesMap !== 'object')
166
+ return null;
167
+ // Own-property guards throughout: both levels are config-supplied plain
168
+ // objects, so a prototype-chain key ("constructor", "toString") must not
169
+ // resolve an inherited member instead of falling through (same hardening as
170
+ // every other config-keyed lookup in this module).
171
+ const runtimeEntry = Object.hasOwn(overridesMap, effectiveRuntime)
172
+ ? overridesMap[effectiveRuntime]
173
+ : undefined;
174
+ if (!runtimeEntry || typeof runtimeEntry !== 'object')
175
+ return null;
176
+ const userRaw = Object.hasOwn(runtimeEntry, tier) ? runtimeEntry[tier] : undefined;
177
+ if (userRaw === undefined || userRaw === null)
178
+ return null;
179
+ const entry = typeof userRaw === 'string'
180
+ ? { model: userRaw }
181
+ : userRaw;
182
+ if (!entry || typeof entry !== 'object')
183
+ return null;
184
+ const model = entry['model'];
185
+ if (typeof model !== 'string' || model.length === 0)
186
+ return null;
187
+ if (Object.hasOwn(CLAUDE_POLICY_ID_TO_ALIAS, model)) {
188
+ return CLAUDE_POLICY_ID_TO_ALIAS[model];
189
+ }
190
+ return model;
191
+ }
131
192
  // Reverse of the Claude tier-default IDs, plus the Fable alias which Claude
132
193
  // Code's Agent tool accepts but which is not a GSD model-profile tier (#1133).
133
194
  const CLAUDE_POLICY_ID_TO_ALIAS = {
@@ -154,7 +215,10 @@ function warnModelPolicyUnmappable(agentType, policyModel, tier) {
154
215
  function _resetModelPolicyWarningCacheForTests() {
155
216
  _modelPolicyUnmappableWarned.clear();
156
217
  }
157
- // Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041).
218
+ // Dedupe stderr warnings for unmappable model_overrides Claude IDs (#2041 /
219
+ // #4192). #2041 originally warned that such a value was being DROPPED to tier
220
+ // resolution; #4192 keeps the warn-once breadcrumb but changes the behavior to
221
+ // a verbatim pass-through, so the text now describes the pass-through.
158
222
  const _modelOverrideUnmappableWarned = new Set();
159
223
  function warnModelOverrideUnmappable(agentType, overrideValue) {
160
224
  const key = `${agentType}::${overrideValue}`;
@@ -165,8 +229,9 @@ function warnModelOverrideUnmappable(agentType, overrideValue) {
165
229
  // full to stderr/logs (#2041 security review). MUST go to stderr — resolve-
166
230
  // model's JSON result is parsed from stdout.
167
231
  const safe = overrideValue.length > 64 ? overrideValue.slice(0, 64) + '…' : overrideValue;
168
- process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} ` +
169
- `has no Claude agent alias; falling through to tier resolution.\n`);
232
+ process.stderr.write(`gsd: warning — model_overrides value "${safe}" for ${agentType} is a fully-qualified ` +
233
+ `Claude model ID with no tier alias; passing it through verbatim. Claude Code setups ` +
234
+ `whose Agent tool accepts only tier aliases will not honor it. (#4192)\n`);
170
235
  }
171
236
  // Test-only: reset the model_overrides warn-dedupe cache between cases (#2041).
172
237
  function _resetModelOverrideWarningCacheForTests() {
@@ -175,12 +240,24 @@ function _resetModelOverrideWarningCacheForTests() {
175
240
  /**
176
241
  * #2041 — Map a `model_overrides` value to its Claude Agent-tool alias on the
177
242
  * claude runtime, mirroring the `model_policy` path (#1144). Claude Code's
178
- * Agent tool `model` parameter documents only tier aliases (opus/sonnet/haiku/
179
- * fable); a full Claude model ID returned verbatim is silently dropped by the
180
- * spawner. Returns the value to return verbatim, or null to signal "fall
181
- * through to normal tier/dynamic-routing resolution" (used when a Claude full
182
- * ID has no alias — matches model_policy's warn-and-fall-through). Non-Claude
183
- * runtimes and non-Claude values always pass through verbatim.
243
+ * Agent tool `model` parameter documents tier aliases (opus/sonnet/haiku/
244
+ * fable) as the always-accepted form. Returns the value to emit verbatim, or
245
+ * null to signal "fall through to normal tier/dynamic-routing resolution".
246
+ * Non-Claude runtimes and non-Claude values always pass through verbatim.
247
+ *
248
+ * #4192 — an unmappable `claude-*` value (a pinned generation that is not the
249
+ * current catalog default, e.g. `claude-opus-4-7`) is now PASSED THROUGH
250
+ * VERBATIM with a warn-once stderr breadcrumb, instead of being dropped to
251
+ * tier resolution. #2041's drop was correct when the value was plausibly a
252
+ * mis-typed current default, but for an explicit pin it silently UNPINNED the
253
+ * operator's choice — the resolver would report a tier the config never asked
254
+ * for, the exact "profile can misrepresent what actually runs" defect #4192
255
+ * files. The documented contract ("any fully-qualified model ID",
256
+ * docs/CONFIGURATION.md § Per-Agent Overrides,
257
+ * gsd-core/references/model-profiles.md § Per-Agent Overrides) is restored:
258
+ * the pin is resolved as configured. Values that DO map to a current tier
259
+ * alias still collapse to that alias — byte-equivalent resolution, the #2041
260
+ * protection preserved — and a mappable pin never warns.
184
261
  *
185
262
  * Hardening (code+security review): a `typeof` guard preserves the pre-fix
186
263
  * no-crash behavior if a malformed config surfaces a non-string value, and an
@@ -204,8 +281,9 @@ function mapClaudeOverrideForRuntime(override, configRuntime, agentType) {
204
281
  if (model_catalog_cjs_1.CLAUDE_AGENT_ALIASES.has(override))
205
282
  return override;
206
283
  if (override.startsWith('claude-')) {
284
+ // #4192: explicit generation pin — resolve as configured (see docblock).
207
285
  warnModelOverrideUnmappable(agentType, override);
208
- return null;
286
+ return override;
209
287
  }
210
288
  return override;
211
289
  }
@@ -467,6 +545,19 @@ function resolveModelInternal(cwd, agentType) {
467
545
  && (projectExplicitlySetsOmit(cwd) || !RUNTIMES_WITH_NATIVE_ALIASES.has(resolveActiveRuntime(config)))) {
468
546
  return '';
469
547
  }
548
+ // 4.5. Claude-runtime tier override (#4192 Finding 1). Sits AFTER the omit
549
+ // gate so an explicit project `resolve_model_ids:"omit"` still wins (#2297:
550
+ // explicit project omit is honored regardless of runtime), and BEFORE the
551
+ // alias return so a pinned generation is not re-collapsed to a tier alias or
552
+ // re-materialized to the LATEST catalog id by step 5's
553
+ // `resolve_model_ids:true` path. Fires ONLY when the user wrote a
554
+ // `model_profile_overrides.claude.<tier>` entry for this tier — see
555
+ // resolveClaudeTierOverrideModel for why the builtin map stays out.
556
+ if (tier && tier !== 'inherit') {
557
+ const claudeOverrideModel = resolveClaudeTierOverrideModel(configRuntime, tier, config['model_profile_overrides']);
558
+ if (claudeOverrideModel !== null)
559
+ return claudeOverrideModel;
560
+ }
470
561
  // 5. Profile lookup (Claude-native default).
471
562
  if (!agentModels) {
472
563
  return profile === 'quality' ? 'opus'
@@ -6,7 +6,7 @@
6
6
  * Unsupported in this router:
7
7
  * - scaffold: routed through top-level scaffold command.
8
8
  *
9
- * CJS-only subcommands: mvp-mode (dispatched directly, before hub).
9
+ * CJS-only subcommands: mvp-mode, tdd-applicable (dispatched directly, before hub).
10
10
  *
11
11
  * #3788: dispatch is mediated by CommandRoutingHub. The public entry point
12
12
  * and observable CLI behaviour are unchanged.
@@ -57,6 +57,13 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
57
57
  phase.cmdPhaseMvpMode(cwd, args.slice(2), raw);
58
58
  return;
59
59
  }
60
+ // `tdd-applicable` (#4273): same CJS-native dispatch shape as `mvp-mode`
61
+ // immediately above — a precedence cascade over plan/task/config sources
62
+ // with its own typed JSON result, not routed through the SDK query layer.
63
+ if (subcommand === 'tdd-applicable') {
64
+ phase.cmdPhaseTddApplicable(cwd, args.slice(2), raw);
65
+ return;
66
+ }
60
67
  // ── Build the CJS registry ──────────────────────────────────────────────────
61
68
  // Each handler receives a ctx object from the hub and must return a HubResult.
62
69
  const cjsRegistry = {
@@ -119,7 +126,13 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
119
126
  if (args.includes('--dry-run')) {
120
127
  return makeInvalidArgs('--dry-run', 'phase insert does not support --dry-run');
121
128
  }
122
- phase.cmdPhaseInsert(cwd, args[2], args.slice(3).join(' '), raw);
129
+ // #4569: --sibling opts into joining afterPhase's parent decimal level
130
+ // instead of nesting one level deeper. Filtered out like other
131
+ // boolean flags (see `remove`'s --force handling above) so it never
132
+ // leaks into the free-text description.
133
+ const sibling = args.includes('--sibling');
134
+ const insertArgs = args.slice(2).filter(token => token !== '--sibling');
135
+ phase.cmdPhaseInsert(cwd, insertArgs[0], insertArgs.slice(1).join(' '), raw, sibling ? 'sibling' : 'nested');
123
136
  return { ok: true, data: null };
124
137
  },
125
138
  remove: (_ctx) => {
@@ -224,14 +237,14 @@ function routePhaseCommand({ phase, args, cwd, raw, error }) {
224
237
  // ── Build manifest (available subcommands for UnknownCommand detection) ─────
225
238
  // `availableSubcommands` is what the error message shows. It excludes
226
239
  // unsupported commands (already handled above) but does NOT include 'mvp-mode'
227
- // because it was absent from PHASE_SUBCOMMANDS in the original and was not
228
- // shown in the "Available:" list there either.
240
+ // or 'tdd-applicable' (#4273) because both are absent from PHASE_SUBCOMMANDS
241
+ // and were not shown in the "Available:" list there either.
229
242
  //
230
243
  // `manifestSubcommands` is the full routing set for the hub — it includes
231
- // 'mvp-mode' (which the original code routed via a handler even without a
232
- // manifest entry) so the hub's UnknownCommand check passes for it.
244
+ // 'mvp-mode' and 'tdd-applicable' (both routed via a handler even without a
245
+ // manifest entry) so the hub's UnknownCommand check passes for them.
233
246
  const availableSubcommands = command_aliases_cjs_1.PHASE_SUBCOMMANDS.filter(s => !UNSUPPORTED[s]);
234
- const manifestSubcommands = ['mvp-mode', ...availableSubcommands];
247
+ const manifestSubcommands = ['mvp-mode', 'tdd-applicable', ...availableSubcommands];
235
248
  const manifest = { phase: manifestSubcommands };
236
249
  // ── Construct hub ──────────────────────────────────────────────────────────
237
250
  // #175: Hub is CJS-only — no mode param, no sdkLoader.