@opengsd/gsd-core 1.11.0 → 1.13.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 (498) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -21,8 +21,9 @@
21
21
  * For every file deleted (`git diff --name-status <base>...HEAD`, status
22
22
  * `D`), grep the post-diff tree for the deleted file's basename:
23
23
  *
24
- * - `.github/workflows/`, `gsd-core/`, `docs/`, `package.json` — ANY
25
- * surviving reference fails (the original rule).
24
+ * - `.github/workflows/`, `gsd-core/`, `docs/` (excluding `docs/adr/**` and
25
+ * `docs/research/**` — see "Historical-record exemption" below),
26
+ * `package.json` — ANY surviving reference fails (the original rule).
26
27
  * - `tests/` — scanned with a discriminator (#3565): a reference that PINS
27
28
  * existence (`fs.existsSync(path)`, `readFileSync`, `require`, or the
28
29
  * basename as a quoted object key) fails; a reference that ASSERTS
@@ -46,6 +47,52 @@
46
47
  * A common basename (`index.js`, `config.json`) can coincidentally match an
47
48
  * unrelated file, and this only catches LITERAL string references — not a
48
49
  * variable holding the filename or a glob that happened to match it.
50
+ *
51
+ * ## Basename-collision refinement (#3907)
52
+ *
53
+ * #3907 deleted `bin/lib/ui-safety-gate.cjs` while the SEPARATE, still-live
54
+ * `gsd-core/bin/lib/ui-safety-gate.cjs` survives — every reference to the
55
+ * survivor (including it referencing itself) was misattributed to the
56
+ * deletion, 14 false violations on a correct tree. This epic is about
57
+ * de-duplicating modules, so "delete one of two files sharing a basename"
58
+ * recurs by construction.
59
+ *
60
+ * The fix is precision, not suppression: for each deleted file, if its
61
+ * basename is ALSO the basename of a file that still exists in the
62
+ * post-diff tree — `git ls-files` (tracked files) UNIONED with the
63
+ * already filesystem-walked corpus/testsCorpus file lists, because
64
+ * `git ls-files` alone misses gitignored BUILD ARTIFACTS such as
65
+ * `gsd-core/bin/lib/*.cjs` (compiled from `.cts`, never committed) —
66
+ * matching switches from the bare basename to the deleted file's full
67
+ * repo-relative path. Because the deletion is already committed on this
68
+ * branch, none of these sources can list the deleted file itself, so any
69
+ * hit is necessarily a DIFFERENT file. A genuine
70
+ * full-path reference to the deleted file is still caught (strictly more
71
+ * precise, not weaker); a bare-basename reference becomes correctly
72
+ * unattributable to the deleted file specifically and is no longer
73
+ * blamed on it. When no surviving file shares the basename, nothing
74
+ * changes — the original basename rule still applies exactly as before.
75
+ *
76
+ * Residual known limit: a bare-basename reference to a deleted file whose
77
+ * basename survives elsewhere is now invisible to this guard (it cannot
78
+ * tell whether the bare mention meant the deleted file or its
79
+ * basename-twin). Same trade the docstring already makes for prose
80
+ * mentions above — a false violation reds a correct tree; a missed
81
+ * ambiguous mention only loses one detection channel.
82
+ *
83
+ * ## Historical-record exemption (#3942)
84
+ *
85
+ * `docs/adr/**` and `docs/research/**` are excluded from the `docs/` scan.
86
+ * An ADR's or a post-mortem's entire job is to record what was retired —
87
+ * naming the deleted file IS the point of the document, not a defect — so
88
+ * without this exemption a PR could never write the ADR that explains its
89
+ * own deletion in the same PR that performs it (it would have to land in a
90
+ * follow-up, after the fact, which is backwards for a decision record).
91
+ * The exemption is narrow and applies only to these two directories: every
92
+ * other document under `docs/` (guides, `TESTING-SUITES.md`, generated
93
+ * indexes, etc.) still enforces "ANY surviving reference fails" exactly as
94
+ * before. `.github/workflows/`, `gsd-core/`, and `package.json` are
95
+ * likewise unaffected — none of those are historical-record surfaces.
49
96
  */
50
97
 
51
98
  const fs = require('node:fs');
@@ -78,6 +125,40 @@ function referencesBasename(content, basename) {
78
125
  return re.test(content);
79
126
  }
80
127
 
128
+ /**
129
+ * Pure: does `content` contain a literal reference to the FULL
130
+ * repo-relative path `relPath` (used instead of {@link referencesBasename}
131
+ * when the deleted file's basename collides with a surviving file — #3907)?
132
+ *
133
+ * Same delimited-match idea as `referencesBasename`, but the left boundary
134
+ * additionally excludes `/` — a `/` immediately to the left would mean the
135
+ * matched text is really a SUFFIX of a longer path (e.g. content contains
136
+ * `gsd-core/bin/lib/x.cjs` and `relPath` is `bin/lib/x.cjs`: without this,
137
+ * the survivor's own path would be mistaken for a reference to the
138
+ * deleted file it merely shares a basename with).
139
+ * @param {string} content
140
+ * @param {string} relPath - repo-relative path, forward-slash separated
141
+ * @returns {boolean}
142
+ */
143
+ function referencesPath(content, relPath) {
144
+ const re = new RegExp(`(^|[^\\w./-])${escapeRegex(relPath)}($|[^\\w.-])`);
145
+ return re.test(content);
146
+ }
147
+
148
+ /**
149
+ * Pure: given the post-diff tree's file list (repo-relative paths), build
150
+ * the set of basenames that still exist. Used to decide, per deleted file,
151
+ * whether basename matching would be ambiguous (#3907).
152
+ * @param {string[]} survivingFiles - repo-relative paths of files present
153
+ * in the post-diff tree (must NOT include the deleted files themselves)
154
+ * @returns {Set<string>}
155
+ */
156
+ function buildSurvivingBasenames(survivingFiles) {
157
+ const set = new Set();
158
+ for (const f of survivingFiles) set.add(path.basename(f));
159
+ return set;
160
+ }
161
+
81
162
  /**
82
163
  * Pure: given the deleted file's basename, does content contain a
83
164
  * lockfile-dependent idiom (`npm ci`, `cache: 'npm'` / `cache: "npm"`)?
@@ -110,15 +191,26 @@ function walk(dir) {
110
191
  * of the post-diff tree, find every surviving reference.
111
192
  * @param {string[]} deletedFiles - repo-relative deleted paths
112
193
  * @param {{ file: string, content: string }[]} corpus
194
+ * @param {Set<string>} [survivingBasenames] - basenames still present in the
195
+ * post-diff tree (#3907); when a deleted file's basename is in this set,
196
+ * matching switches from basename to the deleted file's full path
113
197
  * @returns {{ deletedFile: string, referencedIn: string, reason: string }[]}
114
198
  */
115
- function findSurvivingReferences(deletedFiles, corpus) {
199
+ function findSurvivingReferences(deletedFiles, corpus, survivingBasenames = new Set()) {
116
200
  const violations = [];
117
201
  for (const deletedFile of deletedFiles) {
118
202
  const basename = path.basename(deletedFile);
203
+ const collides = survivingBasenames.has(basename);
119
204
  for (const { file, content } of corpus) {
120
- if (referencesBasename(content, basename)) {
121
- violations.push({ deletedFile, referencedIn: file, reason: `basename '${basename}' still referenced` });
205
+ const matched = collides ? referencesPath(content, deletedFile) : referencesBasename(content, basename);
206
+ if (matched) {
207
+ violations.push({
208
+ deletedFile,
209
+ referencedIn: file,
210
+ reason: collides
211
+ ? `full path '${deletedFile}' still referenced (basename '${basename}' also belongs to a surviving file, so matched by path — #3907)`
212
+ : `basename '${basename}' still referenced`,
213
+ });
122
214
  }
123
215
  }
124
216
  if (basename === 'package-lock.json') {
@@ -154,9 +246,12 @@ function findSurvivingReferences(deletedFiles, corpus) {
154
246
  *
155
247
  * @param {string} line
156
248
  * @param {string} basename
249
+ * @param {string} [matchTarget] - text to look for as a quoted object key
250
+ * (#3907: the deleted file's full path when its basename collides with a
251
+ * surviving file; defaults to `basename`, the original behaviour)
157
252
  * @returns {'asserts-absence'|'pins-existence'|null}
158
253
  */
159
- function classifyTestReference(line, basename) {
254
+ function classifyTestReference(line, basename, matchTarget = basename) {
160
255
  const negatedCheck =
161
256
  /!\s*[A-Za-z_$][\w.$]*\.(includes|indexOf|search|startsWith|endsWith|match)\s*\(/.test(line) ||
162
257
  /!\s*(fs\.)?(existsSync|statSync)\s*\(/.test(line) ||
@@ -170,7 +265,7 @@ function classifyTestReference(line, basename) {
170
265
  // Template literal needs \\s so the RegExp constructor receives \s — a
171
266
  // bare \s in a template literal collapses to 's' and silently matches a
172
267
  // literal 's' instead of whitespace.
173
- if (new RegExp(`['"\`]${escapeRegex(basename)}['"\`]\\s*:`).test(line)) return 'pins-existence';
268
+ if (new RegExp(`['"\`]${escapeRegex(matchTarget)}['"\`]\\s*:`).test(line)) return 'pins-existence';
174
269
  return null;
175
270
  }
176
271
 
@@ -183,24 +278,38 @@ function classifyTestReference(line, basename) {
183
278
  *
184
279
  * @param {string[]} deletedFiles - repo-relative deleted paths
185
280
  * @param {{ file: string, content: string }[]} testsCorpus
281
+ * @param {Set<string>} [survivingBasenames] - basenames still present in the
282
+ * post-diff tree (#3907); when a deleted file's basename is in this set,
283
+ * matching switches from basename to the deleted file's full path, same
284
+ * refinement as {@link findSurvivingReferences} so the two scanners
285
+ * cannot disagree about what "a reference" means
186
286
  * @returns {{ deletedFile: string, referencedIn: string, reason: string }[]}
187
287
  */
188
- function findSurvivingTestReferences(deletedFiles, testsCorpus) {
288
+ function findSurvivingTestReferences(deletedFiles, testsCorpus, survivingBasenames = new Set()) {
189
289
  const violations = [];
190
290
  for (const deletedFile of deletedFiles) {
191
291
  const basename = path.basename(deletedFile);
192
- const refRe = new RegExp(`(^|[^\\w.-])${escapeRegex(basename)}($|[^\\w.-])`);
292
+ const collides = survivingBasenames.has(basename);
293
+ const matchTarget = collides ? deletedFile : basename;
294
+ // The left-boundary class matches referencesPath's when collides
295
+ // (excludes '/' so a survivor's own longer path isn't mistaken for a
296
+ // reference to the deleted file it merely shares a basename with).
297
+ const refRe = collides
298
+ ? new RegExp(`(^|[^\\w./-])${escapeRegex(matchTarget)}($|[^\\w.-])`)
299
+ : new RegExp(`(^|[^\\w.-])${escapeRegex(matchTarget)}($|[^\\w.-])`);
193
300
  for (const { file, content } of testsCorpus) {
194
301
  for (const line of content.split(/\r?\n/)) {
195
302
  if (!refRe.test(line)) continue;
196
- const kind = classifyTestReference(line, basename);
303
+ const kind = classifyTestReference(line, basename, matchTarget);
197
304
  if (kind === 'pins-existence') {
198
305
  violations.push({
199
306
  deletedFile,
200
307
  referencedIn: file,
201
308
  // control-char-stripped + capped: this text is echoed into lint
202
309
  // output that AI agents read as trusted instructions
203
- reason: `pins-existence: test depends on deleted basename '${basename}' — ${sanitizeEcho(line.trim())}`,
310
+ reason: collides
311
+ ? `pins-existence: test depends on deleted path '${matchTarget}' — ${sanitizeEcho(line.trim())}`
312
+ : `pins-existence: test depends on deleted basename '${basename}' — ${sanitizeEcho(line.trim())}`,
204
313
  });
205
314
  }
206
315
  // 'asserts-absence' is correct post-deletion state; null is a
@@ -215,7 +324,7 @@ function getDeletedFiles(root, baseRef) {
215
324
  // Deliberately let a git failure (unresolvable ref, no merge base, etc.)
216
325
  // propagate as a plain Error — main() treats ANY scan() failure as "cannot
217
326
  // resolve this base ref in this environment" and degrades to a skip,
218
- // matching lint-fix-has-regression-test.cjs. There is no failure mode here
327
+ // matching lint-fix-has-regression-tests.cjs. There is no failure mode here
219
328
  // that should hard-exit non-zero; a real drift is only ever reported once
220
329
  // the diff succeeds and findSurvivingReferences finds a violation.
221
330
  const out = cp.execFileSync('git', ['diff', '--name-status', `${baseRef}...HEAD`], {
@@ -231,12 +340,53 @@ function getDeletedFiles(root, baseRef) {
231
340
  .map((line) => line.slice(2));
232
341
  }
233
342
 
343
+ /**
344
+ * All tracked files at the current tree state (the post-diff tree, since
345
+ * this scan runs against a branch where the deletion is already committed —
346
+ * `git ls-files` therefore CANNOT list a deleted file, so any hit for a
347
+ * deleted file's basename is necessarily a different, surviving file).
348
+ * Used to build `survivingBasenames` for the #3907 collision refinement.
349
+ * @param {string} root
350
+ * @returns {string[]} repo-relative paths, forward-slash separated
351
+ */
352
+ function getSurvivingFiles(root) {
353
+ const out = cp.execFileSync('git', ['ls-files'], {
354
+ cwd: root,
355
+ encoding: 'utf8',
356
+ timeout: 15000,
357
+ });
358
+ return out
359
+ .trim()
360
+ .split('\n')
361
+ .filter(Boolean)
362
+ .map((f) => f.replace(/\\/g, '/'));
363
+ }
364
+
365
+ // #3942: docs/adr/** and docs/research/** are historical records — an ADR's
366
+ // or a post-mortem's whole job is to narrate what was retired, so naming a
367
+ // file this same PR deletes is the point, not DEFECT.REMOVED-BUT-NEEDED.
368
+ // Narrow and explicit: only these two `docs/` subtrees are exempt; every
369
+ // other document under `docs/` still enforces the original rule unchanged.
370
+ const DOCS_HISTORICAL_RECORD_PREFIXES = ['docs/adr/', 'docs/research/'];
371
+
372
+ /**
373
+ * Pure: is `relFile` (repo-relative, forward-slash separated) inside one of
374
+ * the exempt historical-record directories (#3942)?
375
+ * @param {string} relFile
376
+ * @returns {boolean}
377
+ */
378
+ function isDocsHistoricalRecord(relFile) {
379
+ return DOCS_HISTORICAL_RECORD_PREFIXES.some((prefix) => relFile.startsWith(prefix));
380
+ }
381
+
234
382
  function buildCorpus(root) {
235
383
  const corpus = [];
236
384
  for (const rel of SCAN_ROOTS) {
237
385
  for (const abs of walk(path.join(root, rel))) {
386
+ const relFile = path.relative(root, abs).replace(/\\/g, '/');
387
+ if (rel === 'docs' && isDocsHistoricalRecord(relFile)) continue;
238
388
  try {
239
- corpus.push({ file: path.relative(root, abs).replace(/\\/g, '/'), content: fs.readFileSync(abs, 'utf8') });
389
+ corpus.push({ file: relFile, content: fs.readFileSync(abs, 'utf8') });
240
390
  } catch {
241
391
  // unreadable (broken symlink, binary that slipped past SKIP_EXT) — skip
242
392
  }
@@ -257,7 +407,6 @@ function scan(root, baseRef) {
257
407
  const deletedFiles = getDeletedFiles(root, baseRef);
258
408
  if (deletedFiles.length === 0) return [];
259
409
  const corpus = buildCorpus(root);
260
- const violations = findSurvivingReferences(deletedFiles, corpus);
261
410
  // tests/ arm (#3565): same deletions, discriminated references. A bare
262
411
  // mention that pins nothing is skipped; an absence assertion is the
263
412
  // correct post-deletion state; only a pin on a deleted file fails.
@@ -272,7 +421,21 @@ function scan(root, baseRef) {
272
421
  // unreadable (broken symlink, binary that slipped past SKIP_EXT) — skip
273
422
  }
274
423
  }
275
- violations.push(...findSurvivingTestReferences(deletedFiles, testsCorpus));
424
+ // #3907: per-deletion basename-collision detection — if a surviving file
425
+ // shares the basename, matching switches from basename to full path (see
426
+ // the header's "Basename-collision refinement" section) for BOTH arms.
427
+ // `git ls-files` alone misses gitignored BUILD ARTIFACTS (e.g.
428
+ // gsd-core/bin/lib/*.cjs, compiled from .cts and never committed) —
429
+ // exactly the #3907 collision partner — so it is unioned with the
430
+ // already filesystem-walked corpus/testsCorpus file lists, which do see
431
+ // them.
432
+ const survivingBasenames = buildSurvivingBasenames([
433
+ ...getSurvivingFiles(root),
434
+ ...corpus.map((c) => c.file),
435
+ ...testsCorpus.map((t) => t.file),
436
+ ]);
437
+ const violations = findSurvivingReferences(deletedFiles, corpus, survivingBasenames);
438
+ violations.push(...findSurvivingTestReferences(deletedFiles, testsCorpus, survivingBasenames));
276
439
  return violations;
277
440
  }
278
441
 
@@ -284,7 +447,7 @@ function main() {
284
447
  } catch (e) {
285
448
  // origin/<base> unreachable in this environment (e.g. a shallow local
286
449
  // clone with no matching remote-tracking ref) — degrade to a skip rather
287
- // than a false failure, matching lint-fix-has-regression-test.cjs.
450
+ // than a false failure, matching lint-fix-has-regression-tests.cjs.
288
451
  console.log(`lint-removed-but-needed: could not resolve ${baseRef}, skipping (${e.message})`);
289
452
  return;
290
453
  }
@@ -305,16 +468,21 @@ function main() {
305
468
 
306
469
  module.exports = {
307
470
  referencesBasename,
471
+ referencesPath,
472
+ buildSurvivingBasenames,
308
473
  referencesNpmLockfileDependency,
309
474
  findSurvivingReferences,
310
475
  classifyTestReference,
311
476
  findSurvivingTestReferences,
312
477
  getDeletedFiles,
478
+ getSurvivingFiles,
313
479
  buildCorpus,
480
+ isDocsHistoricalRecord,
314
481
  scan,
315
482
  SCAN_ROOTS,
316
483
  EXTRA_FILES,
317
484
  TESTS_ROOT,
485
+ DOCS_HISTORICAL_RECORD_PREFIXES,
318
486
  };
319
487
 
320
488
  if (require.main === module) runMain(main);