@opengsd/gsd-core 1.12.0 → 1.14.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (455) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-advisor-researcher.compact.md +85 -0
  5. package/agents/gsd-ai-researcher.compact.md +96 -0
  6. package/agents/gsd-assumptions-analyzer.compact.md +81 -0
  7. package/agents/gsd-code-fixer.compact.md +458 -0
  8. package/agents/gsd-code-fixer.md +5 -5
  9. package/agents/gsd-code-reviewer.compact.md +269 -0
  10. package/agents/gsd-code-reviewer.md +15 -3
  11. package/agents/gsd-codebase-mapper.compact.md +760 -0
  12. package/agents/gsd-debug-session-manager.compact.md +345 -0
  13. package/agents/gsd-doc-classifier.compact.md +192 -0
  14. package/agents/gsd-doc-synthesizer.compact.md +200 -0
  15. package/agents/gsd-doc-verifier.compact.md +143 -0
  16. package/agents/gsd-doc-writer.compact.md +440 -0
  17. package/agents/gsd-dom-verifier.compact.md +138 -0
  18. package/agents/gsd-domain-researcher.compact.md +141 -0
  19. package/agents/gsd-eval-auditor.compact.md +160 -0
  20. package/agents/gsd-eval-planner.compact.md +137 -0
  21. package/agents/gsd-executor.md +63 -35
  22. package/agents/gsd-framework-selector.compact.md +82 -0
  23. package/agents/gsd-integration-checker.compact.md +245 -0
  24. package/agents/gsd-intel-updater.compact.md +226 -0
  25. package/agents/gsd-mempalace-curator.compact.md +45 -0
  26. package/agents/gsd-nyquist-auditor.compact.md +179 -0
  27. package/agents/gsd-pattern-mapper.compact.md +275 -0
  28. package/agents/gsd-plan-checker.md +76 -57
  29. package/agents/gsd-planner.md +14 -0
  30. package/agents/gsd-project-researcher.compact.md +587 -0
  31. package/agents/gsd-research-synthesizer.compact.md +212 -0
  32. package/agents/gsd-roadmapper.compact.md +454 -0
  33. package/agents/gsd-roadmapper.md +13 -0
  34. package/agents/gsd-security-auditor.compact.md +162 -0
  35. package/agents/gsd-ui-auditor.compact.md +404 -0
  36. package/agents/gsd-ui-checker.compact.md +277 -0
  37. package/agents/gsd-ui-checker.md +19 -3
  38. package/agents/gsd-ui-researcher.compact.md +282 -0
  39. package/agents/gsd-ui-researcher.md +29 -0
  40. package/agents/gsd-user-profiler.compact.md +108 -0
  41. package/agents/gsd-verifier.md +23 -1
  42. package/bin/install.js +444 -134
  43. package/commands/gsd/cleanup.md +1 -0
  44. package/commands/gsd/code-review.md +2 -1
  45. package/commands/gsd/complete-milestone.md +1 -0
  46. package/commands/gsd/config.md +1 -0
  47. package/commands/gsd/debug.md +1 -0
  48. package/commands/gsd/execute-phase.md +1 -1
  49. package/commands/gsd/graphify.md +1 -0
  50. package/commands/gsd/health.md +1 -0
  51. package/commands/gsd/mempalace-capture.md +1 -0
  52. package/commands/gsd/mempalace-recall.md +1 -0
  53. package/commands/gsd/new-milestone.md +1 -0
  54. package/commands/gsd/new-project.md +1 -0
  55. package/commands/gsd/next.md +1 -0
  56. package/commands/gsd/ns-workflow.md +2 -1
  57. package/commands/gsd/pause-work.md +1 -0
  58. package/commands/gsd/phase.md +2 -1
  59. package/commands/gsd/pr-branch.md +1 -0
  60. package/commands/gsd/quick-batch.md +105 -0
  61. package/commands/gsd/resume-work.md +1 -0
  62. package/commands/gsd/review-backlog.md +1 -0
  63. package/commands/gsd/settings.md +2 -1
  64. package/commands/gsd/stats.md +1 -0
  65. package/commands/gsd/surface.md +18 -8
  66. package/commands/gsd/thread.md +1 -0
  67. package/commands/gsd/workspace.md +1 -0
  68. package/commands/gsd/workstreams.md +1 -0
  69. package/gsd-core/bin/check-latest-version.cjs +8 -3
  70. package/gsd-core/bin/gsd-tools.cjs +532 -174
  71. package/gsd-core/bin/lib/adr-parser.cjs +1 -1
  72. package/gsd-core/bin/lib/artifacts.cjs +2 -1
  73. package/gsd-core/bin/lib/audit.cjs +39 -22
  74. package/gsd-core/bin/lib/broken-windows.cjs +168 -49
  75. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  76. package/gsd-core/bin/lib/capability-lifecycle.cjs +10 -6
  77. package/gsd-core/bin/lib/capability-loader.cjs +135 -1
  78. package/gsd-core/bin/lib/capability-registry.cjs +528 -116
  79. package/gsd-core/bin/lib/capability-source.cjs +19 -2
  80. package/gsd-core/bin/lib/capability-state.cjs +7 -1
  81. package/gsd-core/bin/lib/capability-validator.cjs +134 -5
  82. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  83. package/gsd-core/bin/lib/check-command-router.cjs +198 -38
  84. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  85. package/gsd-core/bin/lib/clusters.cjs +1 -0
  86. package/gsd-core/bin/lib/code-review-depth.cjs +2 -2
  87. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  88. package/gsd-core/bin/lib/commands.cjs +981 -79
  89. package/gsd-core/bin/lib/config-loader.cjs +4 -0
  90. package/gsd-core/bin/lib/config.cjs +153 -38
  91. package/gsd-core/bin/lib/core-utils.cjs +34 -7
  92. package/gsd-core/bin/lib/coverage.cjs +1 -1
  93. package/gsd-core/bin/lib/decisions.cjs +343 -28
  94. package/gsd-core/bin/lib/edge-probe.cjs +14 -1
  95. package/gsd-core/bin/lib/external-descriptor-trust.cjs +29 -14
  96. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  97. package/gsd-core/bin/lib/frontmatter.cjs +137 -23
  98. package/gsd-core/bin/lib/gap-checker.cjs +22 -13
  99. package/gsd-core/bin/lib/git-base-branch.cjs +10 -2
  100. package/gsd-core/bin/lib/gsd2-import.cjs +1 -2
  101. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  102. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +54 -11
  103. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +87 -23
  104. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +1 -1
  105. package/gsd-core/bin/lib/host-integration.cjs +57 -5
  106. package/gsd-core/bin/lib/init-command-router.cjs +14 -0
  107. package/gsd-core/bin/lib/init.cjs +539 -60
  108. package/gsd-core/bin/lib/install-engine.cjs +199 -14
  109. package/gsd-core/bin/lib/install-model-override-resolver.cjs +45 -0
  110. package/gsd-core/bin/lib/install-profiles.cjs +36 -14
  111. package/gsd-core/bin/lib/installer-migration-report.cjs +1 -0
  112. package/gsd-core/bin/lib/installer-migrations.cjs +33 -4
  113. package/gsd-core/bin/lib/io.cjs +35 -0
  114. package/gsd-core/bin/lib/loop-resolver.cjs +64 -39
  115. package/gsd-core/bin/lib/markdown-table.cjs +123 -0
  116. package/gsd-core/bin/lib/mcp-catalog.cjs +2 -2
  117. package/gsd-core/bin/lib/milestone.cjs +41 -10
  118. package/gsd-core/bin/lib/model-resolver.cjs +101 -10
  119. package/gsd-core/bin/lib/phase-command-router.cjs +20 -7
  120. package/gsd-core/bin/lib/phase-id.cjs +412 -31
  121. package/gsd-core/bin/lib/phase-lifecycle.cjs +61 -0
  122. package/gsd-core/bin/lib/phase.cjs +941 -98
  123. package/gsd-core/bin/lib/plan-document.cjs +10 -0
  124. package/gsd-core/bin/lib/planning-inspect.cjs +34 -18
  125. package/gsd-core/bin/lib/planning-snapshot.cjs +206 -30
  126. package/gsd-core/bin/lib/planning-workspace.cjs +153 -29
  127. package/gsd-core/bin/lib/pristine-baseline.cjs +182 -0
  128. package/gsd-core/bin/lib/prohibition-enforcement.cjs +91 -4
  129. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  130. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  131. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  132. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +61 -2
  133. package/gsd-core/bin/lib/research-store.cjs +11 -12
  134. package/gsd-core/bin/lib/review-lane-descriptor.cjs +53 -5
  135. package/gsd-core/bin/lib/review-lane-invocation.cjs +96 -1
  136. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  137. package/gsd-core/bin/lib/reviewer-step-dispatch.cjs +337 -0
  138. package/gsd-core/bin/lib/roadmap-parser.cjs +555 -41
  139. package/gsd-core/bin/lib/roadmap.cjs +292 -69
  140. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +260 -43
  141. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +28 -20
  142. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +294 -108
  143. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +408 -47
  144. package/gsd-core/bin/lib/security.cjs +126 -7
  145. package/gsd-core/bin/lib/shell-command-projection.cjs +4 -0
  146. package/gsd-core/bin/lib/smart-entry.cjs +7 -9
  147. package/gsd-core/bin/lib/state-document.cjs +159 -32
  148. package/gsd-core/bin/lib/state-md-schema.cjs +44 -27
  149. package/gsd-core/bin/lib/state-transition.cjs +465 -62
  150. package/gsd-core/bin/lib/state.cjs +906 -151
  151. package/gsd-core/bin/lib/surface.cjs +83 -10
  152. package/gsd-core/bin/lib/task-command-router.cjs +12 -6
  153. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  154. package/gsd-core/bin/lib/uat.cjs +1420 -516
  155. package/gsd-core/bin/lib/update-context.cjs +36 -26
  156. package/gsd-core/bin/lib/validate.cjs +230 -12
  157. package/gsd-core/bin/lib/vendor/js-yaml.cjs +11 -3
  158. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  159. package/gsd-core/bin/lib/verification.cjs +316 -23
  160. package/gsd-core/bin/lib/verify-command-grounding.cjs +1 -1
  161. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  162. package/gsd-core/bin/lib/verify.cjs +531 -36
  163. package/gsd-core/bin/lib/workstream-inventory.cjs +21 -2
  164. package/gsd-core/bin/lib/worktree-safety.cjs +21 -7
  165. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  166. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  167. package/gsd-core/bin/verify-reapply-patches.cjs +507 -81
  168. package/gsd-core/references/agent-contracts.md +3 -3
  169. package/gsd-core/references/compact-content-gate.md +66 -0
  170. package/gsd-core/references/edge-probe.md +17 -13
  171. package/gsd-core/references/execute-mvp-tdd.md +18 -16
  172. package/gsd-core/references/execute-phase-response-language.md +6 -0
  173. package/gsd-core/references/executor-examples.md +42 -0
  174. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  175. package/gsd-core/references/loop-hook-dispatch.md +18 -0
  176. package/gsd-core/references/model-profiles.md +12 -3
  177. package/gsd-core/references/mvp-concepts.md +2 -2
  178. package/gsd-core/references/plan-checker-examples.md +41 -0
  179. package/gsd-core/references/planner-antipatterns.md +25 -0
  180. package/gsd-core/references/planner-chunked.md +5 -1
  181. package/gsd-core/references/planner-coupling.md +42 -0
  182. package/gsd-core/references/planner-quick-batch.md +71 -0
  183. package/gsd-core/references/planner-reviews.md +47 -0
  184. package/gsd-core/references/planner-revision.md +75 -2
  185. package/gsd-core/references/planning-config.md +5 -1
  186. package/gsd-core/references/response-language-directive.md +9 -0
  187. package/gsd-core/references/revision-loop.md +118 -11
  188. package/gsd-core/references/tdd.md +17 -9
  189. package/gsd-core/references/thinking-models-planning.md +18 -2
  190. package/gsd-core/references/verification-patterns.md +17 -4
  191. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  192. package/gsd-core/references/worktree-path-safety.md +112 -2
  193. package/gsd-core/templates/README.md +7 -1
  194. package/gsd-core/templates/phase-prompt.md +4 -0
  195. package/gsd-core/templates/state.md +6 -3
  196. package/gsd-core/templates/summary.compact.md +212 -0
  197. package/gsd-core/templates/user-setup.compact.md +199 -0
  198. package/gsd-core/templates/user-setup.md +0 -9
  199. package/gsd-core/templates/verification-report.md +5 -0
  200. package/gsd-core/workflows/add-backlog.md +2 -0
  201. package/gsd-core/workflows/add-phase.md +2 -0
  202. package/gsd-core/workflows/add-tests.md +1 -1
  203. package/gsd-core/workflows/add-todo.md +4 -3
  204. package/gsd-core/workflows/ai-integration-phase.md +1 -1
  205. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  206. package/gsd-core/workflows/audit-fix.md +2 -0
  207. package/gsd-core/workflows/audit-milestone.md +2 -0
  208. package/gsd-core/workflows/audit-uat.md +2 -0
  209. package/gsd-core/workflows/autonomous.md +15 -10
  210. package/gsd-core/workflows/check-todos.md +5 -3
  211. package/gsd-core/workflows/cleanup.md +4 -2
  212. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +22 -13
  213. package/gsd-core/workflows/code-review-fix.md +5 -3
  214. package/gsd-core/workflows/code-review.md +211 -43
  215. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +274 -0
  216. package/gsd-core/workflows/complete-milestone.md +40 -254
  217. package/gsd-core/workflows/debug.md +1 -1
  218. package/gsd-core/workflows/diagnose-issues.md +5 -1
  219. package/gsd-core/workflows/discuss-phase/modes/advisor.md +2 -0
  220. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  221. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  222. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  223. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  224. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -0
  225. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  226. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  227. package/gsd-core/workflows/discuss-phase/modes/text.md +2 -0
  228. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  229. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  230. package/gsd-core/workflows/discuss-phase-assumptions.md +1 -1
  231. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  232. package/gsd-core/workflows/discuss-phase.md +1 -1
  233. package/gsd-core/workflows/do.md +43 -13
  234. package/gsd-core/workflows/docs-update/detail/elaboration.md +179 -0
  235. package/gsd-core/workflows/docs-update.md +15 -156
  236. package/gsd-core/workflows/edit-phase.md +2 -0
  237. package/gsd-core/workflows/eval-review.md +1 -1
  238. package/gsd-core/workflows/execute-phase/detail/elaboration.md +124 -0
  239. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +20 -3
  240. package/gsd-core/workflows/execute-phase/steps/completion-reconciliation.md +56 -0
  241. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +24 -3
  242. package/gsd-core/workflows/execute-phase/steps/executor-progress-policy.md +43 -0
  243. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +8 -2
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +2 -0
  245. package/gsd-core/workflows/execute-phase/steps/sequential-root-pin.md +35 -0
  246. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  247. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  248. package/gsd-core/workflows/execute-phase.md +78 -159
  249. package/gsd-core/workflows/execute-plan.md +28 -15
  250. package/gsd-core/workflows/explore.md +2 -0
  251. package/gsd-core/workflows/extract-learnings.md +2 -0
  252. package/gsd-core/workflows/fast.md +6 -0
  253. package/gsd-core/workflows/forensics.md +2 -0
  254. package/gsd-core/workflows/graduation.md +1 -1
  255. package/gsd-core/workflows/health.md +1 -1
  256. package/gsd-core/workflows/help/modes/brief.md +2 -0
  257. package/gsd-core/workflows/help/modes/default.md +2 -0
  258. package/gsd-core/workflows/help/modes/full.compact.md +398 -0
  259. package/gsd-core/workflows/help/modes/full.md +12 -0
  260. package/gsd-core/workflows/help/modes/topic.md +2 -0
  261. package/gsd-core/workflows/help.md +3 -1
  262. package/gsd-core/workflows/import.md +3 -3
  263. package/gsd-core/workflows/inbox.md +1 -1
  264. package/gsd-core/workflows/ingest-docs.md +1 -1
  265. package/gsd-core/workflows/insert-phase.md +2 -0
  266. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  267. package/gsd-core/workflows/list-seeds.md +2 -0
  268. package/gsd-core/workflows/list-workspaces.md +2 -0
  269. package/gsd-core/workflows/manager.md +3 -3
  270. package/gsd-core/workflows/map-codebase.md +52 -3
  271. package/gsd-core/workflows/milestone-summary.md +2 -0
  272. package/gsd-core/workflows/mvp-phase.md +1 -1
  273. package/gsd-core/workflows/new-milestone.md +55 -13
  274. package/gsd-core/workflows/new-project/detail/elaboration.md +216 -0
  275. package/gsd-core/workflows/new-project.md +37 -205
  276. package/gsd-core/workflows/new-workspace.md +1 -1
  277. package/gsd-core/workflows/next.md +2 -0
  278. package/gsd-core/workflows/node-repair.md +2 -0
  279. package/gsd-core/workflows/note.md +2 -0
  280. package/gsd-core/workflows/onboard.md +1 -1
  281. package/gsd-core/workflows/pause-work.md +19 -4
  282. package/gsd-core/workflows/plan-phase/detail/elaboration.md +209 -0
  283. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  284. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +2 -0
  285. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +9 -0
  286. package/gsd-core/workflows/plan-phase.md +144 -185
  287. package/gsd-core/workflows/plan-review-convergence.md +102 -10
  288. package/gsd-core/workflows/plant-seed.md +1 -1
  289. package/gsd-core/workflows/pr-branch.md +30 -10
  290. package/gsd-core/workflows/profile-user.md +1 -1
  291. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  292. package/gsd-core/workflows/progress.md +25 -3
  293. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +37 -2
  294. package/gsd-core/workflows/quick/steps/research-phase.md +3 -3
  295. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  296. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  297. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  298. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  299. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  300. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  301. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  302. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  303. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  304. package/gsd-core/workflows/quick-batch.md +203 -0
  305. package/gsd-core/workflows/quick.md +21 -4
  306. package/gsd-core/workflows/reapply-patches.md +79 -3
  307. package/gsd-core/workflows/remove-phase.md +2 -0
  308. package/gsd-core/workflows/remove-workspace.md +1 -1
  309. package/gsd-core/workflows/resume-project.md +6 -2
  310. package/gsd-core/workflows/review.md +215 -10
  311. package/gsd-core/workflows/scan.md +2 -0
  312. package/gsd-core/workflows/section-manifest.json +12 -0
  313. package/gsd-core/workflows/secure-phase.md +1 -1
  314. package/gsd-core/workflows/session-report.md +2 -0
  315. package/gsd-core/workflows/settings-advanced.md +2 -0
  316. package/gsd-core/workflows/settings-integrations.md +9 -8
  317. package/gsd-core/workflows/settings.md +19 -6
  318. package/gsd-core/workflows/ship.md +10 -10
  319. package/gsd-core/workflows/sketch-wrap-up.md +2 -0
  320. package/gsd-core/workflows/sketch.md +1 -1
  321. package/gsd-core/workflows/smart-entry.md +1 -1
  322. package/gsd-core/workflows/spec-phase.md +24 -19
  323. package/gsd-core/workflows/spike-wrap-up.md +2 -0
  324. package/gsd-core/workflows/spike.md +1 -1
  325. package/gsd-core/workflows/stats.md +2 -0
  326. package/gsd-core/workflows/sync-skills.md +12 -4
  327. package/gsd-core/workflows/thread.md +2 -0
  328. package/gsd-core/workflows/transition.md +2 -0
  329. package/gsd-core/workflows/ui-phase.md +26 -5
  330. package/gsd-core/workflows/ui-review.md +1 -1
  331. package/gsd-core/workflows/ultraplan-phase.md +2 -0
  332. package/gsd-core/workflows/undo.md +1 -1
  333. package/gsd-core/workflows/update.md +48 -43
  334. package/gsd-core/workflows/validate-phase.md +1 -1
  335. package/gsd-core/workflows/verify-work/detail/elaboration.md +230 -0
  336. package/gsd-core/workflows/verify-work.md +68 -182
  337. package/hooks/dist/gsd-agent-isolation-guard.js +42 -16
  338. package/hooks/dist/gsd-check-update-worker.js +19 -2
  339. package/hooks/dist/gsd-context-monitor.js +371 -27
  340. package/hooks/dist/gsd-cursor-subagent-start.js +34 -14
  341. package/hooks/dist/gsd-node-runner.sh +1 -0
  342. package/hooks/dist/gsd-prompt-guard.js +30 -5
  343. package/hooks/dist/gsd-read-guard.js +2 -0
  344. package/hooks/dist/gsd-read-injection-scanner.js +5 -5
  345. package/hooks/dist/gsd-secret-read-guard.js +1105 -0
  346. package/hooks/dist/gsd-statusline.js +18 -10
  347. package/hooks/dist/gsd-validate-commit.sh +474 -7
  348. package/hooks/dist/gsd-workflow-guard.js +2 -1
  349. package/hooks/dist/gsd-worktree-path-guard.js +25 -14
  350. package/hooks/dist/gsd-write-guard.js +46 -1
  351. package/hooks/dist/lib/dispatch-identity.js +187 -0
  352. package/hooks/dist/lib/filename-classification.js +64 -0
  353. package/hooks/dist/lib/git-cmd.js +210 -1
  354. package/hooks/dist/lib/injection-patterns.js +36 -6
  355. package/hooks/dist/lib/isolation-deny-reason.js +53 -1
  356. package/hooks/dist/lib/isolation-sentinel.js +58 -19
  357. package/hooks/dist/managed-hooks-registry.cjs +1 -0
  358. package/hooks/gsd-agent-isolation-guard.js +42 -16
  359. package/hooks/gsd-check-update-worker.js +19 -2
  360. package/hooks/gsd-context-monitor.js +371 -27
  361. package/hooks/gsd-cursor-subagent-start.js +34 -14
  362. package/hooks/gsd-node-runner.sh +1 -0
  363. package/hooks/gsd-prompt-guard.js +30 -5
  364. package/hooks/gsd-read-guard.js +2 -0
  365. package/hooks/gsd-read-injection-scanner.js +5 -5
  366. package/hooks/gsd-secret-read-guard.js +1105 -0
  367. package/hooks/gsd-statusline.js +18 -10
  368. package/hooks/gsd-validate-commit.sh +474 -7
  369. package/hooks/gsd-workflow-guard.js +2 -1
  370. package/hooks/gsd-worktree-path-guard.js +25 -14
  371. package/hooks/gsd-write-guard.js +46 -1
  372. package/hooks/hooks.json +6 -0
  373. package/hooks/lib/dispatch-identity.js +187 -0
  374. package/hooks/lib/filename-classification.js +64 -0
  375. package/hooks/lib/git-cmd.js +210 -1
  376. package/hooks/lib/injection-patterns.js +36 -6
  377. package/hooks/lib/isolation-deny-reason.js +53 -1
  378. package/hooks/lib/isolation-sentinel.js +58 -19
  379. package/hooks/managed-hooks-registry.cjs +1 -0
  380. package/package.json +13 -9
  381. package/scripts/benchmark-compact-content-variants.cjs +298 -0
  382. package/scripts/benchmark-compact-content.cjs +368 -0
  383. package/scripts/build-hooks.js +11 -4
  384. package/scripts/check-contract-drift.cjs +4 -1
  385. package/scripts/check-env.cjs +36 -8
  386. package/scripts/check-glossary-refs.cjs +25 -21
  387. package/scripts/ci-next-health.cjs +271 -0
  388. package/scripts/ci-prepare-test-scope.cjs +7 -7
  389. package/scripts/ci-test-scope.cjs +133 -20
  390. package/scripts/ci-timeout-report.cjs +1 -1
  391. package/scripts/diff-touches-shipped-paths.cjs +1 -1
  392. package/scripts/docs-guard-registry.cjs +17 -2
  393. package/scripts/gen-adr-index.cjs +8 -2
  394. package/scripts/gen-inventory-manifest.cjs +12 -0
  395. package/scripts/gen-loop-host-contract.cjs +67 -15
  396. package/scripts/gen-platform-conformance-tier.cjs +557 -0
  397. package/scripts/lib/drift-scan.cjs +1 -1
  398. package/scripts/lib/macos-conformance-tier.generated.cjs +210 -0
  399. package/scripts/lib/npm-version-check-diagnosis.cjs +59 -0
  400. package/scripts/lib/platform-conformance-tier.generated.cjs +276 -0
  401. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  402. package/scripts/lib/suite-detection.cjs +32 -0
  403. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  404. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  405. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  406. package/scripts/lint-allowed-tools-parity.cjs +221 -0
  407. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +24 -2
  408. package/scripts/lint-phase-enumeration-drift.cjs +24 -6
  409. package/scripts/lint-phase-id-drift.cjs +465 -15
  410. package/scripts/lint-portable-grep.cjs +176 -0
  411. package/scripts/lint-response-language-coverage.cjs +530 -0
  412. package/scripts/lint-source-test-name-collision.cjs +1 -1
  413. package/scripts/lint-test-file-count.allowlist.json +4 -1
  414. package/scripts/lint-vendored-deps.cjs +128 -17
  415. package/scripts/lint-workflow-shellcheck-baseline.json +1112 -0
  416. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  417. package/scripts/npm-audit-baseline.cjs +376 -0
  418. package/scripts/prompt-injection-scan.sh +22 -0
  419. package/scripts/require-issue-link-policy.cjs +16 -1
  420. package/scripts/workflow-size.cjs +139 -0
  421. package/skills/gsd-cleanup/SKILL.md +1 -0
  422. package/skills/gsd-code-review/SKILL.md +2 -1
  423. package/skills/gsd-complete-milestone/SKILL.md +1 -0
  424. package/skills/gsd-config/SKILL.md +1 -0
  425. package/skills/gsd-debug/SKILL.md +1 -0
  426. package/skills/gsd-execute-phase/SKILL.md +1 -1
  427. package/skills/gsd-graphify/SKILL.md +1 -0
  428. package/skills/gsd-health/SKILL.md +1 -0
  429. package/skills/gsd-mempalace-capture/SKILL.md +1 -0
  430. package/skills/gsd-mempalace-recall/SKILL.md +1 -0
  431. package/skills/gsd-new-milestone/SKILL.md +1 -0
  432. package/skills/gsd-new-project/SKILL.md +1 -0
  433. package/skills/gsd-next/SKILL.md +1 -0
  434. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  435. package/skills/gsd-pause-work/SKILL.md +1 -0
  436. package/skills/gsd-phase/SKILL.md +2 -1
  437. package/skills/gsd-pr-branch/SKILL.md +1 -0
  438. package/skills/gsd-quick-batch/SKILL.md +105 -0
  439. package/skills/gsd-resume-work/SKILL.md +1 -0
  440. package/skills/gsd-review-backlog/SKILL.md +1 -0
  441. package/skills/gsd-settings/SKILL.md +2 -1
  442. package/skills/gsd-stats/SKILL.md +1 -0
  443. package/skills/gsd-surface/SKILL.md +18 -8
  444. package/skills/gsd-thread/SKILL.md +1 -0
  445. package/skills/gsd-workspace/SKILL.md +1 -0
  446. package/skills/gsd-workstreams/SKILL.md +1 -0
  447. package/vscode/package.json +1 -1
  448. package/gsd-core/templates/claude-md.md +0 -145
  449. package/gsd-core/templates/codebase/concerns.md +0 -310
  450. package/gsd-core/templates/codebase/conventions.md +0 -307
  451. package/gsd-core/templates/codebase/integrations.md +0 -280
  452. package/gsd-core/templates/codebase/structure.md +0 -285
  453. package/gsd-core/templates/codebase/testing.md +0 -480
  454. package/gsd-core/templates/debug-subagent-prompt.md +0 -91
  455. package/gsd-core/templates/discovery.md +0 -146
@@ -27,12 +27,13 @@
27
27
  * "failed" and "ran cleanly with nothing to report" IS the defect this epic closes (#2494/#2605).
28
28
  */
29
29
  Object.defineProperty(exports, "__esModule", { value: true });
30
- exports.MODEL_VALUE_MAX = exports.BANNER_SCAN_LINES = exports.UNRESOLVED_MODEL = exports.MODEL_SOURCE = void 0;
30
+ exports.ANTIGRAVITY_FAILURE_MODE = exports.MODEL_VALUE_MAX = exports.BANNER_SCAN_LINES = exports.UNRESOLVED_MODEL = exports.MODEL_SOURCE = void 0;
31
31
  exports.parseModelBanner = parseModelBanner;
32
32
  exports.parseTranscriptModel = parseTranscriptModel;
33
33
  exports.checkEgressHost = checkEgressHost;
34
34
  exports.probeLane = probeLane;
35
35
  exports.writeReviewOrStub = writeReviewOrStub;
36
+ exports.emptyOutputDiagnosis = emptyOutputDiagnosis;
36
37
  exports.handleOpencodeOutput = handleOpencodeOutput;
37
38
  exports.antigravityWatermark = antigravityWatermark;
38
39
  exports.antigravityTranscriptFallback = antigravityTranscriptFallback;
@@ -40,6 +41,7 @@ exports.antigravityModel = antigravityModel;
40
41
  exports.resolveSpawnModel = resolveSpawnModel;
41
42
  exports.antigravityPrompt = antigravityPrompt;
42
43
  exports.antigravityArgv = antigravityArgv;
44
+ exports.antigravityFailureMode = antigravityFailureMode;
43
45
  exports.antigravityDiagnostic = antigravityDiagnostic;
44
46
  exports.stampBlindReview = stampBlindReview;
45
47
  exports.stampUngroundedReview = stampUngroundedReview;
@@ -354,8 +356,15 @@ async function probeLane(plan, deps) {
354
356
  * `extraDiagnostics` carries the raw HTTP response body for the OpenAI-compatible lanes: an error
355
357
  * from such a server arrives with HTTP 4xx/5xx and the JSON in the BODY, so stderr alone is empty
356
358
  * and the body is the only evidence.
359
+ *
360
+ * `outcome` (#4255) carries the spawn's exit status so the stub can say WHICH empty it is. The
361
+ * header alone cannot: a crash, a timeout kill and a model that ended its turn without writing a
362
+ * final message all reach here as the same zero bytes, and the third is what a too-low reasoning
363
+ * effort produces on a large source-grounded prompt. A clean exit inside the timeout with no
364
+ * output is a stopped-short model, and the stub now says so — with the effort it ran at, which is
365
+ * the value the operator would change.
357
366
  */
358
- function writeReviewOrStub(plan, content, deps, extraDiagnostics) {
367
+ function writeReviewOrStub(plan, content, deps, extraDiagnostics, outcome) {
359
368
  if (!(0, review_lane_invocation_cjs_1.isEmptyReview)(content)) {
360
369
  deps.writeFile(plan.reviewPath, content.endsWith('\n') ? content : `${content}\n`);
361
370
  return { stubbed: false };
@@ -364,9 +373,53 @@ function writeReviewOrStub(plan, content, deps, extraDiagnostics) {
364
373
  const parts = [`${plan.slug} review failed or returned empty output. stderr:`, stderr];
365
374
  if (extraDiagnostics)
366
375
  parts.push('Raw response body:', extraDiagnostics);
376
+ parts.push(emptyOutputDiagnosis(plan, outcome));
367
377
  deps.writeFile(plan.reviewPath, `${parts.join('\n')}\n`);
368
378
  return { stubbed: true };
369
379
  }
380
+ /**
381
+ * One line naming the effort the lane ran at and how the process ended (#4255).
382
+ *
383
+ * Kept out of the header so the `failed or returned empty output` string every downstream reader
384
+ * greps for is untouched — this is an added line, not a reworded one.
385
+ */
386
+ function emptyOutputDiagnosis(plan, outcome) {
387
+ // `effort` lives on the spawn plan only. An HTTP lane reaches a server directly and has no
388
+ // reviewer CLI at all, so naming one there would be a lie about what ran (Codex review of
389
+ // #4255) — the two transports get different, accurate wording.
390
+ const spawned = plan.transport === 'spawn';
391
+ const level = spawned ? plan.effort : null;
392
+ const effort = level
393
+ ? `ran at effort=${level}`
394
+ : spawned
395
+ ? "ran with no effort argument, so the reviewer CLI's own configuration applied"
396
+ : 'is an HTTP lane and carries no reasoning-effort setting';
397
+ if (!outcome)
398
+ return `Diagnosis: ${plan.slug} ${effort}.`;
399
+ // Four endings, not two. `status` is null for BOTH a timeout kill and a process that never
400
+ // ran or died on a signal (ENOENT, SIGKILL) — reporting the latter as "status null" said
401
+ // nothing, and folding them together would attach the stopped-short hint to a crash.
402
+ const timedOut = outcome.errorCode === 'ETIMEDOUT';
403
+ const neverRan = !timedOut && outcome.status === null;
404
+ const cleanExit = outcome.status === 0;
405
+ const ending = timedOut
406
+ ? 'was killed by the outer timeout'
407
+ : neverRan
408
+ ? `did not exit normally (${outcome.errorCode ?? 'killed by a signal'})`
409
+ : cleanExit
410
+ ? 'exited cleanly inside the timeout'
411
+ : `exited with status ${String(outcome.status)}`;
412
+ // Hedged deliberately. A clean exit with no output is CONSISTENT with a model ending its turn
413
+ // without a final message — which is what too low an effort produces on a large prompt — but it
414
+ // is equally consistent with the CLI writing its output somewhere this lane did not read. The
415
+ // line points at the likeliest cause without asserting it.
416
+ const tail = cleanExit
417
+ ? ' — a clean exit that produced no output is most often a model ending its turn without'
418
+ + ' writing a final message rather than a crash; if this lane carries a reasoning effort,'
419
+ + ' raising it is the usual fix.'
420
+ : '.';
421
+ return `Diagnosis: ${plan.slug} ${effort} and ${ending}${tail}`;
422
+ }
370
423
  /* ------------------------------------------------------------------ *
371
424
  * Handlers (D6) — named first-party code, never conditionals in data
372
425
  * ------------------------------------------------------------------ */
@@ -677,9 +730,13 @@ function antigravityPrompt(promptPath, repoRoot) {
677
730
  * lane that fails to start is worse than one that runs on the prompt anchor alone.
678
731
  * 2. The self-report prompt variant above, swapped in for the standard file-ref text.
679
732
  *
680
- * Both are argv shape, so they belong here rather than in the descriptor: expressing "add this flag
681
- * only if the binary's --help mentions it" as data would need a conditional, which is precisely
682
- * what the named-handler seam exists to absorb (ADR-2782 D6).
733
+ * Both are argv shape, so they belong here rather than in the descriptor: expressing "add this
734
+ * flag only if the binary's --help mentions it" as data would need a conditional, which is
735
+ * precisely what the named-handler seam exists to absorb (ADR-2782 D6). The native
736
+ * `--print-timeout` VALUE (#3274) is NOT this handler's job — `resolveLanePlan`
737
+ * (review-lane-invocation.cts) resolves the `{{nativeTimeout}}` ARGV_PLACEHOLDER itself, exactly
738
+ * like `{{model}}`/`{{effort}}`/`{{output}}`/`{{prompt}}`, so `plan.argv` arrives here already fully
739
+ * resolved.
683
740
  */
684
741
  function antigravityArgv(argv, promptPath, repoRoot, deps) {
685
742
  const standard = (0, review_lane_invocation_cjs_1.fileRefPrompt)(promptPath, repoRoot);
@@ -710,8 +767,54 @@ function antigravityArgv(argv, promptPath, repoRoot, deps) {
710
767
  *
711
768
  * Mode 3 (a pre-session stall, which `--print-timeout` cannot bound because it cannot fire before a
712
769
  * session exists) leaves no log line at all, so its tell is stated rather than searched for.
770
+ *
771
+ * #3996 mode 4 (a headless tool-permission denial) exits 0 with empty stdout and a POPULATED
772
+ * transcript, and reports the cause only on stderr — so the stub carries the run's stderr
773
+ * (`evidence.stderr`, the same `plan.errPath` content the generic stub reads), and the mode-3
774
+ * tell is stated only when `antigravityFailureMode` rules a session out (a fresh conv-id or
775
+ * transcript growth past the watermark means one verifiably started). Asserting mode 3 over a
776
+ * transcript this code just read inverts who is better placed to know.
713
777
  */
714
- function antigravityDiagnostic(deps) {
778
+ /**
779
+ * What actually failed, as a typed fact rather than a guess baked into prose. #3996.
780
+ *
781
+ * The session-started question is decidable at diagnostic time with the same staleness rule the
782
+ * layer-2 fallback uses: re-resolve the workspace's CURRENT conv-id and compare against the
783
+ * pre-spawn watermark. A conv-id that changed, or a transcript that grew past the watermark
784
+ * line count, means THIS invocation demonstrably reached a session — a pre-launch stall is
785
+ * ruled out. An unchanged conv-id with no growth means nothing new happened, which is the
786
+ * #2073 mode-3 shape even when a PRIOR session's transcript still exists on disk (existence
787
+ * alone would mis-diagnose mode 3 as mode 4 in any workspace with history).
788
+ */
789
+ exports.ANTIGRAVITY_FAILURE_MODE = Object.freeze({
790
+ /** A session ran this invocation (fresh conv-id, or transcript growth) but no review was recovered. */
791
+ SESSION_STARTED: 'session_started',
792
+ /** No session this invocation — the #2073 mode-3 stall tell is the right signpost. */
793
+ PRE_SESSION_STALL: 'pre_session_stall',
794
+ });
795
+ function antigravityFailureMode(workspace, mark, deps) {
796
+ const convId = resolveWorkspaceConvId(workspace, deps);
797
+ if (!convId)
798
+ return exports.ANTIGRAVITY_FAILURE_MODE.PRE_SESSION_STALL;
799
+ // A different conv-id than the watermark saw = a fresh session this run, transcript or not.
800
+ if (convId !== mark.convId)
801
+ return exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED;
802
+ const tx = transcriptPath(deps.homeDir, convId);
803
+ if (!deps.exists(tx))
804
+ return exports.ANTIGRAVITY_FAILURE_MODE.PRE_SESSION_STALL;
805
+ try {
806
+ const lines = deps.readFile(tx).split(/\r?\n/).filter((l) => l.trim()).length;
807
+ return lines > mark.lines
808
+ ? exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED
809
+ : exports.ANTIGRAVITY_FAILURE_MODE.PRE_SESSION_STALL;
810
+ }
811
+ catch {
812
+ // #3118 fail-closed shape: the transcript indisputably exists but cannot be read — do not
813
+ // assert the stall case over a file this code cannot check.
814
+ return exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED;
815
+ }
816
+ }
817
+ function antigravityDiagnostic(deps, evidence = {}) {
715
818
  const lines = [
716
819
  'Antigravity review failed or returned empty output.',
717
820
  ];
@@ -731,8 +834,28 @@ function antigravityDiagnostic(deps) {
731
834
  /* an unreadable log is not worth failing the lane over */
732
835
  }
733
836
  }
734
- lines.push('If no agy run started, that is the pre-session-stall case: check whether a new ' +
735
- '~/.gemini/antigravity-cli/brain/<conv-id>/ dir appeared within ~30s of launch.');
837
+ // #3996: the stub carries agy's stderr, as the generic lane stub already does — mode 4 (a
838
+ // headless tool-permission denial) exits 0 with empty stdout and a populated transcript, and
839
+ // the stderr line is the only signal that names its cause.
840
+ const stderr = (evidence.stderr ?? '').trim();
841
+ if (stderr)
842
+ lines.push('stderr:', stderr);
843
+ // #3996: mode 3's tell is stated only when its precondition holds — the mode is computed from
844
+ // the watermark (#3996 mode 3 in a workspace with history is a no-growth transcript, not an
845
+ // absent one), never guessed from prose.
846
+ if (evidence.mode === exports.ANTIGRAVITY_FAILURE_MODE.SESSION_STARTED) {
847
+ lines.push('An agy session started this run but no review was recovered, so a pre-launch stall is ' +
848
+ 'ruled out.' +
849
+ (stderr
850
+ ? ' See stderr above; a headless run that was auto-denied a tool permission reports ' +
851
+ 'the cause and its fix there.'
852
+ : ' agy reported nothing on its error stream; inspect the transcript under ' +
853
+ '~/.gemini/antigravity-cli/brain/<conv-id>/ for where the session stopped.'));
854
+ }
855
+ else {
856
+ lines.push('If no agy run started, that is the pre-session-stall case: check whether a new ' +
857
+ '~/.gemini/antigravity-cli/brain/<conv-id>/ dir appeared within ~30s of launch.');
858
+ }
736
859
  return lines.join('\n');
737
860
  }
738
861
  /**
@@ -948,7 +1071,10 @@ function runSpawnLane(plan, deps, repoRoot) {
948
1071
  // pinned model that 404s server-side and exits 0 with empty output, so the model IS the
949
1072
  // diagnosis — dropping it here would throw away the one piece of evidence the stub exists
950
1073
  // to preserve.
951
- deps.writeFile(plan.reviewPath, `${antigravityDiagnostic(deps)}\n`);
1074
+ deps.writeFile(plan.reviewPath, `${antigravityDiagnostic(deps, {
1075
+ stderr: errContent,
1076
+ mode: antigravityFailureMode(repoRoot, mark, deps),
1077
+ })}\n`);
952
1078
  return { slug: plan.slug, ok: true, stubbed: true, model };
953
1079
  }
954
1080
  }
@@ -959,7 +1085,7 @@ function runSpawnLane(plan, deps, repoRoot) {
959
1085
  // folded in as a diff observation, and the citation check must not change that surface.
960
1086
  if (plan.evidenceClass !== 'diff-only')
961
1087
  review = stampUngroundedReview(review);
962
- const { stubbed } = writeReviewOrStub(plan, review, deps, extra);
1088
+ const { stubbed } = writeReviewOrStub(plan, review, deps, extra, out);
963
1089
  return { slug: plan.slug, ok: true, stubbed, model };
964
1090
  }
965
1091
  async function runHttpLane(plan, deps) {
@@ -0,0 +1,337 @@
1
+ "use strict";
2
+ /**
3
+ * Reviewer Step Dispatch (#4209 Phase 1 Plan 2, ADR-2782 seam).
4
+ *
5
+ * ONE interpreter for "a step declares `supportsReviewerLanes: true`" — see
6
+ * `gsd-core/references/loop-hook-dispatch.md` for the canonical explanation of the trait and how
7
+ * `review-lane dispatch-step` re-derives it. This module trusts `trait` exactly as given: it is
8
+ * the CALLER's job to have derived it correctly. Every direct or lifecycle caller routes through
9
+ * `dispatchReviewerLanes` so selection/plan/invoke logic is owned once, not re-derived per
10
+ * feature. This module owns NONE of those primitives — it wires `resolveReviewerSelection`
11
+ * (selection) and `resolveLanePlan` (planning), the same building blocks
12
+ * `gsd-core/bin/gsd-tools.cjs`'s `review-lane plan` subcommand uses. Invocation (`runLane`) needs
13
+ * OS-aware spawn/probe plumbing this module does not own, so `deps.invoke` is the one required,
14
+ * caller-supplied seam (wired for real in `gsd-core/bin/gsd-tools.cjs`'s `review-lane
15
+ * dispatch-step` route).
16
+ *
17
+ * Fail-closed contract:
18
+ * - Trait not exactly `true`, or nothing selected → inert. Zero plan/invoke calls.
19
+ * - Missing/unsafe request-level input (paths escaping `repoRoot`, absent depth/base SHA) stops
20
+ * the WHOLE dispatch before any lane is planned or invoked.
21
+ * - An explicitly requested lane the selector could not resolve does not silently narrow the
22
+ * result to only what worked: lanes that DID resolve still run and their results are kept,
23
+ * but the aggregate `ok` is `false` so no caller mistakes a partial run for a clean one.
24
+ * - Once a lane is planned, a per-lane plan/budget/invoke failure never displaces or cancels a
25
+ * sibling lane already run.
26
+ * - A lane whose `promptChannel` is `'none'` (it reviews the working tree on its own terms, fed
27
+ * nothing — e.g. `coderabbit`) cannot receive the bounded prompt below and is rejected before
28
+ * plan/invoke, same as an unresolved slug.
29
+ *
30
+ * The bounded source-review prompt built here is METADATA ONLY — repository root, canonical
31
+ * file paths, review depth, base SHA, and four fixed prohibitions. It never embeds file
32
+ * contents. If the assembled prompt exceeds a lane's resolved budget, that lane's dispatch
33
+ * hard-fails before `invoke` runs for it — no silent truncation of the file list.
34
+ */
35
+ var __importDefault = (this && this.__importDefault) || function (mod) {
36
+ return (mod && mod.__esModule) ? mod : { "default": mod };
37
+ };
38
+ Object.defineProperty(exports, "__esModule", { value: true });
39
+ exports.SOURCE_REVIEW_PROHIBITIONS = exports.DISPATCH_REASON = void 0;
40
+ exports.buildSourceReviewPrompt = buildSourceReviewPrompt;
41
+ exports.dispatchReviewerLanes = dispatchReviewerLanes;
42
+ const node_fs_1 = __importDefault(require("node:fs"));
43
+ const node_path_1 = __importDefault(require("node:path"));
44
+ const prompt_budget_cjs_1 = require("./prompt-budget.cjs");
45
+ const security_cjs_1 = require("./security.cjs");
46
+ const review_lane_invocation_cjs_1 = require("./review-lane-invocation.cjs");
47
+ const review_reviewer_selection_cjs_1 = require("./review-reviewer-selection.cjs");
48
+ /** Closed set of request-level (not per-lane) halt reasons. Mirrors `LANE_UNAVAILABLE`'s shape. */
49
+ exports.DISPATCH_REASON = Object.freeze({
50
+ TRAIT_NOT_ENABLED: 'trait_not_enabled',
51
+ NO_LANES_SELECTED: 'no_lanes_selected',
52
+ SELECTION_FAILED: 'selection_failed',
53
+ INVALID_PATHS: 'invalid_paths',
54
+ PATH_ESCAPES_REPO_ROOT: 'path_escapes_repo_root',
55
+ MISSING_PROVENANCE: 'missing_provenance',
56
+ INVALID_PROVENANCE: 'invalid_provenance',
57
+ PROMPT_WRITE_FAILED: 'prompt_write_failed',
58
+ });
59
+ /** Fixed, non-negotiable prompt constraints (SAFE-03..SAFE-06). Order is the display order. */
60
+ exports.SOURCE_REVIEW_PROHIBITIONS = Object.freeze([
61
+ 'Do not modify any source file.',
62
+ 'Do not run tests.',
63
+ 'Do not start background processes.',
64
+ 'Do not poll or wait — return findings from a single read-only pass.',
65
+ ]);
66
+ // #4209 RQ-04: a control character (newline, CR, NUL, ...) in ANY string this module embeds
67
+ // into the external prompt (`buildSourceReviewPrompt`) lets it inject a fabricated section —
68
+ // not just via `paths` (agy-F1's original finding), since `depth`, `baseSha`, and `repoRoot` land
69
+ // in that same markdown. Every embedded string is checked against this ONE shared boundary.
70
+ const CONTROL_CHAR = /[\x00-\x1f\x7f\u2028\u2029]/;
71
+ function defaultWritePromptFile(filePath, content) {
72
+ node_fs_1.default.writeFileSync(filePath, content, 'utf8');
73
+ }
74
+ /**
75
+ * Validate that every path is a non-empty string resolving INSIDE `repoRoot` — blocks `..`
76
+ * traversal and absolute paths pointing elsewhere before any lane sees them.
77
+ */
78
+ function validatePaths(repoRoot, paths) {
79
+ if (!Array.isArray(paths) || paths.length === 0) {
80
+ return { ok: false, reason: exports.DISPATCH_REASON.INVALID_PATHS };
81
+ }
82
+ const root = node_path_1.default.resolve(String(repoRoot ?? ''));
83
+ // repoRoot itself may be a symlink (e.g. a `/tmp`-based worktree on macOS, where `/tmp` is
84
+ // itself a symlink to `/private/tmp`) — realpath it once so the per-path comparison below
85
+ // compares like with like, not a resolved child path against an unresolved root.
86
+ let realRoot;
87
+ try {
88
+ realRoot = node_fs_1.default.realpathSync(root);
89
+ }
90
+ catch {
91
+ realRoot = root;
92
+ }
93
+ // #4209 agy-F1: a control character (newline, CR, NUL, ...) in a path lets a maliciously
94
+ // named repo file inject a fabricated section into the markdown prompt built from `paths`
95
+ // below (buildSourceReviewPrompt) — reject it here, at the shared trust boundary, rather than
96
+ // relying on the incidental quoting `git diff --name-only` happens to apply upstream.
97
+ for (const p of paths) {
98
+ if (typeof p !== 'string' || p.length === 0 || CONTROL_CHAR.test(p)) {
99
+ return { ok: false, reason: exports.DISPATCH_REASON.INVALID_PATHS };
100
+ }
101
+ // ADR-4650 decision 6: lexical family — this is the first of the two
102
+ // deliberate halves (#4209 WR-05); the ENOENT-tolerant realpath half
103
+ // below cannot be folded into a single `tryWithinRoot` call (its
104
+ // ancestor-walk would accept a deleted path via the nearest existing
105
+ // ancestor, not the explicit `continue` this code requires).
106
+ const resolved = node_path_1.default.resolve(root, p);
107
+ if ((0, security_cjs_1.tryWithinRootLexical)(p, root) === null) {
108
+ return { ok: false, reason: exports.DISPATCH_REASON.PATH_ESCAPES_REPO_ROOT };
109
+ }
110
+ // #4209 WR-05: `path.resolve` is lexical only — a symlink whose OWN path sits inside
111
+ // repoRoot can still point outside it, passing the check above while listing an
112
+ // out-of-repo file for the external lane to read. `fs.realpathSync` follows the link;
113
+ // ENOENT is expected and benign here (a `git diff --name-only` path can legitimately name
114
+ // a file already deleted in a stale worktree) and is not itself an escape.
115
+ let real;
116
+ try {
117
+ real = node_fs_1.default.realpathSync(resolved);
118
+ }
119
+ catch {
120
+ continue;
121
+ }
122
+ if ((0, security_cjs_1.tryWithinRootLexical)(real, realRoot) === null) {
123
+ return { ok: false, reason: exports.DISPATCH_REASON.PATH_ESCAPES_REPO_ROOT };
124
+ }
125
+ }
126
+ return { ok: true };
127
+ }
128
+ // `resolveLaneBudget` (review-lane-invocation.cjs) resolves the number; `null` and a resolved
129
+ // `0` both mean unbounded (#2797) — the caller's overflow check must test both `!== null` and
130
+ // `!== 0`. See the call site below.
131
+ /**
132
+ * One-line depth definition for an external reviewer lane, condensed from `<depth_levels>` in
133
+ * `agents/gsd-code-reviewer.md` (#4209 review: a bare `quick`/`standard`/`deep` label means
134
+ * nothing to a third-party CLI that never sees that agent's system prompt — unlike the internal
135
+ * reviewer, whose own persona fully defines these three terms). Every category named here must
136
+ * stay a strict subset of what `<depth_levels>` actually does — `tests/reviewer-step-dispatch
137
+ * .test.cjs`'s "depthMeaning tracks depth_levels" tests assert each case against the real agent
138
+ * file, not just against this function, so the two cannot silently drift again. An unrecognised
139
+ * depth normalizes to `standard`'s text, matching `agents/gsd-code-reviewer.md`'s own "if depth
140
+ * is not one of quick/standard/deep, default to standard" rule — the raw label is not repeated
141
+ * here since `buildSourceReviewPrompt` already states it once, verbatim, earlier in the prompt.
142
+ */
143
+ function depthMeaning(depth) {
144
+ switch (depth) {
145
+ case 'quick':
146
+ return 'pattern-scan without reading full file contents: hardcoded secrets, dangerous functions, debug artifacts, empty catch blocks, commented-out code';
147
+ case 'standard':
148
+ return 'read each changed file in context for bugs, security, and quality problems; cross-reference imports and exports';
149
+ case 'deep':
150
+ return 'standard, plus cross-file analysis: trace call chains, check type consistency at API boundaries, verify error propagation, check state mutation consistency, detect circular dependencies';
151
+ default:
152
+ return depthMeaning('standard');
153
+ }
154
+ }
155
+ /**
156
+ * Build the bounded source-review prompt. Metadata only — repoRoot, paths, depth, base SHA, and
157
+ * the four fixed prohibitions. NEVER embeds file contents.
158
+ */
159
+ function buildSourceReviewPrompt(input) {
160
+ // Base SHA is identical for every file and already stated once above — repeating it per line
161
+ // (as an earlier version of this prompt did) wastes real tokens at O(files), for zero
162
+ // information gain, on every dispatched lane.
163
+ const fileLines = input.paths.map((p) => `- ${p}`).join('\n');
164
+ const ruleLines = exports.SOURCE_REVIEW_PROHIBITIONS.map((r, i) => `${i + 1}. ${r}`).join('\n');
165
+ return [
166
+ '## Source Review Request',
167
+ '',
168
+ `Repository root: ${input.repoRoot}`,
169
+ `Review depth: ${input.depth}`,
170
+ `Base SHA: ${input.baseSha}`,
171
+ '',
172
+ 'Review the changes introduced in each file below relative to the base SHA above, at the',
173
+ `requested depth (${depthMeaning(input.depth)}). Report every bug, security issue, and`,
174
+ 'code-quality problem you find. For every claim you make, cite the exact file path and line',
175
+ 'number(s) it applies to — a claim with no file:line citation cannot be independently',
176
+ 're-verified and will be discarded by the consolidating reviewer. Performance issues',
177
+ '(O(n²), memory leaks) are out of scope unless also correctness issues (e.g. an infinite',
178
+ 'loop) — do not flag them otherwise.',
179
+ '',
180
+ '### Files in scope',
181
+ fileLines,
182
+ '',
183
+ '### Rules',
184
+ ruleLines,
185
+ ].join('\n');
186
+ }
187
+ /**
188
+ * Dispatch every selected reviewer lane for one opted-in step. See module docstring for scope.
189
+ */
190
+ async function dispatchReviewerLanes(input, deps) {
191
+ if (input.trait !== true) {
192
+ return { dispatched: false, ok: true, reason: exports.DISPATCH_REASON.TRAIT_NOT_ENABLED, results: [] };
193
+ }
194
+ const resolveSelection = deps.resolveSelection ?? review_reviewer_selection_cjs_1.resolveReviewerSelection;
195
+ const selection = resolveSelection(input.selection);
196
+ if (selection.selected.length === 0) {
197
+ // Distinguish "explicitly requested but every candidate was unavailable" (a real failure —
198
+ // `errors` is non-empty) from "nothing was ever requested" (a clean, inert no-op).
199
+ const reason = selection.errors.length > 0
200
+ ? exports.DISPATCH_REASON.SELECTION_FAILED
201
+ : exports.DISPATCH_REASON.NO_LANES_SELECTED;
202
+ return { dispatched: false, ok: selection.errors.length === 0, reason, selection, results: [] };
203
+ }
204
+ const pathCheck = validatePaths(input.repoRoot, input.paths);
205
+ if (!pathCheck.ok) {
206
+ return { dispatched: false, ok: false, reason: pathCheck.reason, selection, results: [] };
207
+ }
208
+ // #4209 RQ-04: depth/baseSha/repoRoot/runDir land in the SAME markdown prompt `paths` does
209
+ // (buildSourceReviewPrompt, `dispatchReviewerLanes`'s `runDir`-derived promptPath write) — a
210
+ // control character in any of them is the identical injection vector agy-F1 found in `paths`,
211
+ // so this trust boundary must reject it here too, not just for the file list.
212
+ if (typeof input.depth !== 'string' || input.depth.length === 0
213
+ || typeof input.baseSha !== 'string' || input.baseSha.length === 0
214
+ || typeof input.repoRoot !== 'string' || input.repoRoot.length === 0
215
+ || typeof input.runDir !== 'string' || input.runDir.length === 0) {
216
+ return { dispatched: false, ok: false, reason: exports.DISPATCH_REASON.MISSING_PROVENANCE, selection, results: [] };
217
+ }
218
+ // #4209 WR-04: a present-but-malicious field (control character) is a different failure mode
219
+ // than an absent one — MISSING_PROVENANCE above means "the caller never supplied this"; this
220
+ // branch means "the caller supplied something and it's an injection attempt," which a caller
221
+ // handling the two reasons differently (e.g. surfacing one as a config problem, the other as
222
+ // a security event) must be able to tell apart.
223
+ if (CONTROL_CHAR.test(input.depth) || CONTROL_CHAR.test(input.baseSha)
224
+ || CONTROL_CHAR.test(input.repoRoot) || CONTROL_CHAR.test(input.runDir)) {
225
+ return { dispatched: false, ok: false, reason: exports.DISPATCH_REASON.INVALID_PROVENANCE, selection, results: [] };
226
+ }
227
+ // #4209 WR-03 (considered, declined): gating `depth` to code-review's quick/standard/deep
228
+ // enum here would reject the deliberately capability-neutral case this function supports —
229
+ // see "a second, unrelated synthetic step context dispatches through the same function
230
+ // identically" below, which passes a wholly different depth vocabulary on purpose to prove
231
+ // this dispatcher has no code-review-specific special-casing. `depthMeaning()`'s `standard`
232
+ // fallback for an off-enum value is accepted, not a bug, for that reason.
233
+ const { configGet, getLane, plan } = deps;
234
+ const writePromptFile = deps.writePromptFile ?? defaultWritePromptFile;
235
+ const prompt = buildSourceReviewPrompt(input);
236
+ const estimatedTokens = (0, prompt_budget_cjs_1.estimateTokens)(prompt);
237
+ // Written once, before any lane's plan() runs: `promptPath` is derived from `runDir` alone
238
+ // (see `artifactPaths`), constant across every lane in this dispatch by construction — there
239
+ // is no per-lane variance to defend against, so writing it per-lane (as an earlier version of
240
+ // this function did) was pure redundancy, not a real safeguard.
241
+ // A hoisted, whole-dispatch write (see the doc comment above) that throws must not escape as
242
+ // an uncaught exception — no lane can succeed anyway if the shared prompt file was never
243
+ // written, so this is a dispatch-level halt like `validatePaths`/`MISSING_PROVENANCE` above,
244
+ // not a per-lane failure.
245
+ try {
246
+ writePromptFile((0, review_lane_invocation_cjs_1.artifactPaths)(input.runDir, '').promptPath, prompt);
247
+ }
248
+ catch {
249
+ return { dispatched: false, ok: false, reason: exports.DISPATCH_REASON.PROMPT_WRITE_FAILED, selection, results: [] };
250
+ }
251
+ const results = [];
252
+ // Never narrow the requested set: an explicit reviewer the selector could not resolve is
253
+ // already surfaced in `selection.errors` — reflect that in the aggregate `ok` even though
254
+ // lanes that DID resolve still run below and keep their own results.
255
+ let anyFailed = selection.errors.length > 0;
256
+ // Tracks whether any lane actually reached plan() — `dispatched` must stay false when every
257
+ // selected slug turned out to be unresolvable, even though a `results` entry was still pushed.
258
+ let planned = false;
259
+ for (const slug of selection.selected) {
260
+ const lane = getLane(slug);
261
+ if (!lane) {
262
+ results.push({ slug, ok: false, reason: 'malformed_lane', detail: 'no such declared lane' });
263
+ anyFailed = true;
264
+ continue;
265
+ }
266
+ // #4209 review: a `promptChannel: 'none'` lane (coderabbit) is fed nothing and reviews
267
+ // whatever it independently sees fit (its own working-tree diff, review.md:367) rather than
268
+ // the bounded `paths`/`depth`/`baseSha` scope `buildSourceReviewPrompt` promises — silently
269
+ // dispatching it here would violate this interpreter's own scoped, metadata-only review
270
+ // contract. Reject before plan()/invoke() rather than let the mismatch surface as an
271
+ // unexplained out-of-scope review.
272
+ if (lane.transport === 'spawn' && lane.invoke.promptChannel === 'none') {
273
+ results.push({
274
+ slug,
275
+ ok: false,
276
+ reason: 'prompt_channel_unsupported',
277
+ detail: `lane '${slug}' declares promptChannel 'none' and cannot receive a scoped source-review prompt`,
278
+ });
279
+ anyFailed = true;
280
+ continue;
281
+ }
282
+ // A single throwing plan()/invoke() must not take down every sibling lane already collected
283
+ // in `results` — same rationale as gsd-tools.cjs's resolveLanePlan guard
284
+ // (#2494/#2605/#1698/#1936/#2073/#2176/#2589/#2794): belt and braces on purpose.
285
+ let planOutcome;
286
+ try {
287
+ planOutcome = plan(lane, { configGet, runDir: input.runDir, repoRoot: input.repoRoot });
288
+ }
289
+ catch (e) {
290
+ results.push({ slug, ok: false, reason: 'malformed_lane', detail: e instanceof Error ? e.message : String(e) });
291
+ anyFailed = true;
292
+ continue;
293
+ }
294
+ if (!planOutcome.ok) {
295
+ results.push({ slug, ok: false, reason: planOutcome.reason, detail: planOutcome.detail });
296
+ anyFailed = true;
297
+ continue;
298
+ }
299
+ const budget = (0, review_lane_invocation_cjs_1.resolveLaneBudget)(lane, configGet);
300
+ if (budget !== null && budget !== 0 && estimatedTokens > budget) {
301
+ results.push({
302
+ slug,
303
+ ok: false,
304
+ reason: 'budget_exceeded',
305
+ detail: `estimated ${estimatedTokens} tokens exceeds resolved budget ${budget} for lane '${slug}'`,
306
+ });
307
+ anyFailed = true;
308
+ continue;
309
+ }
310
+ planned = true;
311
+ let invokeOutcome;
312
+ try {
313
+ invokeOutcome = await deps.invoke(lane, planOutcome.plan);
314
+ }
315
+ catch (e) {
316
+ results.push({ slug, ok: false, reason: 'invoke_failed', detail: e instanceof Error ? e.message : String(e) });
317
+ anyFailed = true;
318
+ continue;
319
+ }
320
+ if (!invokeOutcome.ok)
321
+ anyFailed = true;
322
+ results.push({
323
+ slug,
324
+ ok: invokeOutcome.ok,
325
+ reason: invokeOutcome.reason,
326
+ detail: invokeOutcome.detail,
327
+ reviewPath: invokeOutcome.reviewPath,
328
+ errPath: invokeOutcome.errPath,
329
+ });
330
+ }
331
+ return {
332
+ dispatched: planned,
333
+ ok: !anyFailed,
334
+ selection,
335
+ results,
336
+ };
337
+ }