@opengsd/gsd-core 1.15.0 → 1.16.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 (487) 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 +85 -5
  4. package/agents/gsd-code-fixer.compact.md +1 -1
  5. package/agents/gsd-code-fixer.md +1 -1
  6. package/agents/gsd-code-reviewer.compact.md +5 -3
  7. package/agents/gsd-code-reviewer.md +8 -6
  8. package/agents/gsd-debug-session-manager.compact.md +1 -1
  9. package/agents/gsd-debug-session-manager.md +1 -1
  10. package/agents/gsd-debugger.md +2 -2
  11. package/agents/gsd-eval-auditor.compact.md +1 -1
  12. package/agents/gsd-eval-auditor.md +1 -1
  13. package/agents/gsd-executor.md +4 -4
  14. package/agents/gsd-intel-updater.compact.md +1 -1
  15. package/agents/gsd-intel-updater.md +1 -1
  16. package/agents/gsd-mempalace-curator.md +2 -2
  17. package/agents/gsd-phase-researcher.md +1 -1
  18. package/agents/gsd-plan-checker.md +7 -2
  19. package/agents/gsd-planner.md +3 -3
  20. package/agents/gsd-project-researcher.compact.md +1 -1
  21. package/agents/gsd-project-researcher.md +1 -1
  22. package/agents/gsd-research-synthesizer.compact.md +1 -1
  23. package/agents/gsd-research-synthesizer.md +1 -1
  24. package/agents/gsd-ui-auditor.compact.md +21 -30
  25. package/agents/gsd-ui-auditor.md +42 -51
  26. package/agents/gsd-ui-researcher.compact.md +1 -1
  27. package/agents/gsd-ui-researcher.md +1 -1
  28. package/agents/gsd-verifier.md +31 -9
  29. package/bin/install.js +126 -196
  30. package/commands/gsd/add-tests.md +6 -1
  31. package/commands/gsd/ai-integration-phase.md +6 -1
  32. package/commands/gsd/audit-fix.md +5 -0
  33. package/commands/gsd/audit-milestone.md +6 -1
  34. package/commands/gsd/autonomous.md +5 -0
  35. package/commands/gsd/capture.md +8 -4
  36. package/commands/gsd/code-review.md +7 -2
  37. package/commands/gsd/complete-milestone.md +4 -0
  38. package/commands/gsd/config.md +7 -3
  39. package/commands/gsd/debug.md +11 -7
  40. package/commands/gsd/discuss-phase.md +7 -3
  41. package/commands/gsd/docs-update.md +12 -7
  42. package/commands/gsd/eval-review.md +6 -1
  43. package/commands/gsd/execute-phase.md +12 -7
  44. package/commands/gsd/extract-learnings.md +5 -0
  45. package/commands/gsd/fast.md +4 -0
  46. package/commands/gsd/forensics.md +5 -1
  47. package/commands/gsd/graphify.md +10 -6
  48. package/commands/gsd/health.md +5 -0
  49. package/commands/gsd/help.md +7 -2
  50. package/commands/gsd/import.md +7 -3
  51. package/commands/gsd/inbox.md +5 -0
  52. package/commands/gsd/ingest-docs.md +5 -1
  53. package/commands/gsd/manager.md +6 -1
  54. package/commands/gsd/map-codebase.md +7 -3
  55. package/commands/gsd/mempalace-capture.md +5 -1
  56. package/commands/gsd/mempalace-recall.md +5 -1
  57. package/commands/gsd/milestone-summary.md +5 -1
  58. package/commands/gsd/mvp-phase.md +8 -3
  59. package/commands/gsd/new-milestone.md +6 -1
  60. package/commands/gsd/new-project.md +5 -0
  61. package/commands/gsd/next.md +6 -1
  62. package/commands/gsd/ns-context.md +4 -0
  63. package/commands/gsd/ns-ideate.md +4 -0
  64. package/commands/gsd/ns-manage.md +4 -0
  65. package/commands/gsd/ns-project.md +4 -0
  66. package/commands/gsd/ns-review.md +4 -0
  67. package/commands/gsd/ns-workflow.md +4 -0
  68. package/commands/gsd/onboard.md +6 -1
  69. package/commands/gsd/pause-work.md +5 -1
  70. package/commands/gsd/phase.md +8 -4
  71. package/commands/gsd/plan-phase.md +6 -1
  72. package/commands/gsd/plan-review-convergence.md +5 -1
  73. package/commands/gsd/pr-branch.md +4 -0
  74. package/commands/gsd/profile-user.md +5 -1
  75. package/commands/gsd/progress.md +6 -1
  76. package/commands/gsd/quick-batch.md +20 -8
  77. package/commands/gsd/quick.md +12 -7
  78. package/commands/gsd/review.md +5 -1
  79. package/commands/gsd/secure-phase.md +6 -1
  80. package/commands/gsd/ship.md +5 -0
  81. package/commands/gsd/sketch.md +7 -2
  82. package/commands/gsd/spec-phase.md +5 -1
  83. package/commands/gsd/spike.md +8 -3
  84. package/commands/gsd/surface.md +5 -1
  85. package/commands/gsd/thread.md +4 -0
  86. package/commands/gsd/ui-phase.md +6 -1
  87. package/commands/gsd/ui-review.md +6 -1
  88. package/commands/gsd/ultraplan-phase.md +5 -1
  89. package/commands/gsd/undo.md +5 -1
  90. package/commands/gsd/update.md +6 -2
  91. package/commands/gsd/validate-phase.md +6 -1
  92. package/commands/gsd/verify-work.md +6 -1
  93. package/commands/gsd/workspace.md +7 -3
  94. package/gsd-core/bin/gsd-tools.cjs +142 -56
  95. package/gsd-core/bin/lib/active-workstream-store.cjs +15 -0
  96. package/gsd-core/bin/lib/agent-install-check.cjs +4 -1
  97. package/gsd-core/bin/lib/audit.cjs +78 -44
  98. package/gsd-core/bin/lib/broken-windows.cjs +13 -13
  99. package/gsd-core/bin/lib/capability-activation.cjs +9 -4
  100. package/gsd-core/bin/lib/capability-registry.cjs +183 -103
  101. package/gsd-core/bin/lib/capability-validator.cjs +16 -0
  102. package/gsd-core/bin/lib/check-auto-mode.cjs +35 -0
  103. package/gsd-core/bin/lib/check-command-router.cjs +164 -1712
  104. package/gsd-core/bin/lib/cjs-command-router-adapter.cjs +13 -2
  105. package/gsd-core/bin/lib/cli-exit.cjs +12 -0
  106. package/gsd-core/bin/lib/codex-agent-toml.cjs +11 -8
  107. package/gsd-core/bin/lib/command-aliases.cjs +7 -0
  108. package/gsd-core/bin/lib/command-routing-hub.cjs +48 -1
  109. package/gsd-core/bin/lib/commands.cjs +173 -170
  110. package/gsd-core/bin/lib/complexity-trigger.cjs +8 -7
  111. package/gsd-core/bin/lib/config.cjs +43 -12
  112. package/gsd-core/bin/lib/core-utils.cjs +6 -1
  113. package/gsd-core/bin/lib/coverage.cjs +4 -8
  114. package/gsd-core/bin/lib/decision-coverage-support.cjs +259 -0
  115. package/gsd-core/bin/lib/drift.cjs +177 -42
  116. package/gsd-core/bin/lib/frontmatter-fence.cjs +90 -0
  117. package/gsd-core/bin/lib/frontmatter-splice.cjs +494 -0
  118. package/gsd-core/bin/lib/frontmatter.cjs +413 -234
  119. package/gsd-core/bin/lib/gap-checker.cjs +72 -29
  120. package/gsd-core/bin/lib/gate-api-coverage-verify-pre.cjs +381 -0
  121. package/gsd-core/bin/lib/gate-args.cjs +53 -0
  122. package/gsd-core/bin/lib/gate-codebase-drift.cjs +285 -0
  123. package/gsd-core/bin/lib/gate-config.cjs +46 -0
  124. package/gsd-core/bin/lib/gate-context-drift.cjs +141 -0
  125. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +169 -0
  126. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +126 -0
  127. package/gsd-core/bin/lib/gate-evaluation-scope.cjs +555 -0
  128. package/gsd-core/bin/lib/gate-evidence.cjs +138 -0
  129. package/gsd-core/bin/lib/gate-exit.cjs +27 -0
  130. package/gsd-core/bin/lib/gate-gap-analysis-plan-post.cjs +61 -0
  131. package/gsd-core/bin/lib/gate-phase-context.cjs +170 -0
  132. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +1 -1
  133. package/gsd-core/bin/lib/gate-predicate.cjs +165 -0
  134. package/gsd-core/bin/lib/gate-prohibition-enforcement.cjs +94 -0
  135. package/gsd-core/bin/lib/gate-schema-drift.cjs +165 -0
  136. package/gsd-core/bin/lib/gate-tdd-red-evidence.cjs +100 -0
  137. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +182 -0
  138. package/gsd-core/bin/lib/gate-ui-plan.cjs +86 -0
  139. package/gsd-core/bin/lib/gate-ui-safety.cjs +80 -0
  140. package/gsd-core/bin/lib/gate-verdict.cjs +64 -0
  141. package/gsd-core/bin/lib/gate-verify-command-paths.cjs +78 -0
  142. package/gsd-core/bin/lib/gate-verify-failure-directions.cjs +41 -0
  143. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +43 -47
  144. package/gsd-core/bin/lib/health-diagnostic.cjs +45 -8
  145. package/gsd-core/bin/lib/init.cjs +162 -96
  146. package/gsd-core/bin/lib/install-engine.cjs +19 -35
  147. package/gsd-core/bin/lib/install-profiles.cjs +7 -4
  148. package/gsd-core/bin/lib/io.cjs +122 -3
  149. package/gsd-core/bin/lib/loop-resolver.cjs +95 -0
  150. package/gsd-core/bin/lib/markdown-sectionizer.cjs +75 -1
  151. package/gsd-core/bin/lib/milestone.cjs +19 -1
  152. package/gsd-core/bin/lib/model-resolver.cjs +18 -18
  153. package/gsd-core/bin/lib/observability/event.cjs +1 -1
  154. package/gsd-core/bin/lib/observability/logger.cjs +46 -1
  155. package/gsd-core/bin/lib/pattern.cjs +10 -0
  156. package/gsd-core/bin/lib/phase-command-router.cjs +11 -4
  157. package/gsd-core/bin/lib/phase-estimation.cjs +5 -4
  158. package/gsd-core/bin/lib/phase-id.cjs +1 -1
  159. package/gsd-core/bin/lib/phase-lifecycle.cjs +9 -2
  160. package/gsd-core/bin/lib/phase-status.cjs +360 -0
  161. package/gsd-core/bin/lib/phase.cjs +280 -80
  162. package/gsd-core/bin/lib/plan-document.cjs +93 -19
  163. package/gsd-core/bin/lib/plan-drift-guard.cjs +5 -0
  164. package/gsd-core/bin/lib/planning-document.cjs +273 -40
  165. package/gsd-core/bin/lib/planning-inspect.cjs +42 -8
  166. package/gsd-core/bin/lib/planning-snapshot.cjs +18 -0
  167. package/gsd-core/bin/lib/planning-workspace.cjs +76 -53
  168. package/gsd-core/bin/lib/pristine-baseline.cjs +10 -0
  169. package/gsd-core/bin/lib/profile-output.cjs +6 -3
  170. package/gsd-core/bin/lib/prohibition-enforcement.cjs +0 -55
  171. package/gsd-core/bin/lib/quick-batch-command-router.cjs +35 -9
  172. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +11 -8
  173. package/gsd-core/bin/lib/real-home-guard.cjs +9 -1
  174. package/gsd-core/bin/lib/report-parser.cjs +269 -0
  175. package/gsd-core/bin/lib/roadmap-command-router.cjs +13 -15
  176. package/gsd-core/bin/lib/roadmap-parser.cjs +82 -15
  177. package/gsd-core/bin/lib/roadmap-upgrade.cjs +127 -65
  178. package/gsd-core/bin/lib/roadmap.cjs +169 -72
  179. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +91 -157
  180. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +2 -1
  181. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +57 -1
  182. package/gsd-core/bin/lib/runtime-homes.cjs +13 -10
  183. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +307 -591
  184. package/gsd-core/bin/lib/runtime-name-policy.cjs +142 -27
  185. package/gsd-core/bin/lib/runtime-slash.cjs +47 -30
  186. package/gsd-core/bin/lib/shell-command-projection.cjs +37 -2
  187. package/gsd-core/bin/lib/smart-entry.cjs +19 -3
  188. package/gsd-core/bin/lib/stale-bake-guard.cjs +32 -48
  189. package/gsd-core/bin/lib/state-contract.cjs +15 -18
  190. package/gsd-core/bin/lib/state-document.cjs +100 -22
  191. package/gsd-core/bin/lib/state.cjs +214 -105
  192. package/gsd-core/bin/lib/surface.cjs +2 -1
  193. package/gsd-core/bin/lib/tdd-red-evidence.cjs +48 -152
  194. package/gsd-core/bin/lib/uat-predicate.cjs +313 -35
  195. package/gsd-core/bin/lib/uat.cjs +424 -7
  196. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +94 -58
  197. package/gsd-core/bin/lib/vendor/README.md +31 -9
  198. package/gsd-core/bin/lib/vendor/saxes.cjs +1934 -0
  199. package/gsd-core/bin/lib/vendor/saxes.cjs.LICENSE.txt +92 -0
  200. package/gsd-core/bin/lib/vendor/tap-parser.cjs +8927 -0
  201. package/gsd-core/bin/lib/vendor/tap-parser.cjs.LICENSE.txt +152 -0
  202. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  203. package/gsd-core/bin/lib/verification.cjs +1165 -314
  204. package/gsd-core/bin/lib/verify-command-router.cjs +18 -7
  205. package/gsd-core/bin/lib/verify.cjs +286 -567
  206. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +14 -6
  207. package/gsd-core/bin/lib/workstream-inventory.cjs +31 -21
  208. package/gsd-core/bin/lib/workstream-name-policy.cjs +31 -1
  209. package/gsd-core/bin/lib/workstream.cjs +11 -2
  210. package/gsd-core/bin/shared/config-defaults.manifest.json +5 -0
  211. package/gsd-core/bin/shared/config-schema.manifest.json +4 -0
  212. package/gsd-core/references/autonomous-smart-discuss.md +2 -1
  213. package/gsd-core/references/autonomous-ui-design-contract.md +3 -3
  214. package/gsd-core/references/execute-mvp-tdd.md +5 -10
  215. package/gsd-core/references/execute-phase-between-wave-reset.md +3 -0
  216. package/gsd-core/references/execute-phase-response-language.md +1 -1
  217. package/gsd-core/references/gsd-run-resolver.md +1 -1
  218. package/gsd-core/references/loop-hook-dispatch.md +7 -1
  219. package/gsd-core/references/offer-next.md +1 -1
  220. package/gsd-core/references/planning-config.md +1 -1
  221. package/gsd-core/references/spidr-splitting.md +1 -1
  222. package/gsd-core/references/tdd.md +10 -4
  223. package/gsd-core/references/verifier-phase-gates.md +5 -2
  224. package/gsd-core/references/verify-mvp-mode.md +2 -2
  225. package/gsd-core/references/workstream-flag.md +33 -3
  226. package/gsd-core/templates/README.md +1 -1
  227. package/gsd-core/templates/UAT.md +17 -1
  228. package/gsd-core/templates/config.json +2 -11
  229. package/gsd-core/templates/verification-report.md +1 -1
  230. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  231. package/gsd-core/workflows/add-backlog.md +1 -1
  232. package/gsd-core/workflows/add-phase.md +8 -7
  233. package/gsd-core/workflows/add-tests.md +3 -2
  234. package/gsd-core/workflows/add-todo.md +4 -3
  235. package/gsd-core/workflows/ai-integration-phase.md +3 -2
  236. package/gsd-core/workflows/audit-fix.md +1 -1
  237. package/gsd-core/workflows/audit-milestone.md +4 -3
  238. package/gsd-core/workflows/audit-uat.md +1 -1
  239. package/gsd-core/workflows/autonomous.md +28 -18
  240. package/gsd-core/workflows/check-todos.md +6 -5
  241. package/gsd-core/workflows/cleanup.md +1 -1
  242. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +20 -13
  243. package/gsd-core/workflows/code-review-fix.md +5 -4
  244. package/gsd-core/workflows/code-review.md +84 -90
  245. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +4 -3
  246. package/gsd-core/workflows/complete-milestone.md +12 -7
  247. package/gsd-core/workflows/debug.md +30 -5
  248. package/gsd-core/workflows/diagnose-issues.md +3 -2
  249. package/gsd-core/workflows/discuss-phase/modes/advisor.md +1 -1
  250. package/gsd-core/workflows/discuss-phase/modes/chain.md +1 -1
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +4 -3
  252. package/gsd-core/workflows/discuss-phase.md +4 -3
  253. package/gsd-core/workflows/do.md +1 -1
  254. package/gsd-core/workflows/docs-update.md +4 -3
  255. package/gsd-core/workflows/edit-phase.md +4 -3
  256. package/gsd-core/workflows/eval-review.md +5 -3
  257. package/gsd-core/workflows/execute-phase/detail/elaboration.md +1 -1
  258. package/gsd-core/workflows/execute-phase/steps/code-review-disposition.md +4 -2
  259. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +15 -4
  260. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +10 -7
  261. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +3 -1
  263. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +2 -2
  264. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  265. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +1 -1
  266. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +1 -1
  267. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +1 -1
  268. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +1 -1
  269. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +2 -0
  270. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +1 -1
  271. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +187 -0
  272. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +2 -3
  273. package/gsd-core/workflows/execute-phase.md +78 -170
  274. package/gsd-core/workflows/execute-plan.md +13 -19
  275. package/gsd-core/workflows/explore.md +2 -2
  276. package/gsd-core/workflows/extract-learnings.md +3 -2
  277. package/gsd-core/workflows/fast.md +1 -1
  278. package/gsd-core/workflows/forensics.md +1 -1
  279. package/gsd-core/workflows/graduation.md +1 -1
  280. package/gsd-core/workflows/health.md +2 -1
  281. package/gsd-core/workflows/import.md +3 -2
  282. package/gsd-core/workflows/inbox.md +1 -1
  283. package/gsd-core/workflows/ingest-docs.md +1 -1
  284. package/gsd-core/workflows/insert-phase.md +4 -3
  285. package/gsd-core/workflows/list-seeds.md +1 -1
  286. package/gsd-core/workflows/list-workspaces.md +1 -1
  287. package/gsd-core/workflows/manager.md +5 -3
  288. package/gsd-core/workflows/map-codebase.md +4 -3
  289. package/gsd-core/workflows/milestone-summary.md +3 -2
  290. package/gsd-core/workflows/mvp-phase.md +14 -14
  291. package/gsd-core/workflows/new-milestone.md +10 -10
  292. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  293. package/gsd-core/workflows/new-project.md +1 -1
  294. package/gsd-core/workflows/new-workspace.md +1 -1
  295. package/gsd-core/workflows/next.md +1 -1
  296. package/gsd-core/workflows/pause-work.md +2 -2
  297. package/gsd-core/workflows/plan-phase/detail/elaboration.md +1 -1
  298. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +1 -1
  299. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +1 -1
  300. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +1 -1
  301. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +1 -1
  302. package/gsd-core/workflows/plan-phase.md +27 -18
  303. package/gsd-core/workflows/plan-review-convergence.md +1 -1
  304. package/gsd-core/workflows/plant-seed.md +1 -1
  305. package/gsd-core/workflows/pr-branch.md +1 -1
  306. package/gsd-core/workflows/profile-user.md +1 -1
  307. package/gsd-core/workflows/progress.md +19 -49
  308. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +4 -2
  309. package/gsd-core/workflows/quick/steps/quick-verification.md +4 -4
  310. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +30 -11
  311. package/gsd-core/workflows/quick-batch/steps/batch-init.md +1 -1
  312. package/gsd-core/workflows/quick-batch/steps/completion.md +1 -1
  313. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +1 -1
  314. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +1 -1
  315. package/gsd-core/workflows/quick-batch/steps/research-phase.md +1 -1
  316. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +1 -1
  317. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +9 -3
  318. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +1 -1
  319. package/gsd-core/workflows/quick-batch.md +15 -10
  320. package/gsd-core/workflows/quick.md +42 -31
  321. package/gsd-core/workflows/remove-phase.md +3 -2
  322. package/gsd-core/workflows/remove-workspace.md +1 -1
  323. package/gsd-core/workflows/resume-project.md +3 -2
  324. package/gsd-core/workflows/review.md +3 -2
  325. package/gsd-core/workflows/scan.md +3 -2
  326. package/gsd-core/workflows/secure-phase.md +11 -12
  327. package/gsd-core/workflows/settings-advanced.md +1 -1
  328. package/gsd-core/workflows/settings-integrations.md +1 -1
  329. package/gsd-core/workflows/settings.md +1 -1
  330. package/gsd-core/workflows/ship.md +12 -12
  331. package/gsd-core/workflows/sketch-wrap-up.md +1 -1
  332. package/gsd-core/workflows/sketch.md +1 -1
  333. package/gsd-core/workflows/smart-entry.md +1 -1
  334. package/gsd-core/workflows/spec-phase.md +1 -1
  335. package/gsd-core/workflows/spike-wrap-up.md +1 -1
  336. package/gsd-core/workflows/spike.md +1 -1
  337. package/gsd-core/workflows/stats.md +1 -1
  338. package/gsd-core/workflows/sync-skills.md +1 -1
  339. package/gsd-core/workflows/thread.md +2 -2
  340. package/gsd-core/workflows/transition.md +13 -23
  341. package/gsd-core/workflows/ui-phase.md +5 -4
  342. package/gsd-core/workflows/ui-review.md +4 -3
  343. package/gsd-core/workflows/ultraplan-phase.md +3 -2
  344. package/gsd-core/workflows/undo.md +1 -1
  345. package/gsd-core/workflows/validate-phase.md +10 -12
  346. package/gsd-core/workflows/verify-work/detail/elaboration.md +43 -3
  347. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +1 -1
  348. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -3
  349. package/gsd-core/workflows/verify-work.md +68 -59
  350. package/hooks/dist/gsd-agent-isolation-guard.js +8 -0
  351. package/hooks/dist/gsd-check-update-worker.js +8 -0
  352. package/hooks/dist/gsd-check-update.js +8 -0
  353. package/hooks/dist/gsd-context-monitor.js +31 -8
  354. package/hooks/dist/gsd-cursor-subagent-start.js +8 -0
  355. package/hooks/dist/gsd-secret-read-guard.js +147 -16
  356. package/hooks/dist/gsd-statusline.js +15 -7
  357. package/hooks/dist/gsd-update-banner.js +8 -0
  358. package/hooks/dist/gsd-windsurf-pre-write.js +11 -2
  359. package/hooks/dist/gsd-workflow-guard.js +5 -4
  360. package/hooks/dist/gsd-worktree-path-guard.js +6 -2
  361. package/hooks/dist/lib/cli-exit.js +12 -0
  362. package/hooks/dist/lib/git-probe.js +17 -1
  363. package/hooks/dist/lib/isolation-sentinel.js +2 -2
  364. package/hooks/gsd-agent-isolation-guard.js +8 -0
  365. package/hooks/gsd-check-update-worker.js +8 -0
  366. package/hooks/gsd-check-update.js +8 -0
  367. package/hooks/gsd-context-monitor.js +31 -8
  368. package/hooks/gsd-cursor-subagent-start.js +8 -0
  369. package/hooks/gsd-secret-read-guard.js +147 -16
  370. package/hooks/gsd-statusline.js +15 -7
  371. package/hooks/gsd-update-banner.js +8 -0
  372. package/hooks/gsd-windsurf-pre-write.js +11 -2
  373. package/hooks/gsd-workflow-guard.js +5 -4
  374. package/hooks/gsd-worktree-path-guard.js +6 -2
  375. package/hooks/hooks.json +5 -5
  376. package/hooks/lib/cli-exit.js +12 -0
  377. package/hooks/lib/git-probe.js +17 -1
  378. package/hooks/lib/isolation-sentinel.js +2 -2
  379. package/package.json +21 -4
  380. package/scripts/changeset/parse.cjs +52 -4
  381. package/scripts/ci-timeout-report.cjs +770 -4
  382. package/scripts/command-contract-helpers.cjs +12 -8
  383. package/scripts/docs-guard-registry.cjs +6 -0
  384. package/scripts/gen-features.cjs +13 -8
  385. package/scripts/gen-hooks-cli-exit.cjs +12 -28
  386. package/scripts/gen-loop-host-contract.cjs +10 -1
  387. package/scripts/gen-platform-conformance-tier.cjs +187 -1
  388. package/scripts/gen-plugin-skills.cjs +87 -1
  389. package/scripts/gen-research-agents.cjs +24 -31
  390. package/scripts/gen-scripts-cli-exit.cjs +30 -3
  391. package/scripts/gen-test-timings.cjs +32 -7
  392. package/scripts/lib/cli-exit.cjs +12 -0
  393. package/scripts/lib/macos-conformance-tier.generated.cjs +20 -2
  394. package/scripts/lib/ndjson-reporter.cjs +28 -3
  395. package/scripts/lib/platform-conformance-tier.generated.cjs +34 -5
  396. package/scripts/lib/registration-ledger-preload.cjs +155 -0
  397. package/scripts/lib/vendor-bundle.cjs +59 -0
  398. package/scripts/lib/vendor-licenses/saxes-6.0.0.txt +64 -0
  399. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  400. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  401. package/scripts/lint-completion-predicate-drift.cjs +18 -19
  402. package/scripts/lint-descriptions.cjs +7 -3
  403. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +10 -0
  404. package/scripts/lint-eslint-glob-coverage.allowlist.json +20 -0
  405. package/scripts/lint-frontmatter-fence-drift.cjs +313 -0
  406. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +122 -16
  407. package/scripts/lint-phase-enumeration-drift.cjs +12 -8
  408. package/scripts/lint-phase-id-drift.cjs +29 -0
  409. package/scripts/lint-planning-document-positive-control.cjs +329 -0
  410. package/scripts/lint-response-language-coverage.cjs +3 -0
  411. package/scripts/lint-skill-deps.cjs +7 -3
  412. package/scripts/lint-test-file-count.allowlist.json +11 -1
  413. package/scripts/lint-test-file-count.cjs +34 -1
  414. package/scripts/lint-vendored-deps.cjs +41 -5
  415. package/scripts/lint-workflow-shellcheck-baseline.json +10 -10
  416. package/scripts/mutation-matrix.cjs +50 -5
  417. package/scripts/require-issue-link-policy.cjs +6 -2
  418. package/scripts/sync-runtime-launcher.cjs +184 -2
  419. package/scripts/verify-npm-publish.cjs +76 -20
  420. package/skills/gsd-add-tests/SKILL.md +6 -1
  421. package/skills/gsd-ai-integration-phase/SKILL.md +6 -1
  422. package/skills/gsd-audit-fix/SKILL.md +5 -0
  423. package/skills/gsd-audit-milestone/SKILL.md +6 -1
  424. package/skills/gsd-autonomous/SKILL.md +5 -0
  425. package/skills/gsd-capture/SKILL.md +8 -4
  426. package/skills/gsd-code-review/SKILL.md +7 -2
  427. package/skills/gsd-complete-milestone/SKILL.md +4 -0
  428. package/skills/gsd-config/SKILL.md +8 -4
  429. package/skills/gsd-debug/SKILL.md +11 -7
  430. package/skills/gsd-discuss-phase/SKILL.md +7 -3
  431. package/skills/gsd-docs-update/SKILL.md +12 -7
  432. package/skills/gsd-eval-review/SKILL.md +6 -1
  433. package/skills/gsd-execute-phase/SKILL.md +12 -7
  434. package/skills/gsd-extract-learnings/SKILL.md +5 -0
  435. package/skills/gsd-fast/SKILL.md +4 -0
  436. package/skills/gsd-forensics/SKILL.md +5 -1
  437. package/skills/gsd-graphify/SKILL.md +10 -6
  438. package/skills/gsd-health/SKILL.md +5 -0
  439. package/skills/gsd-help/SKILL.md +7 -2
  440. package/skills/gsd-import/SKILL.md +7 -3
  441. package/skills/gsd-inbox/SKILL.md +5 -0
  442. package/skills/gsd-ingest-docs/SKILL.md +5 -1
  443. package/skills/gsd-manager/SKILL.md +6 -1
  444. package/skills/gsd-map-codebase/SKILL.md +7 -3
  445. package/skills/gsd-mempalace-capture/SKILL.md +5 -1
  446. package/skills/gsd-mempalace-recall/SKILL.md +5 -1
  447. package/skills/gsd-milestone-summary/SKILL.md +5 -1
  448. package/skills/gsd-mvp-phase/SKILL.md +8 -3
  449. package/skills/gsd-new-milestone/SKILL.md +6 -1
  450. package/skills/gsd-new-project/SKILL.md +5 -0
  451. package/skills/gsd-next/SKILL.md +6 -1
  452. package/skills/gsd-ns-context/SKILL.md +4 -0
  453. package/skills/gsd-ns-ideate/SKILL.md +4 -0
  454. package/skills/gsd-ns-manage/SKILL.md +4 -0
  455. package/skills/gsd-ns-project/SKILL.md +4 -0
  456. package/skills/gsd-ns-review/SKILL.md +4 -0
  457. package/skills/gsd-ns-workflow/SKILL.md +4 -0
  458. package/skills/gsd-onboard/SKILL.md +6 -1
  459. package/skills/gsd-pause-work/SKILL.md +5 -1
  460. package/skills/gsd-phase/SKILL.md +8 -4
  461. package/skills/gsd-plan-phase/SKILL.md +6 -1
  462. package/skills/gsd-plan-review-convergence/SKILL.md +5 -1
  463. package/skills/gsd-pr-branch/SKILL.md +4 -0
  464. package/skills/gsd-profile-user/SKILL.md +5 -1
  465. package/skills/gsd-progress/SKILL.md +6 -1
  466. package/skills/gsd-quick/SKILL.md +16 -10
  467. package/skills/gsd-quick-batch/SKILL.md +20 -8
  468. package/skills/gsd-review/SKILL.md +5 -1
  469. package/skills/gsd-review-backlog/SKILL.md +3 -2
  470. package/skills/gsd-secure-phase/SKILL.md +6 -1
  471. package/skills/gsd-ship/SKILL.md +5 -0
  472. package/skills/gsd-sketch/SKILL.md +7 -2
  473. package/skills/gsd-spec-phase/SKILL.md +5 -1
  474. package/skills/gsd-spike/SKILL.md +8 -3
  475. package/skills/gsd-surface/SKILL.md +5 -1
  476. package/skills/gsd-thread/SKILL.md +4 -0
  477. package/skills/gsd-ui-phase/SKILL.md +6 -1
  478. package/skills/gsd-ui-review/SKILL.md +6 -1
  479. package/skills/gsd-ultraplan-phase/SKILL.md +5 -1
  480. package/skills/gsd-undo/SKILL.md +5 -1
  481. package/skills/gsd-update/SKILL.md +6 -2
  482. package/skills/gsd-validate-phase/SKILL.md +6 -1
  483. package/skills/gsd-verify-work/SKILL.md +6 -1
  484. package/skills/gsd-workspace/SKILL.md +7 -3
  485. package/skills/gsd-workstreams/SKILL.md +6 -6
  486. package/vscode/package.json +1 -1
  487. package/gsd-core/workflows/execute-phase/steps/stale-reverification.md +0 -24
@@ -4,17 +4,31 @@
4
4
  * Detects structural drift between a committed codebase and the
5
5
  * `.planning/codebase/STRUCTURE.md` map produced by `gsd-codebase-mapper`.
6
6
  *
7
- * Four categories of drift element:
7
+ * Six categories of drift element:
8
8
  * - new_dir → a newly-added file whose directory prefix does not appear
9
- * in STRUCTURE.md
9
+ * in any generated document
10
10
  * - barrel → a newly-added barrel export at
11
11
  * (packages|apps)/<name>/src/index.(ts|tsx|js|mjs|cjs)
12
12
  * - migration → a newly-added migration file under one of the recognized
13
13
  * migration directories (supabase, prisma, drizzle, src/migrations, …)
14
14
  * - route → a newly-added route module under a `routes/` or `api/` dir
15
+ * - modified → a modified file whose directory prefix IS mapped
16
+ * - deleted → a deleted file whose directory prefix IS mapped
17
+ *
18
+ * Inverse-territory rule (#4886, #5134): an addition is drift OUTSIDE mapped
19
+ * territory (the map cannot describe what did not exist); a modification or
20
+ * deletion is drift INSIDE mapped territory (the map describes it and it
21
+ * changed). A rename is a deletion of the old path plus an addition of the new
22
+ * path; a copy is an addition of the new path. Mapped territory is any
23
+ * directory prefix named in ANY provided generated document.
15
24
  *
16
25
  * Each file is counted at most once; when a file matches multiple categories
17
- * the most specific category wins (migration > route > barrel > new_dir).
26
+ * the most specific category wins
27
+ * (deleted > modified > migration > route > barrel > new_dir).
28
+ *
29
+ * Every path that leaves this module (`affectedPaths`, the `--paths` argument,
30
+ * message bullets) passes `sanitizePaths` (#4923); dropped paths are reported
31
+ * as data in `withheldPaths`, never interpolated into the message.
18
32
  *
19
33
  * Design decisions (see PR for full rubber-duck):
20
34
  * - The library is pure. It takes parsed git diff output and returns a
@@ -38,11 +52,14 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
38
52
  const node_fs_1 = __importDefault(require("node:fs"));
39
53
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
40
54
  const runtime_slash_cjs_1 = require("./runtime-slash.cjs");
55
+ const frontmatter_fence_cjs_1 = require("./frontmatter-fence.cjs");
41
56
  // ─── Constants ───────────────────────────────────────────────────────────────
42
- const DRIFT_CATEGORIES = Object.freeze(['new_dir', 'barrel', 'migration', 'route']);
57
+ const DRIFT_CATEGORIES = Object.freeze(['new_dir', 'barrel', 'migration', 'route', 'modified', 'deleted']);
43
58
  // Category priority when a single file matches multiple rules.
44
59
  // Higher index = more specific = wins.
45
- const CATEGORY_PRIORITY = { new_dir: 0, barrel: 1, route: 2, migration: 3 };
60
+ const CATEGORY_PRIORITY = {
61
+ new_dir: 0, barrel: 1, route: 2, migration: 3, modified: 4, deleted: 5,
62
+ };
46
63
  const BARREL_RE = /^(packages|apps)\/[^/]+\/src\/index\.(ts|tsx|js|mjs|cjs)$/;
47
64
  const MIGRATION_RES = [
48
65
  /^supabase\/migrations\/.+\.sql$/,
@@ -78,30 +95,99 @@ function classifyFile(file) {
78
95
  return 'barrel';
79
96
  return null;
80
97
  }
98
+ // Characters that continue a path component: a prefix occurrence directly
99
+ // preceded by one of `BEFORE_CONT` or followed by one of `AFTER_CONT` is a
100
+ // fragment of a longer word or name, not the prefix itself.
101
+ const BEFORE_CONT_RE = /[A-Za-z0-9_.-]/;
102
+ const AFTER_CONT_RE = /[A-Za-z0-9_-]/;
81
103
  /**
82
- * True iff any prefix of `file` (dir1, dir1/dir2, …) appears as a substring
83
- * of `structureMd`. Used to decide whether a file is in "mapped territory".
104
+ * True iff `needle` occurs in `corpus` at path-component boundaries: the
105
+ * character before it (or the start) is not a path-component character, and
106
+ * the character after it (or the end) is not one either. `.` may follow (a
107
+ * sentence end) but may not precede (`x.src`). Linear scan, no regex built
108
+ * from `needle`.
109
+ */
110
+ function occursAtBoundary(corpus, needle) {
111
+ if (!needle)
112
+ return false;
113
+ let from = 0;
114
+ for (;;) {
115
+ const at = corpus.indexOf(needle, from);
116
+ if (at === -1)
117
+ return false;
118
+ const end = at + needle.length;
119
+ const beforeOk = at === 0 || !BEFORE_CONT_RE.test(corpus[at - 1]);
120
+ const afterOk = end >= corpus.length || !AFTER_CONT_RE.test(corpus[end]);
121
+ if (beforeOk && afterOk)
122
+ return true;
123
+ from = at + 1;
124
+ }
125
+ }
126
+ /**
127
+ * True iff any prefix of `file` (dir1, dir1/dir2, …) appears in `corpus` (the
128
+ * provided generated documents joined) at path-component boundaries. Used to
129
+ * decide whether a file is in "mapped territory".
84
130
  *
85
- * Matching is deliberately substring-based — STRUCTURE.md is free-form
86
- * markdown, not a structured manifest. If the map mentions `src/lib/` the
87
- * check `structureMd.includes('src/lib')` holds.
131
+ * The documents are free-form markdown, not a structured manifest, so the
132
+ * match is textual — but it is component-aware: `src/lib/` and `` `src/lib` ``
133
+ * map `src/lib`; the word `library` does not map `lib` and `capital` does not
134
+ * map `api`. The `name/` and `` `name` `` forms of the top-level directory
135
+ * fall out of the same boundary rule.
88
136
  */
89
- function isPathMapped(file, structureMd) {
137
+ function isPathMapped(file, corpus) {
90
138
  const norm = (0, shell_command_projection_cjs_1.posixNormalize)(file);
91
139
  const parts = norm.split('/');
92
- // Check prefixes from longest to shortest; any hit means "mapped".
140
+ // A path with an empty component (`/x`, `a//b`) has no mappable prefix at
141
+ // that depth; never search for the empty string.
93
142
  for (let i = parts.length - 1; i >= 1; i--) {
94
- const prefix = parts.slice(0, i).join('/');
95
- if (structureMd.includes(prefix))
143
+ const prefixParts = parts.slice(0, i);
144
+ if (prefixParts.some((part) => part === ''))
145
+ continue;
146
+ if (occursAtBoundary(corpus, prefixParts.join('/')))
147
+ return true;
148
+ }
149
+ // A single-component path (a root-level file) has no directory prefix; the
150
+ // documented forms name the component itself as a directory.
151
+ if (parts.length === 1 && parts[0] !== '') {
152
+ if (occursAtBoundary(corpus, parts[0] + '/'))
153
+ return true;
154
+ if (corpus.includes('`' + parts[0] + '`'))
96
155
  return true;
97
156
  }
98
- // Finally, if even the top-level dir is mentioned, count as mapped.
99
- if (parts.length > 0 && structureMd.includes(parts[0] + '/'))
100
- return true;
101
- if (parts.length > 0 && structureMd.includes('`' + parts[0] + '`'))
102
- return true;
103
157
  return false;
104
158
  }
159
+ // Characters that would let a path rewrite a terminal or the reader's view of
160
+ // it: C0 controls, DEL, C1 controls, bidi controls, zero-width characters and
161
+ // the line/paragraph separators.
162
+ const DISPLAY_UNSAFE_RE = /[\u0000-\u001f\u007f-\u009f\u061c\u200b-\u200f\u2028\u2029\u202a-\u202e\u2060\u2066-\u2069\ufeff]/;
163
+ const DISPLAY_MAX_UNITS = 200;
164
+ /**
165
+ * Render an attacker-controlled path for a display surface. Display-unsafe
166
+ * characters become `\uXXXX` (lowercase hex); every other character —
167
+ * including ordinary non-ASCII — is left alone. A result longer than 200
168
+ * UTF-16 code units is cut on a code-point (and escape) boundary so that the
169
+ * result, with its trailing `…`, is exactly 200 code units or fewer.
170
+ */
171
+ function displaySafePath(p) {
172
+ const pieces = [];
173
+ let total = 0;
174
+ for (const ch of String(p)) {
175
+ const piece = DISPLAY_UNSAFE_RE.test(ch)
176
+ ? '\\u' + ch.codePointAt(0).toString(16).padStart(4, '0')
177
+ : ch;
178
+ pieces.push(piece);
179
+ total += piece.length;
180
+ }
181
+ if (total <= DISPLAY_MAX_UNITS)
182
+ return pieces.join('');
183
+ let out = '';
184
+ for (const piece of pieces) {
185
+ if (out.length + piece.length > DISPLAY_MAX_UNITS - 1)
186
+ break;
187
+ out += piece;
188
+ }
189
+ return out + '…';
190
+ }
105
191
  // ─── Main detection ──────────────────────────────────────────────────────────
106
192
  /**
107
193
  * Detect codebase drift.
@@ -112,40 +198,62 @@ function detectDrift(input) {
112
198
  return skipped('invalid-input');
113
199
  }
114
200
  const inp = input;
115
- const { addedFiles, modifiedFiles, deletedFiles, structureMd, } = inp;
201
+ const { addedFiles, modifiedFiles, deletedFiles, documents, } = inp;
116
202
  const threshold = Number.isInteger(inp.threshold) && inp.threshold >= 1
117
203
  ? inp.threshold
118
204
  : 3;
119
205
  const action = inp.action === 'auto-remap' ? 'auto-remap' : 'warn';
206
+ // STRUCTURE.md is the one required document; the other six only widen the
207
+ // mapped territory and are ignored when absent or not text.
208
+ const docs = documents !== null && typeof documents === 'object'
209
+ ? documents
210
+ : {};
211
+ const structureMd = docs['STRUCTURE.md'];
120
212
  if (structureMd === null || structureMd === undefined) {
121
213
  return skipped('missing-structure-md');
122
214
  }
123
215
  if (typeof structureMd !== 'string') {
124
216
  return skipped('invalid-structure-md');
125
217
  }
218
+ const corpus = Object.values(docs)
219
+ .filter((doc) => typeof doc === 'string')
220
+ .join('\n');
126
221
  const added = Array.isArray(addedFiles) ? addedFiles.filter((x) => typeof x === 'string') : [];
127
222
  const modified = Array.isArray(modifiedFiles) ? modifiedFiles : [];
128
223
  const deleted = Array.isArray(deletedFiles) ? deletedFiles : [];
129
224
  // Build elements. One element per file, highest-priority category wins.
130
225
  const elements = [];
131
226
  const seen = new Map();
227
+ const count = (file, category) => {
228
+ // Dedup: if we've already counted this path at higher-or-equal priority, skip
229
+ const prior = seen.get(file);
230
+ if (prior && CATEGORY_PRIORITY[prior] >= CATEGORY_PRIORITY[category])
231
+ return;
232
+ seen.set(file, category);
233
+ };
132
234
  for (const rawFile of added) {
133
235
  const file = (0, shell_command_projection_cjs_1.posixNormalize)(rawFile);
134
236
  const specific = classifyFile(file);
135
237
  let category = specific;
136
238
  if (!category) {
137
- if (!isPathMapped(file, structureMd)) {
239
+ if (!isPathMapped(file, corpus)) {
138
240
  category = 'new_dir';
139
241
  }
140
242
  else {
141
243
  continue; // mapped, known, ordinary file — not drift
142
244
  }
143
245
  }
144
- // Dedup: if we've already counted this path at higher-or-equal priority, skip
145
- const prior = seen.get(file);
146
- if (prior && CATEGORY_PRIORITY[prior] >= CATEGORY_PRIORITY[category])
147
- continue;
148
- seen.set(file, category);
246
+ count(file, category);
247
+ }
248
+ // Inverse territory: an edit is drift only where the map describes the file.
249
+ for (const [rawFiles, category] of [[modified, 'modified'], [deleted, 'deleted']]) {
250
+ for (const rawFile of rawFiles) {
251
+ if (typeof rawFile !== 'string')
252
+ continue;
253
+ const file = (0, shell_command_projection_cjs_1.posixNormalize)(rawFile);
254
+ if (file && isPathMapped(file, corpus))
255
+ count(file, category);
256
+ }
149
257
  }
150
258
  for (const [file, category] of seen.entries()) {
151
259
  elements.push({ category, path: file });
@@ -158,14 +266,20 @@ function detectDrift(input) {
158
266
  let directive = 'none';
159
267
  let spawnMapper = false;
160
268
  let affectedPaths = [];
269
+ let withheldPaths = [];
161
270
  let message = '';
162
271
  if (actionRequired) {
163
272
  directive = action;
164
- affectedPaths = chooseAffectedPaths(elements.map((e) => e.path));
165
- if (action === 'auto-remap') {
166
- spawnMapper = true;
167
- }
168
- message = buildMessage(elements, affectedPaths, action, inp.runtime);
273
+ // The one egress seam (#4923): `affectedPaths` is the only path list
274
+ // that reaches the result, the auto-remap line and `--paths`.
275
+ const elementPaths = elements.map((e) => e.path);
276
+ const chosen = chooseAffectedPaths(elementPaths);
277
+ affectedPaths = sanitizePaths(chosen);
278
+ const kept = new Set([...sanitizePaths(elementPaths), ...affectedPaths]);
279
+ withheldPaths = [...new Set([...elementPaths, ...chosen].filter((p) => !kept.has(p)))].sort();
280
+ // An empty `--paths` would remap the whole repo (#3418).
281
+ spawnMapper = action === 'auto-remap' && affectedPaths.length > 0;
282
+ message = buildMessage(elements, affectedPaths, withheldPaths.length, action, inp.runtime);
169
283
  }
170
284
  return {
171
285
  skipped: false,
@@ -174,6 +288,7 @@ function detectDrift(input) {
174
288
  directive,
175
289
  spawnMapper,
176
290
  affectedPaths,
291
+ withheldPaths,
177
292
  threshold,
178
293
  action,
179
294
  message,
@@ -199,12 +314,16 @@ function skipped(reason) {
199
314
  directive: 'none',
200
315
  spawnMapper: false,
201
316
  affectedPaths: [],
317
+ withheldPaths: [],
202
318
  message: '',
203
319
  };
204
320
  }
205
- function buildMessage(elements, affectedPaths, action, runtime) {
321
+ function buildMessage(elements, affectedPaths, withheldCount, action, runtime) {
206
322
  const byCat = {};
323
+ const listable = new Set(sanitizePaths(elements.map((e) => e.path)));
207
324
  for (const e of elements) {
325
+ if (!listable.has(e.path))
326
+ continue;
208
327
  if (!byCat[e.category])
209
328
  byCat[e.category] = [];
210
329
  byCat[e.category].push(e.path);
@@ -218,8 +337,10 @@ function buildMessage(elements, affectedPaths, action, runtime) {
218
337
  barrel: 'New barrel exports',
219
338
  migration: 'New migrations',
220
339
  route: 'New route modules',
340
+ modified: 'Modified files in mapped directories',
341
+ deleted: 'Deleted files in mapped directories',
221
342
  };
222
- for (const cat of ['new_dir', 'barrel', 'migration', 'route']) {
343
+ for (const cat of DRIFT_CATEGORIES) {
223
344
  if (byCat[cat]) {
224
345
  lines.push(`${labels[cat]}:`);
225
346
  for (const p of byCat[cat])
@@ -227,12 +348,18 @@ function buildMessage(elements, affectedPaths, action, runtime) {
227
348
  }
228
349
  }
229
350
  lines.push('');
230
- if (action === 'auto-remap') {
351
+ // Dropped paths are counted, never named: the names are data in `withheldPaths`.
352
+ if (withheldCount > 0) {
353
+ lines.push(`${withheldCount} path(s) withheld: not passed to the mapper or listed (absolute, traversal, whitespace, non-ASCII or shell-metacharacter characters)`);
354
+ }
355
+ // Nothing safe to scope a refresh to (empty `affectedPaths`): an empty
356
+ // `--paths` would remap the whole repo (#3418), so no command line is emitted.
357
+ if (affectedPaths.length > 0 && action === 'auto-remap') {
231
358
  lines.push(`Auto-remap scheduled for paths: ${affectedPaths.join(', ')}`);
232
359
  }
233
- else {
360
+ else if (affectedPaths.length > 0) {
234
361
  // drift.cts is a pure library — it must never read env/config. The
235
- // caller (verify.cmdVerifyCodebaseDrift) resolves the runtime once and
362
+ // caller (gate-codebase-drift.cts) resolves the runtime once and
236
363
  // passes it in via input.runtime so emitted commands match the project
237
364
  // the caller is targeting, not the current process directory.
238
365
  const mapCmd = (0, runtime_slash_cjs_1.formatGsdSlash)('map-codebase', runtime || 'claude');
@@ -256,6 +383,8 @@ function chooseAffectedPaths(paths) {
256
383
  if (parts.length === 0)
257
384
  continue;
258
385
  const top = parts[0];
386
+ if (!top)
387
+ continue; // an empty first component (`/x`) names no directory
259
388
  if ((top === 'apps' || top === 'packages') && parts.length >= 2) {
260
389
  out.add(`${top}/${parts[1]}`);
261
390
  }
@@ -281,26 +410,31 @@ function sanitizePaths(paths) {
281
410
  continue;
282
411
  if (!SAFE_PATH_RE.test(p))
283
412
  continue;
413
+ // A `.` component would make `--paths .` (the whole repo) producible (#3418).
414
+ if (p.split('/').some((component) => component === '.'))
415
+ continue;
284
416
  out.push(p);
285
417
  }
286
418
  return out;
287
419
  }
288
- // ─── Frontmatter helpers ─────────────────────────────────────────────────────
289
- const FRONTMATTER_RE = /^---\r?\n([\s\S]*?)\r?\n---\r?\n?/;
420
+ /**
421
+ * The block is the one the one fence owner finds (`locateFrontmatterFence`); `body` is
422
+ * everything after the closing fence line and its line ending.
423
+ */
290
424
  function parseFrontmatter(content) {
291
425
  if (typeof content !== 'string')
292
426
  return { data: {}, body: '' };
293
- const m = content.match(FRONTMATTER_RE);
294
- if (!m)
427
+ const fence = (0, frontmatter_fence_cjs_1.locateFrontmatterFence)(content);
428
+ if (!fence?.closed)
295
429
  return { data: {}, body: content };
296
430
  const data = {};
297
- for (const line of m[1].split(/\r?\n/)) {
431
+ for (const line of content.slice(fence.openEnd, fence.bodyEnd).split(/\r?\n/)) {
298
432
  const kv = line.match(/^([A-Za-z0-9_][A-Za-z0-9_-]*):\s*(.*)$/);
299
433
  if (!kv)
300
434
  continue;
301
435
  data[kv[1]] = kv[2];
302
436
  }
303
- return { data, body: content.slice(m[0].length) };
437
+ return { data, body: content.slice(fence.closingFenceEnd).replace(/^\r?\n/, '') };
304
438
  }
305
439
  function serializeFrontmatter(data, body) {
306
440
  const keys = Object.keys(data);
@@ -357,6 +491,7 @@ module.exports = {
357
491
  detectDrift,
358
492
  chooseAffectedPaths,
359
493
  sanitizePaths,
494
+ displaySafePath,
360
495
  readMappedCommit,
361
496
  writeMappedCommit,
362
497
  // Exposed for the CLI layer to reuse the same parser.
@@ -0,0 +1,90 @@
1
+ "use strict";
2
+ /**
3
+ * frontmatter-fence.cts — the one owner of "where does a document's frontmatter block start
4
+ * and stop" (ADR-5057 one-owner rule; found while implementing #5105).
5
+ *
6
+ * Source in src/frontmatter-fence.cts, compiled to gsd-core/bin/lib/frontmatter-fence.cjs
7
+ * (gitignored), per the repo's ADR-457 build-at-publish convention.
8
+ *
9
+ * Before this module the answer was re-derived some thirty times across `src/` — regexes such
10
+ * as `/^---\r?\n([\s\S]*?)\r?\n---/`, `indexOf('\n---', 4)` scans, `lines[0].trim() === '---'`
11
+ * checks — and the copies disagreed on a BOM, CRLF, an adjacent empty block, `----`, `--- x`,
12
+ * a `---` inside a value and leading whitespace. Every frontmatter reader and writer in `src/`
13
+ * (planning documents, agent/skill/command files, the installer's converters and injectors),
14
+ * in the runtime hooks, and in the two hand-written entry points that load `bin/lib`
15
+ * (`bin/install.js`, `gsd-core/bin/gsd-tools.cjs`) now reads the fence from
16
+ * `locateFrontmatterFence` — directly for offsets, or through `frontmatterRegion`/
17
+ * `frontmatterBlock`/`extractFrontmatter`/`stripFrontmatter` (`frontmatter.cts`) for content.
18
+ * `scripts/lint-frontmatter-fence-drift.cjs` (run by `lint:ci`) fails on a new hand-rolled
19
+ * fence in any of them.
20
+ *
21
+ * A "genuine leaf" module (CONTEXT.md's term): zero I/O, zero imports, so both
22
+ * `frontmatter.cts` and `shell-command-projection.cts` — which import each other's side of a
23
+ * cycle — can depend on it.
24
+ */
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.locateFrontmatterFence = locateFrontmatterFence;
27
+ /** A closing fence is a WHOLE line: three dashes, then only spaces or tabs. */
28
+ const CLOSING_FENCE_LINE = /^---[ \t]*$/;
29
+ /**
30
+ * The lenient closer: a WHOLE line of four or more dashes, then only spaces or tabs. It closes
31
+ * a block only when no exact closer follows the opening fence — the pre-existing lenient parse
32
+ * of a `----`-closed block (#1882, `tests/unusable-input.test.cjs`), kept so such a document
33
+ * still reads its keys instead of reading as unterminated.
34
+ */
35
+ const LENIENT_CLOSING_FENCE_LINE = /^-{4,}[ \t]*$/;
36
+ function locateFrontmatterFence(text, options = {}) {
37
+ if (typeof text !== 'string') {
38
+ throw new TypeError(`locateFrontmatterFence: expected a string, got ${typeof text}`);
39
+ }
40
+ const bom = text.charCodeAt(0) === 0xfeff ? String.fromCharCode(0xfeff) : '';
41
+ const openingEolAt = (at) => {
42
+ if (text.startsWith('---\r\n', at))
43
+ return '\r\n';
44
+ if (text.startsWith('---\n', at))
45
+ return '\n';
46
+ return null;
47
+ };
48
+ let start = bom.length;
49
+ let found = openingEolAt(start);
50
+ if (found === null && options.allowPreamble === true) {
51
+ for (let newline = text.indexOf('\n', start); newline !== -1; newline = text.indexOf('\n', newline + 1)) {
52
+ found = openingEolAt(newline + 1);
53
+ if (found !== null) {
54
+ start = newline + 1;
55
+ break;
56
+ }
57
+ }
58
+ }
59
+ if (found === null)
60
+ return null;
61
+ const eol = found;
62
+ const openEnd = start + 3 + eol.length;
63
+ const closedAt = (lineStart, lineEnd) => {
64
+ let bodyEnd = openEnd;
65
+ if (lineStart > openEnd) {
66
+ // Back over the line ending that ends the last content line.
67
+ bodyEnd = lineStart - 1;
68
+ if (bodyEnd > openEnd && text[bodyEnd - 1] === '\r')
69
+ bodyEnd -= 1;
70
+ }
71
+ return { bom, eol, openEnd, closed: true, closingStart: lineStart, closingFenceEnd: lineEnd, bodyEnd };
72
+ };
73
+ let lenient = null;
74
+ let lineStart = openEnd;
75
+ while (lineStart <= text.length) {
76
+ const newline = text.indexOf('\n', lineStart);
77
+ const lineEnd = newline === -1 ? text.length : newline > lineStart && text[newline - 1] === '\r' ? newline - 1 : newline;
78
+ const line = text.slice(lineStart, lineEnd);
79
+ if (CLOSING_FENCE_LINE.test(line))
80
+ return closedAt(lineStart, lineEnd);
81
+ if (lenient === null && LENIENT_CLOSING_FENCE_LINE.test(line))
82
+ lenient = [lineStart, lineEnd];
83
+ if (newline === -1)
84
+ break;
85
+ lineStart = newline + 1;
86
+ }
87
+ if (lenient !== null)
88
+ return closedAt(lenient[0], lenient[1]);
89
+ return { bom, eol, openEnd, closed: false, closingStart: -1, closingFenceEnd: -1, bodyEnd: text.length };
90
+ }