@opengsd/gsd-core 1.13.0 → 1.15.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 (441) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/README.ja-JP.md +3 -3
  4. package/README.ko-KR.md +3 -3
  5. package/README.pt-BR.md +3 -3
  6. package/README.zh-CN.md +3 -3
  7. package/agents/gsd-advisor-researcher.compact.md +85 -0
  8. package/agents/gsd-ai-researcher.compact.md +96 -0
  9. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  10. package/agents/gsd-code-fixer.compact.md +459 -0
  11. package/agents/gsd-code-fixer.md +9 -8
  12. package/agents/gsd-code-reviewer.compact.md +269 -0
  13. package/agents/gsd-code-reviewer.md +15 -3
  14. package/agents/gsd-codebase-mapper.compact.md +760 -0
  15. package/agents/gsd-debug-session-manager.compact.md +360 -0
  16. package/agents/gsd-debug-session-manager.md +17 -2
  17. package/agents/gsd-debugger.md +2 -2
  18. package/agents/gsd-doc-classifier.compact.md +192 -0
  19. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  20. package/agents/gsd-doc-verifier.compact.md +143 -0
  21. package/agents/gsd-doc-writer.compact.md +440 -0
  22. package/agents/gsd-dom-verifier.compact.md +138 -0
  23. package/agents/gsd-domain-researcher.compact.md +141 -0
  24. package/agents/gsd-eval-auditor.compact.md +160 -0
  25. package/agents/gsd-eval-auditor.md +1 -1
  26. package/agents/gsd-eval-planner.compact.md +137 -0
  27. package/agents/gsd-executor.md +13 -8
  28. package/agents/gsd-framework-selector.compact.md +82 -0
  29. package/agents/gsd-integration-checker.compact.md +245 -0
  30. package/agents/gsd-intel-updater.compact.md +226 -0
  31. package/agents/gsd-intel-updater.md +1 -1
  32. package/agents/gsd-mempalace-curator.compact.md +45 -0
  33. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  34. package/agents/gsd-pattern-mapper.compact.md +275 -0
  35. package/agents/gsd-phase-researcher.md +19 -11
  36. package/agents/gsd-plan-checker.md +8 -7
  37. package/agents/gsd-planner.md +12 -8
  38. package/agents/gsd-project-researcher.compact.md +587 -0
  39. package/agents/gsd-project-researcher.md +1 -1
  40. package/agents/gsd-research-synthesizer.compact.md +212 -0
  41. package/agents/gsd-research-synthesizer.md +1 -1
  42. package/agents/gsd-roadmapper.compact.md +454 -0
  43. package/agents/gsd-roadmapper.md +13 -0
  44. package/agents/gsd-security-auditor.compact.md +162 -0
  45. package/agents/gsd-ui-auditor.compact.md +404 -0
  46. package/agents/gsd-ui-auditor.md +155 -17
  47. package/agents/gsd-ui-checker.compact.md +277 -0
  48. package/agents/gsd-ui-researcher.compact.md +282 -0
  49. package/agents/gsd-ui-researcher.md +1 -1
  50. package/agents/gsd-user-profiler.compact.md +108 -0
  51. package/agents/gsd-verifier.md +10 -9
  52. package/bin/install.js +848 -163
  53. package/commands/gsd/autonomous.md +2 -2
  54. package/commands/gsd/capture.md +1 -1
  55. package/commands/gsd/cleanup.md +1 -0
  56. package/commands/gsd/code-review.md +2 -1
  57. package/commands/gsd/complete-milestone.md +1 -0
  58. package/commands/gsd/config.md +1 -0
  59. package/commands/gsd/debug.md +1 -0
  60. package/commands/gsd/graphify.md +1 -0
  61. package/commands/gsd/health.md +1 -0
  62. package/commands/gsd/mempalace-capture.md +8 -3
  63. package/commands/gsd/mempalace-recall.md +1 -0
  64. package/commands/gsd/new-milestone.md +1 -0
  65. package/commands/gsd/new-project.md +1 -0
  66. package/commands/gsd/next.md +1 -0
  67. package/commands/gsd/pause-work.md +1 -0
  68. package/commands/gsd/phase.md +1 -0
  69. package/commands/gsd/plan-review-convergence.md +6 -6
  70. package/commands/gsd/pr-branch.md +1 -0
  71. package/commands/gsd/progress.md +1 -1
  72. package/commands/gsd/quick-batch.md +1 -1
  73. package/commands/gsd/resume-work.md +1 -0
  74. package/commands/gsd/review-backlog.md +1 -0
  75. package/commands/gsd/review.md +2 -3
  76. package/commands/gsd/settings.md +2 -1
  77. package/commands/gsd/stats.md +1 -0
  78. package/commands/gsd/thread.md +1 -0
  79. package/commands/gsd/workspace.md +1 -0
  80. package/commands/gsd/workstreams.md +1 -0
  81. package/gsd-core/bin/check-latest-version.cjs +8 -3
  82. package/gsd-core/bin/gsd-tools.cjs +672 -146
  83. package/gsd-core/bin/lib/adr-parser.cjs +4 -2
  84. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  85. package/gsd-core/bin/lib/audit.cjs +119 -34
  86. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  87. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  88. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  89. package/gsd-core/bin/lib/capability-registry.cjs +96 -189
  90. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  91. package/gsd-core/bin/lib/capability-validator.cjs +14 -2
  92. package/gsd-core/bin/lib/check-command-router.cjs +213 -49
  93. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  94. package/gsd-core/bin/lib/codex-agent-toml.cjs +21 -25
  95. package/gsd-core/bin/lib/commands.cjs +823 -112
  96. package/gsd-core/bin/lib/config-loader.cjs +66 -4
  97. package/gsd-core/bin/lib/config.cjs +186 -45
  98. package/gsd-core/bin/lib/coverage.cjs +1 -1
  99. package/gsd-core/bin/lib/decisions.cjs +164 -45
  100. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  101. package/gsd-core/bin/lib/frontmatter.cjs +13 -0
  102. package/gsd-core/bin/lib/graphify.cjs +10 -2
  103. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  104. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +12 -1
  105. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  106. package/gsd-core/bin/lib/host-runtime-detection.cjs +9 -0
  107. package/gsd-core/bin/lib/init.cjs +614 -86
  108. package/gsd-core/bin/lib/install-engine.cjs +29 -3
  109. package/gsd-core/bin/lib/install-profiles.cjs +14 -0
  110. package/gsd-core/bin/lib/installer-migrations.cjs +41 -5
  111. package/gsd-core/bin/lib/loop-resolver.cjs +50 -31
  112. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  113. package/gsd-core/bin/lib/milestone.cjs +37 -13
  114. package/gsd-core/bin/lib/model-resolver.cjs +253 -53
  115. package/gsd-core/bin/lib/phase-command-router.cjs +16 -2
  116. package/gsd-core/bin/lib/phase-id-card.cjs +32 -0
  117. package/gsd-core/bin/lib/phase-id-display.cjs +78 -0
  118. package/gsd-core/bin/lib/phase-id.cjs +268 -27
  119. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  120. package/gsd-core/bin/lib/phase-locator.cjs +29 -10
  121. package/gsd-core/bin/lib/phase.cjs +393 -88
  122. package/gsd-core/bin/lib/plan-document.cjs +49 -1
  123. package/gsd-core/bin/lib/planning-document.cjs +459 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +52 -19
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +61 -12
  126. package/gsd-core/bin/lib/planning-workspace.cjs +57 -3
  127. package/gsd-core/bin/lib/pr-branch-patterns.cjs +57 -0
  128. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  129. package/gsd-core/bin/lib/probe-core.cjs +7 -1
  130. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  131. package/gsd-core/bin/lib/project-root.cjs +41 -2
  132. package/gsd-core/bin/lib/quick-batch.cjs +1 -1
  133. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  134. package/gsd-core/bin/lib/research-store.cjs +11 -12
  135. package/gsd-core/bin/lib/review-lane-descriptor.cjs +10 -30
  136. package/gsd-core/bin/lib/review-lane-invocation.cjs +23 -0
  137. package/gsd-core/bin/lib/review-reviewer-selection.cjs +2 -2
  138. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  139. package/gsd-core/bin/lib/roadmap-command-router.cjs +12 -4
  140. package/gsd-core/bin/lib/roadmap-parser.cjs +219 -18
  141. package/gsd-core/bin/lib/roadmap-upgrade.cjs +1539 -13
  142. package/gsd-core/bin/lib/roadmap.cjs +356 -42
  143. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +310 -41
  144. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +15 -4
  145. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +13 -5
  146. package/gsd-core/bin/lib/runtime-homes.cjs +4 -0
  147. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -37
  148. package/gsd-core/bin/lib/runtime-name-policy.cjs +111 -1
  149. package/gsd-core/bin/lib/security.cjs +126 -7
  150. package/gsd-core/bin/lib/shell-command-projection.cjs +10 -6
  151. package/gsd-core/bin/lib/state-document.cjs +130 -28
  152. package/gsd-core/bin/lib/state-md-schema.cjs +21 -14
  153. package/gsd-core/bin/lib/state-transition.cjs +181 -30
  154. package/gsd-core/bin/lib/state.cjs +265 -27
  155. package/gsd-core/bin/lib/surface.cjs +77 -3
  156. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  157. package/gsd-core/bin/lib/tdd-red-evidence.cjs +78 -5
  158. package/gsd-core/bin/lib/uat-predicate.cjs +47 -4
  159. package/gsd-core/bin/lib/uat.cjs +9 -1
  160. package/gsd-core/bin/lib/ui-consideration-probe.cjs +15 -2
  161. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +100 -9
  162. package/gsd-core/bin/lib/undo-commit-selection.cjs +131 -0
  163. package/gsd-core/bin/lib/update-context.cjs +30 -24
  164. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  165. package/gsd-core/bin/lib/verification.cjs +315 -30
  166. package/gsd-core/bin/lib/verify-command-grounding.cjs +47 -3
  167. package/gsd-core/bin/lib/verify.cjs +320 -48
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +1 -0
  169. package/gsd-core/bin/lib/worktree-base-ref.cjs +482 -73
  170. package/gsd-core/bin/lib/worktree-safety.cjs +797 -58
  171. package/gsd-core/bin/shared/config-defaults.manifest.json +4 -0
  172. package/gsd-core/bin/shared/config-schema.manifest.json +6 -0
  173. package/gsd-core/bin/verify-reapply-patches.cjs +439 -80
  174. package/gsd-core/references/checkpoints.md +5 -3
  175. package/gsd-core/references/compact-content-gate.md +66 -0
  176. package/gsd-core/references/edge-probe-fixtures/01-round-half-even/expected-coverage.json +28 -3
  177. package/gsd-core/references/edge-probe-fixtures/02-merge-intervals/expected-coverage.json +37 -4
  178. package/gsd-core/references/edge-probe-fixtures/03-truncate-graphemes/expected-coverage.json +28 -3
  179. package/gsd-core/references/edge-probe-fixtures/04-money-rounding/expected-coverage.json +28 -3
  180. package/gsd-core/references/edge-probe-fixtures/05-list-dedupe/expected-coverage.json +37 -4
  181. package/gsd-core/references/edge-probe-fixtures/06-resolved-mixed/expected-coverage.json +37 -4
  182. package/gsd-core/references/edge-probe.md +195 -21
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +7 -6
  184. package/gsd-core/references/execute-phase-wave-guard.md +22 -11
  185. package/gsd-core/references/gsd-run-resolver.md +1 -1
  186. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  187. package/gsd-core/references/model-profiles.md +13 -4
  188. package/gsd-core/references/phase-argument-parsing.md +9 -7
  189. package/gsd-core/references/phase-id-convention.md +28 -0
  190. package/gsd-core/references/planner-gap-closure.md +2 -0
  191. package/gsd-core/references/planner-load-graph-context.md +24 -13
  192. package/gsd-core/references/planner-verify-command-grounding.md +14 -0
  193. package/gsd-core/references/planning-config.md +14 -2
  194. package/gsd-core/references/tdd.md +30 -4
  195. package/gsd-core/references/thinking-models-planning.md +18 -2
  196. package/gsd-core/references/ui-consideration-probe.md +10 -5
  197. package/gsd-core/references/verification-patterns.md +17 -4
  198. package/gsd-core/references/verify-command-path-resolvability.md +10 -2
  199. package/gsd-core/references/worktree-path-safety.md +433 -2
  200. package/gsd-core/templates/README.md +7 -1
  201. package/gsd-core/templates/state.md +6 -3
  202. package/gsd-core/templates/summary.compact.md +212 -0
  203. package/gsd-core/templates/user-setup.compact.md +199 -0
  204. package/gsd-core/templates/user-setup.md +0 -9
  205. package/gsd-core/templates/verification-report.md +1 -1
  206. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  207. package/gsd-core/workflows/add-backlog.md +1 -1
  208. package/gsd-core/workflows/add-phase.md +1 -1
  209. package/gsd-core/workflows/add-tests.md +2 -2
  210. package/gsd-core/workflows/add-todo.md +6 -5
  211. package/gsd-core/workflows/ai-integration-phase.md +11 -3
  212. package/gsd-core/workflows/audit-fix.md +1 -1
  213. package/gsd-core/workflows/audit-milestone.md +1 -1
  214. package/gsd-core/workflows/audit-uat.md +1 -1
  215. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +9 -18
  216. package/gsd-core/workflows/autonomous.md +29 -16
  217. package/gsd-core/workflows/check-todos.md +6 -4
  218. package/gsd-core/workflows/cleanup.md +5 -3
  219. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +4 -3
  220. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +8 -1
  221. package/gsd-core/workflows/code-review-fix.md +108 -22
  222. package/gsd-core/workflows/code-review.md +216 -73
  223. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  224. package/gsd-core/workflows/complete-milestone.md +41 -264
  225. package/gsd-core/workflows/debug.md +3 -3
  226. package/gsd-core/workflows/diagnose-issues.md +1 -1
  227. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  228. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  229. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  230. package/gsd-core/workflows/discuss-phase.md +1 -1
  231. package/gsd-core/workflows/do.md +2 -2
  232. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  233. package/gsd-core/workflows/docs-update.md +17 -158
  234. package/gsd-core/workflows/edit-phase.md +1 -1
  235. package/gsd-core/workflows/eval-review.md +10 -3
  236. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  237. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +1017 -0
  238. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +19 -4
  239. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  240. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +43 -4
  241. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  242. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  243. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  244. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  245. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  246. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +46 -9
  247. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  248. package/gsd-core/workflows/execute-phase/steps/ready-wave-gate.md +37 -0
  249. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  250. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  251. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +24 -0
  252. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  253. package/gsd-core/workflows/execute-phase/steps/threat-id-gate.md +28 -0
  254. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +1 -1
  255. package/gsd-core/workflows/execute-phase/steps/worktree-base-check.md +25 -0
  256. package/gsd-core/workflows/execute-phase.md +83 -172
  257. package/gsd-core/workflows/execute-plan.md +24 -10
  258. package/gsd-core/workflows/explore.md +3 -3
  259. package/gsd-core/workflows/extract-learnings.md +2 -1
  260. package/gsd-core/workflows/fast.md +1 -1
  261. package/gsd-core/workflows/forensics.md +1 -1
  262. package/gsd-core/workflows/graduation.md +1 -1
  263. package/gsd-core/workflows/health.md +2 -2
  264. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  265. package/gsd-core/workflows/help/modes/full.md +5 -5
  266. package/gsd-core/workflows/help/modes/topic.md +15 -5
  267. package/gsd-core/workflows/help.md +1 -1
  268. package/gsd-core/workflows/import.md +2 -2
  269. package/gsd-core/workflows/inbox.md +2 -2
  270. package/gsd-core/workflows/ingest-docs.md +3 -3
  271. package/gsd-core/workflows/insert-phase.md +1 -1
  272. package/gsd-core/workflows/list-seeds.md +1 -1
  273. package/gsd-core/workflows/list-workspaces.md +1 -1
  274. package/gsd-core/workflows/manager.md +2 -2
  275. package/gsd-core/workflows/map-codebase.md +52 -5
  276. package/gsd-core/workflows/milestone-summary.md +1 -1
  277. package/gsd-core/workflows/mvp-phase.md +1 -1
  278. package/gsd-core/workflows/new-milestone.md +56 -14
  279. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  280. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +3 -3
  281. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +1 -1
  282. package/gsd-core/workflows/new-project.md +39 -209
  283. package/gsd-core/workflows/new-workspace.md +2 -2
  284. package/gsd-core/workflows/next.md +1 -1
  285. package/gsd-core/workflows/note.md +1 -1
  286. package/gsd-core/workflows/onboard.md +1 -1
  287. package/gsd-core/workflows/pause-work.md +1 -1
  288. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  289. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +17 -5
  290. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  291. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +23 -5
  292. package/gsd-core/workflows/plan-phase.md +45 -187
  293. package/gsd-core/workflows/plan-review-convergence.md +21 -5
  294. package/gsd-core/workflows/plant-seed.md +62 -20
  295. package/gsd-core/workflows/pr-branch.md +132 -20
  296. package/gsd-core/workflows/profile-user.md +2 -2
  297. package/gsd-core/workflows/progress.md +1 -1
  298. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +25 -0
  299. package/gsd-core/workflows/quick/steps/quick-verification.md +1 -1
  300. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +1 -1
  301. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  302. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  303. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  304. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  305. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  306. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  307. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +1 -1
  308. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  309. package/gsd-core/workflows/quick-batch.md +1 -1
  310. package/gsd-core/workflows/quick.md +29 -10
  311. package/gsd-core/workflows/reapply-patches.md +86 -6
  312. package/gsd-core/workflows/remove-phase.md +1 -1
  313. package/gsd-core/workflows/remove-workspace.md +2 -2
  314. package/gsd-core/workflows/resume-project.md +1 -1
  315. package/gsd-core/workflows/review.md +31 -16
  316. package/gsd-core/workflows/scan.md +1 -1
  317. package/gsd-core/workflows/secure-phase.md +3 -2
  318. package/gsd-core/workflows/settings-advanced.md +30 -10
  319. package/gsd-core/workflows/settings-integrations.md +2 -3
  320. package/gsd-core/workflows/settings.md +22 -9
  321. package/gsd-core/workflows/ship.md +3 -2
  322. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  323. package/gsd-core/workflows/sketch.md +1 -1
  324. package/gsd-core/workflows/smart-entry.md +2 -2
  325. package/gsd-core/workflows/spec-phase.md +15 -5
  326. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  327. package/gsd-core/workflows/spike.md +1 -1
  328. package/gsd-core/workflows/stats.md +1 -1
  329. package/gsd-core/workflows/sync-skills.md +5 -5
  330. package/gsd-core/workflows/thread.md +1 -1
  331. package/gsd-core/workflows/transition.md +1 -1
  332. package/gsd-core/workflows/ui-phase.md +44 -8
  333. package/gsd-core/workflows/ui-review.md +18 -4
  334. package/gsd-core/workflows/ultraplan-phase.md +1 -1
  335. package/gsd-core/workflows/undo.md +339 -20
  336. package/gsd-core/workflows/update.md +14 -12
  337. package/gsd-core/workflows/validate-phase.md +3 -2
  338. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  339. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  340. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  341. package/gsd-core/workflows/verify-work.md +101 -196
  342. package/hooks/dist/gsd-agent-isolation-guard.js +66 -16
  343. package/hooks/dist/gsd-context-monitor.js +88 -15
  344. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  345. package/hooks/dist/gsd-secret-read-guard.js +71 -19
  346. package/hooks/dist/gsd-statusline.js +81 -20
  347. package/hooks/dist/gsd-validate-commit.sh +97 -8
  348. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  349. package/hooks/dist/gsd-write-guard.js +46 -1
  350. package/hooks/dist/lib/dispatch-identity.js +187 -0
  351. package/hooks/dist/lib/filename-classification.js +64 -0
  352. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  353. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  354. package/hooks/gsd-agent-isolation-guard.js +66 -16
  355. package/hooks/gsd-context-monitor.js +88 -15
  356. package/hooks/gsd-cursor-subagent-start.js +34 -14
  357. package/hooks/gsd-secret-read-guard.js +71 -19
  358. package/hooks/gsd-statusline.js +81 -20
  359. package/hooks/gsd-validate-commit.sh +97 -8
  360. package/hooks/gsd-worktree-path-guard.js +25 -14
  361. package/hooks/gsd-write-guard.js +46 -1
  362. package/hooks/lib/dispatch-identity.js +187 -0
  363. package/hooks/lib/filename-classification.js +64 -0
  364. package/hooks/lib/isolation-deny-reason.js +53 -1
  365. package/hooks/lib/isolation-sentinel.js +58 -19
  366. package/package.json +11 -6
  367. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  368. package/scripts/benchmark-compact-content.cjs +368 -0
  369. package/scripts/build-hooks.js +15 -6
  370. package/scripts/check-contract-drift.cjs +131 -12
  371. package/scripts/check-env.cjs +36 -8
  372. package/scripts/check-glossary-refs.cjs +25 -21
  373. package/scripts/ci-next-health.cjs +271 -0
  374. package/scripts/ci-prepare-test-scope.cjs +7 -7
  375. package/scripts/ci-test-scope.cjs +126 -20
  376. package/scripts/ci-timeout-report.cjs +1 -1
  377. package/scripts/command-contract-helpers.cjs +3 -0
  378. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  379. package/scripts/docs-guard-registry.cjs +35 -2
  380. package/scripts/gen-adr-index.cjs +8 -2
  381. package/scripts/gen-inventory-manifest.cjs +12 -0
  382. package/scripts/gen-loop-host-contract.cjs +69 -0
  383. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  384. package/scripts/lib/drift-scan.cjs +1 -1
  385. package/scripts/lib/macos-conformance-tier.generated.cjs +224 -0
  386. package/scripts/lib/ndjson-reporter.cjs +3 -2
  387. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  388. package/scripts/lib/platform-conformance-tier.generated.cjs +287 -0
  389. package/scripts/lib/suite-detection.cjs +32 -0
  390. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  391. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +47 -3
  392. package/scripts/lint-phase-arg-assignment.cjs +257 -0
  393. package/scripts/lint-phase-id-drift.cjs +623 -13
  394. package/scripts/lint-pr-branch-pattern-drift.cjs +148 -0
  395. package/scripts/lint-response-language-coverage.cjs +9 -3
  396. package/scripts/lint-retired-runtime-name.cjs +619 -0
  397. package/scripts/lint-source-test-name-collision.cjs +1 -1
  398. package/scripts/lint-state-write-path-drift.cjs +93 -0
  399. package/scripts/lint-test-file-count.allowlist.json +29 -9
  400. package/scripts/lint-vendored-deps.cjs +128 -17
  401. package/scripts/lint-workflow-shellcheck-baseline.json +100 -0
  402. package/scripts/prompt-injection-scan.sh +18 -0
  403. package/scripts/release-tarball-smoke.cjs +194 -1
  404. package/scripts/workflow-size.cjs +139 -0
  405. package/skills/gsd-autonomous/SKILL.md +2 -2
  406. package/skills/gsd-capture/SKILL.md +1 -1
  407. package/skills/gsd-cleanup/SKILL.md +1 -0
  408. package/skills/gsd-code-review/SKILL.md +2 -1
  409. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  410. package/skills/gsd-config/SKILL.md +1 -0
  411. package/skills/gsd-debug/SKILL.md +1 -0
  412. package/skills/gsd-graphify/SKILL.md +1 -0
  413. package/skills/gsd-health/SKILL.md +1 -0
  414. package/skills/gsd-mempalace-capture/SKILL.md +8 -3
  415. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  416. package/skills/gsd-new-milestone/SKILL.md +1 -0
  417. package/skills/gsd-new-project/SKILL.md +1 -0
  418. package/skills/gsd-next/SKILL.md +1 -0
  419. package/skills/gsd-pause-work/SKILL.md +1 -0
  420. package/skills/gsd-phase/SKILL.md +1 -0
  421. package/skills/gsd-plan-review-convergence/SKILL.md +5 -5
  422. package/skills/gsd-pr-branch/SKILL.md +1 -0
  423. package/skills/gsd-progress/SKILL.md +1 -1
  424. package/skills/gsd-quick-batch/SKILL.md +1 -1
  425. package/skills/gsd-resume-work/SKILL.md +1 -0
  426. package/skills/gsd-review/SKILL.md +2 -3
  427. package/skills/gsd-review-backlog/SKILL.md +1 -0
  428. package/skills/gsd-settings/SKILL.md +2 -1
  429. package/skills/gsd-stats/SKILL.md +1 -0
  430. package/skills/gsd-thread/SKILL.md +1 -0
  431. package/skills/gsd-workspace/SKILL.md +1 -0
  432. package/skills/gsd-workstreams/SKILL.md +1 -0
  433. package/vscode/package.json +1 -1
  434. package/gsd-core/templates/claude-md.md +0 -145
  435. package/gsd-core/templates/codebase/concerns.md +0 -310
  436. package/gsd-core/templates/codebase/conventions.md +0 -307
  437. package/gsd-core/templates/codebase/integrations.md +0 -280
  438. package/gsd-core/templates/codebase/structure.md +0 -285
  439. package/gsd-core/templates/codebase/testing.md +0 -480
  440. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  441. package/gsd-core/templates/discovery.md +0 -146
@@ -52,6 +52,7 @@ const CANONICAL_HEADERS = {
52
52
  'recommendation',
53
53
  'strategy',
54
54
  'decision outcome',
55
+ 'locked decisions',
55
56
  ],
56
57
  considered_options: [
57
58
  'considered options',
@@ -190,7 +191,8 @@ function normalizeAdrHeader(raw) {
190
191
  .toLowerCase()
191
192
  .replace(/[\s:._-]+/g, ' ')
192
193
  .replace(/[^\w\s]/g, '')
193
- .trim();
194
+ .trim()
195
+ .replace(/^\d+(?:\s+\d+)*\s+/, '');
194
196
  }
195
197
  // Normalized synonym index (audit M7). classifyHeader receives an ALREADY-normalized
196
198
  // header (via normalizeAdrHeader), but historically compared it against the RAW synonym
@@ -402,7 +404,7 @@ function parseCliArgs(argv) {
402
404
  }
403
405
  function main(argv) {
404
406
  const opts = parseCliArgs(argv);
405
- const safePath = (0, security_cjs_1.requireSafePath)(opts.input, node_path_1.default.resolve(opts.projectDir), 'ADR input path', { allowAbsolute: true });
407
+ const safePath = (0, security_cjs_1.requireSafePath)(opts.input, node_path_1.default.resolve(opts.projectDir), 'ADR input path', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
406
408
  const content = node_fs_1.default.readFileSync(safePath, 'utf8');
407
409
  const parsed = parseAdrMarkdown(content, { sourcePath: opts.input ?? undefined, format: opts.format });
408
410
  process.stdout.write(JSON.stringify(parsed, null, 2));
@@ -31,7 +31,8 @@ exports.CANONICAL_EXACT = new Set([
31
31
  'STATE-ARCHIVE.md', // state.cts's cmdStatePrune writes this at the .planning/ root
32
32
  'milestone.lock', // #3311: milestone (phase + session) claim (src/milestone-lock.cts); persistent, unlike the transient STATE.md.lock/WAITING.json
33
33
  'state.json', // #3227: machine-readable state contract published at step boundaries (src/state-contract.cts)
34
- 'skill-manifest.json', // init.cts routeSkillManifest --write (project-scoped planning root, #3964)
34
+ 'skill-manifest.json', // init.cts cmdSkillManifest --write (project-scoped planning root, #3964)
35
+ 'PATTERNS.md', // #4282: graduated cross-phase patterns (workflows/graduation.md, `patterns` target) -- distinct from the per-phase NN-PATTERNS.md (templates/README.md)
35
36
  ]);
36
37
  // Pattern-match canonical file names (regex tests on the basename)
37
38
  // Each pattern includes the name of the workflow that produces it as a comment.
@@ -25,10 +25,15 @@ const coreUtils = require("./core-utils.cjs");
25
25
  const { normalizeLineEndings } = coreUtils;
26
26
  // eslint-disable-next-line @typescript-eslint/no-require-imports
27
27
  const planningWorkspace = require("./planning-workspace.cjs");
28
- const { planningDir, quickDirFrom } = planningWorkspace;
28
+ const { planningDir, quickDirFrom, todosDir } = planningWorkspace;
29
29
  // eslint-disable-next-line @typescript-eslint/no-require-imports
30
30
  const frontmatter = require("./frontmatter.cjs");
31
- const { extractFrontmatter, spliceFrontmatter } = frontmatter;
31
+ // #4378 (roll-in): scanSeeds publishes the SAME canonical seed identity the
32
+ // list-seeds gate derives — one grammar, two surfaces, no drift. commands.cjs
33
+ // does not require this module, so the edge is acyclic.
34
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
35
+ const commandsModule = require("./commands.cjs");
36
+ const { extractFrontmatter, FRONTMATTER_UNPARSEABLE, spliceFrontmatter } = frontmatter;
32
37
  // eslint-disable-next-line @typescript-eslint/no-require-imports
33
38
  const phaseIdMod = require("./phase-id.cjs");
34
39
  const { PHASE_NUMBER_TOKEN_SOURCE, scopeToPhase } = phaseIdMod;
@@ -277,7 +282,7 @@ function scanDebugSessions(planDir) {
277
282
  const filePath = node_path_1.default.join(debugDir, entry.name);
278
283
  let safeFilePath;
279
284
  try {
280
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'debug session file', { allowAbsolute: true });
285
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'debug session file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
281
286
  }
282
287
  catch {
283
288
  continue;
@@ -387,7 +392,7 @@ function scanQuickTasks(planDir) {
387
392
  const taskDir = node_path_1.default.join(quickDir, dirName);
388
393
  let safeTaskDir;
389
394
  try {
390
- safeTaskDir = (0, security_cjs_1.requireSafePath)(taskDir, planDir, 'quick task dir', { allowAbsolute: true });
395
+ safeTaskDir = (0, security_cjs_1.requireSafePath)(taskDir, planDir, 'quick task dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
391
396
  }
392
397
  catch {
393
398
  continue;
@@ -399,7 +404,7 @@ function scanQuickTasks(planDir) {
399
404
  if (summaryPath && node_fs_1.default.existsSync(summaryPath)) {
400
405
  let safeSum;
401
406
  try {
402
- safeSum = (0, security_cjs_1.requireSafePath)(summaryPath, planDir, 'quick task summary', { allowAbsolute: true });
407
+ safeSum = (0, security_cjs_1.requireSafePath)(summaryPath, planDir, 'quick task summary', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
403
408
  }
404
409
  catch {
405
410
  continue;
@@ -475,7 +480,7 @@ function scanThreads(planDir) {
475
480
  const filePath = node_path_1.default.join(threadsDir, entry.name);
476
481
  let safeFilePath;
477
482
  try {
478
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'thread file', { allowAbsolute: true });
483
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'thread file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
479
484
  }
480
485
  catch {
481
486
  continue;
@@ -522,9 +527,16 @@ function scanThreads(planDir) {
522
527
  * Scan .planning/todos/pending/ for pending todos.
523
528
  * Returns array of { filename, priority, area, summary }.
524
529
  * Display limited to first 5 + count of remainder.
530
+ *
531
+ * #4256: takes the ROOT-scoped todos base (`todosDir(cwd)`), NOT the
532
+ * workstream-scoped planning dir the other scans use — todos are shared
533
+ * project state (the migrateToWorkstreams contract keeps them at
534
+ * .planning/todos/), so the close gate must read the root or it clears
535
+ * vacuously under a workstream. The requireSafePath boundary below moves
536
+ * with the base.
525
537
  */
526
- function scanTodos(planDir) {
527
- const pendingDir = node_path_1.default.join(planDir, 'todos', 'pending');
538
+ function scanTodos(todosBase) {
539
+ const pendingDir = node_path_1.default.join(todosBase, 'pending');
528
540
  if (!node_fs_1.default.existsSync(pendingDir))
529
541
  return { items: [], acknowledged: 0 };
530
542
  let files;
@@ -550,7 +562,7 @@ function scanTodos(planDir) {
550
562
  const filePath = node_path_1.default.join(pendingDir, entry.name);
551
563
  let safeFilePath;
552
564
  try {
553
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'todo file', { allowAbsolute: true });
565
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, todosBase, 'todo file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
554
566
  }
555
567
  catch {
556
568
  continue;
@@ -595,6 +607,14 @@ function scanTodos(planDir) {
595
607
  }
596
608
  return { items: results, acknowledged };
597
609
  }
610
+ // #4378 (roll-in): true when the filename is a well-formed seed name —
611
+ // `SEED-` prefix, `.md` suffix, and no control bytes anywhere in between (the
612
+ // scanSeeds admission filter passes such names through to the identity
613
+ // derivation; a name with an embedded control byte falls back to its raw
614
+ // stem exactly as the pre-canonical code did).
615
+ function seedIdMatchRawName(name) {
616
+ return !/[\u0000-\u001f\u007f]/.test(name);
617
+ }
598
618
  // ─── scanSeeds ────────────────────────────────────────────────────────────────
599
619
  /**
600
620
  * Scan .planning/seeds/SEED-*.md for unimplemented seeds.
@@ -622,7 +642,7 @@ function scanSeeds(planDir) {
622
642
  const filePath = node_path_1.default.join(seedsDir, entry.name);
623
643
  let safeFilePath;
624
644
  try {
625
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'seed file', { allowAbsolute: true });
645
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'seed file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
626
646
  }
627
647
  catch {
628
648
  continue;
@@ -646,16 +666,18 @@ function scanSeeds(planDir) {
646
666
  acknowledged++;
647
667
  continue;
648
668
  }
649
- // Extract seed_id from filename or frontmatter. The regex match is
650
- // `\w`/hyphen-constrained (safe by construction, like `archived_milestone`)
651
- // but the fallback taken when a filename doesn't fully match — e.g. a
652
- // `SEED-`-prefixed, `.md`-suffixed name with a control byte SOMEWHERE in
653
- // the middle, which still passes the `startsWith`/`endsWith` filter above
654
- // — is the raw, unconstrained basename. Both branches are routed through
655
- // sanitizeLabel below.
656
- const seedIdMatch = entry.name.match(/^(SEED-[\w-]+)\.md$/);
657
- const seed_id = seedIdMatch ? seedIdMatch[1] : node_path_1.default.basename(entry.name, '.md');
658
- const slug = (0, security_cjs_1.sanitizeLabel)(seed_id.replace(/^SEED-/, ''));
669
+ // #4378 (roll-in): the canonical identity comes from the SAME derivation
670
+ // the list-seeds surface uses — frontmatter `id:` when it matches a seed
671
+ // grammar (legacy `SEED-NNN` or date-suffixed `SEED-YYMMDD-xxx`), else the
672
+ // filename's id prefix, else the whole stem. The old fused
673
+ // filename-stem id (e.g. `SEED-081-region` for `SEED-081-region.md`)
674
+ // disagreed with list-seeds and misfiled deferrals; publishing the
675
+ // canonical id keeps the two surfaces answering identically. The raw-
676
+ // basename fallback for control-byte-bearing names is preserved.
677
+ const stem = node_path_1.default.basename(entry.name, '.md');
678
+ const derived = commandsModule.deriveSeedIdentity(stem, fm.id);
679
+ const seed_id = seedIdMatchRawName(entry.name) ? derived.seed_id : stem;
680
+ const slug = (0, security_cjs_1.sanitizeLabel)(derived.slug);
659
681
  let title = (0, security_cjs_1.sanitizeForDisplay)(fm.title || '');
660
682
  if (!title) {
661
683
  const headingMatch = content.match(/^#\s*(.+)$/m);
@@ -785,7 +807,7 @@ function scanUatGaps(planDir, cwd) {
785
807
  const filePath = node_path_1.default.join(target.fullPath, file);
786
808
  let safeFilePath;
787
809
  try {
788
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'UAT file', { allowAbsolute: true });
810
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'UAT file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
789
811
  }
790
812
  catch {
791
813
  continue;
@@ -858,7 +880,7 @@ function scanVerificationGaps(planDir, cwd) {
858
880
  const filePath = node_path_1.default.join(target.fullPath, file);
859
881
  let safeFilePath;
860
882
  try {
861
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'VERIFICATION file', { allowAbsolute: true });
883
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'VERIFICATION file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
862
884
  }
863
885
  catch {
864
886
  continue;
@@ -920,7 +942,7 @@ function scanContextQuestions(planDir, cwd) {
920
942
  const filePath = node_path_1.default.join(target.fullPath, file);
921
943
  let safeFilePath;
922
944
  try {
923
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'CONTEXT file', { allowAbsolute: true });
945
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'CONTEXT file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
924
946
  }
925
947
  catch {
926
948
  continue;
@@ -1008,7 +1030,7 @@ function scanDeferredItems(planDir, cwd) {
1008
1030
  continue;
1009
1031
  let safeFilePath;
1010
1032
  try {
1011
- safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'deferred items file', { allowAbsolute: true });
1033
+ safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'deferred items file', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1012
1034
  }
1013
1035
  catch {
1014
1036
  continue;
@@ -1076,8 +1098,13 @@ function auditOpenArtifacts(cwd) {
1076
1098
  }
1077
1099
  })();
1078
1100
  const todos = (() => {
1101
+ // #4256: the ONE root-scoped category — todos are shared project state,
1102
+ // so the close gate reads todosDir(cwd) (the root), not the workstream-
1103
+ // scoped planDir every other scan below receives. Reading planDir here
1104
+ // made audit-open print "All artifact types clear. Safe to proceed."
1105
+ // with pending todos on disk under a workstream.
1079
1106
  try {
1080
- return scanTodos(planDir);
1107
+ return scanTodos(todosDir(cwd));
1081
1108
  }
1082
1109
  catch {
1083
1110
  return { items: [{ scan_error: true, filename: '', priority: '', area: '', summary: '' }], acknowledged: 0 };
@@ -1421,7 +1448,7 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1421
1448
  ioError(`no phase directory found for phase "${phase}"${archivedMilestone ? ` (archived-milestone "${archivedMilestone}")` : ''}`);
1422
1449
  }
1423
1450
  const filePath = node_path_1.default.join(targetDir, file);
1424
- const safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'audit acknowledge target', { allowAbsolute: true });
1451
+ const safeFilePath = (0, security_cjs_1.requireSafePath)(filePath, planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1425
1452
  if (!node_fs_1.default.existsSync(safeFilePath))
1426
1453
  ioError(`file not found: ${file}`);
1427
1454
  if (category === 'deferred_items') {
@@ -1452,6 +1479,13 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1452
1479
  }
1453
1480
  const content = node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
1454
1481
  const fm = extractFrontmatter(content, safeFilePath);
1482
+ // #4802: an unparseable frontmatter block is NOT an empty one — splicing
1483
+ // the marker-marked object over the file would discard every field the
1484
+ // author actually wrote. Refuse and name the file (the write-path
1485
+ // counterpart of the read-side FRONTMATTER_UNPARSEABLE contract).
1486
+ if (fm[FRONTMATTER_UNPARSEABLE] === true) {
1487
+ ioError(`refusing to acknowledge — the frontmatter of "${file}" is not parseable YAML (splicing would discard every other frontmatter field); fix the YAML syntax error first, then re-run`);
1488
+ }
1455
1489
  // Mixed-frame fix (security review, 4th instance on this branch): the
1456
1490
  // splice above and below stays keyed to RAW `content` (raw byte offsets
1457
1491
  // must not shift), but `scanUatGaps`/`scanContextQuestions` now derive
@@ -1503,7 +1537,7 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1503
1537
  if (category === 'debug_sessions') {
1504
1538
  if (!slug)
1505
1539
  ioError('--slug is required for --category debug_sessions');
1506
- safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'debug', `${slug}.md`), planDir, 'audit acknowledge target', { allowAbsolute: true });
1540
+ safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'debug', `${slug}.md`), planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1507
1541
  if (!node_fs_1.default.existsSync(safeFilePath))
1508
1542
  ioError(`file not found: debug/${slug}.md`);
1509
1543
  const content = node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
@@ -1512,7 +1546,7 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1512
1546
  else if (category === 'threads') {
1513
1547
  if (!slug)
1514
1548
  ioError('--slug is required for --category threads');
1515
- safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'threads', `${slug}.md`), planDir, 'audit acknowledge target', { allowAbsolute: true });
1549
+ safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'threads', `${slug}.md`), planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1516
1550
  if (!node_fs_1.default.existsSync(safeFilePath))
1517
1551
  ioError(`file not found: threads/${slug}.md`);
1518
1552
  const content = node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
@@ -1521,16 +1555,60 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1521
1555
  else if (category === 'seeds') {
1522
1556
  if (!seedId)
1523
1557
  ioError('--seed-id is required for --category seeds');
1524
- safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'seeds', `${seedId}.md`), planDir, 'audit acknowledge target', { allowAbsolute: true });
1525
- if (!node_fs_1.default.existsSync(safeFilePath))
1558
+ // #4378 (roll-in): `--seed-id` arrives as whichever id an audit/list
1559
+ // surface published — the canonical identity (frontmatter `id:` or the
1560
+ // derived prefix, e.g. `SEED-081`, `SEED-260914-k3x`) or, for callers
1561
+ // scripted against pre-canonical output, the full filename stem. Resolve
1562
+ // by scanning the seeds directory and matching each candidate's derived
1563
+ // identity (falling back to the literal stem), then prove the winner with
1564
+ // requireSafePath exactly like every other acknowledge target. The direct
1565
+ // `seeds/<seedId>.md` build stays as the final fallback so a genuine
1566
+ // miss still reports the same "file not found" error as before.
1567
+ const seedsAckDir = node_path_1.default.join(planDir, 'seeds');
1568
+ let ackCandidate = null;
1569
+ let ackEntries = [];
1570
+ try {
1571
+ ackEntries = node_fs_1.default.readdirSync(seedsAckDir).filter((n) => n.startsWith('SEED-') && n.endsWith('.md'));
1572
+ }
1573
+ catch {
1574
+ ackEntries = [];
1575
+ }
1576
+ for (const name of ackEntries) {
1577
+ const stem = node_path_1.default.basename(name, '.md');
1578
+ let fmId;
1579
+ try {
1580
+ const rawAck = (0, shell_command_projection_cjs_1.platformReadSync)(node_path_1.default.join(seedsAckDir, name));
1581
+ if (rawAck !== null)
1582
+ fmId = extractFrontmatter(normalizeLineEndings(rawAck), node_path_1.default.join(seedsAckDir, name)).id;
1583
+ }
1584
+ catch {
1585
+ fmId = undefined;
1586
+ }
1587
+ const { seed_id: derivedAckId } = commandsModule.deriveSeedIdentity(stem, fmId);
1588
+ if (derivedAckId === seedId || stem === seedId) {
1589
+ ackCandidate = name;
1590
+ break;
1591
+ }
1592
+ }
1593
+ if (ackCandidate !== null) {
1594
+ safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(seedsAckDir, ackCandidate), planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1595
+ }
1596
+ else {
1597
+ safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(seedsAckDir, `${seedId}.md`), planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1526
1598
  ioError(`file not found: seeds/${seedId}.md`);
1599
+ }
1527
1600
  const content = node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
1528
1601
  currentValue = (extractFrontmatter(content, safeFilePath).status || 'dormant').toLowerCase();
1529
1602
  }
1530
1603
  else if (category === 'todos') {
1531
1604
  if (!filename)
1532
1605
  ioError('--filename is required for --category todos');
1533
- safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'todos', 'pending', filename), planDir, 'audit acknowledge target', { allowAbsolute: true });
1606
+ // #4256: todos are root-scoped shared state — derive the todos base and
1607
+ // pass it as BOTH the path base and the requireSafePath boundary. The
1608
+ // old workstream-scoped planDir boundary would refuse a root todos file
1609
+ // outright, and even a path fix alone would have thrown here.
1610
+ const rootTodos = todosDir(cwd);
1611
+ safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(rootTodos, 'pending', filename), rootTodos, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1534
1612
  if (!node_fs_1.default.existsSync(safeFilePath))
1535
1613
  ioError(`file not found: todos/pending/${filename}`);
1536
1614
  currentValue = ''; // presence-only — see scanTodos
@@ -1538,14 +1616,14 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1538
1616
  else if (category === 'quick_tasks') {
1539
1617
  if (!quickDir)
1540
1618
  ioError('--dir is required for --category quick_tasks');
1541
- const taskDir = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'quick', quickDir), planDir, 'audit acknowledge target dir', { allowAbsolute: true });
1619
+ const taskDir = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(planDir, 'quick', quickDir), planDir, 'audit acknowledge target dir', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1542
1620
  if (!node_fs_1.default.existsSync(taskDir))
1543
1621
  ioError(`directory not found: quick/${quickDir}`);
1544
1622
  // Shared with scanQuickTasks (#3458 follow-up) so the reader and this
1545
1623
  // writer can never disagree about which file is the task's record.
1546
1624
  const resolvedSummaryPath = resolveQuickTaskSummaryFile(taskDir, quickDir);
1547
1625
  if (resolvedSummaryPath) {
1548
- safeFilePath = (0, security_cjs_1.requireSafePath)(resolvedSummaryPath, planDir, 'audit acknowledge target', { allowAbsolute: true });
1626
+ safeFilePath = (0, security_cjs_1.requireSafePath)(resolvedSummaryPath, planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1549
1627
  const content = node_fs_1.default.readFileSync(safeFilePath, 'utf-8');
1550
1628
  currentValue = (extractFrontmatter(content, safeFilePath).status || 'unknown').toLowerCase();
1551
1629
  }
@@ -1556,7 +1634,7 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1556
1634
  // acknowledgment's own snapshot of "no summary exists yet", which
1557
1635
  // self-invalidates the moment a real SUMMARY.md is written (the
1558
1636
  // scanner then reads THAT file's own status instead).
1559
- safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(taskDir, `${quickDir}-SUMMARY.md`), planDir, 'audit acknowledge target', { allowAbsolute: true });
1637
+ safeFilePath = (0, security_cjs_1.requireSafePath)(node_path_1.default.join(taskDir, `${quickDir}-SUMMARY.md`), planDir, 'audit acknowledge target', security_cjs_1.PathAcceptance.AbsoluteInsideRoot);
1560
1638
  currentValue = 'missing';
1561
1639
  createIfMissing = true;
1562
1640
  fmForCreate = { status: 'missing' };
@@ -1568,6 +1646,13 @@ function cmdAuditAcknowledge(cwd, args, raw) {
1568
1646
  }
1569
1647
  const presenceOnly = category === 'todos';
1570
1648
  const fm = createIfMissing ? fmForCreate : extractFrontmatter(node_fs_1.default.readFileSync(safeFilePath, 'utf-8'), safeFilePath);
1649
+ // #4802: same unparseable-frontmatter refusal as the phase-scoped branch —
1650
+ // createIfMissing never reaches this extract (it only fires when the file is
1651
+ // absent), so an existing file with broken YAML refuses instead of splicing
1652
+ // a near-empty object over every field the author wrote.
1653
+ if (!createIfMissing && fm[FRONTMATTER_UNPARSEABLE] === true) {
1654
+ ioError(`refusing to acknowledge — the frontmatter of "${safeFilePath}" is not parseable YAML (splicing would discard every other frontmatter field); fix the YAML syntax error first, then re-run`);
1655
+ }
1571
1656
  fm.audit_acknowledged = presenceOnly ? { ...markerBase } : { ...markerBase, [snapshotKey]: currentValue };
1572
1657
  const newContent = createIfMissing
1573
1658
  ? spliceFrontmatter('', fm)