@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
@@ -35,6 +35,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
35
35
  const node_path_1 = __importDefault(require("node:path"));
36
36
  const node_fs_1 = __importDefault(require("node:fs"));
37
37
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
38
+ const security_cjs_1 = require("./security.cjs");
38
39
  // eslint-disable-next-line @typescript-eslint/no-require-imports
39
40
  const io = require("./io.cjs");
40
41
  // eslint-disable-next-line @typescript-eslint/no-require-imports
@@ -174,8 +175,11 @@ function resolvePhaseDirForArg(cwd, phaseArg) {
174
175
  */
175
176
  function resolveConfinedPath(cwd, relFile) {
176
177
  const root = node_path_1.default.resolve(cwd);
177
- const resolved = node_path_1.default.resolve(root, relFile);
178
- if (resolved !== root && !resolved.startsWith(root + node_path_1.default.sep))
178
+ // ADR-4650 decision 6: lexical family — resolving the symlink here would
179
+ // undo the refuse-don't-resolve posture documented above; `lstatSync` below
180
+ // is the gate that actually refuses a symlink.
181
+ const resolved = (0, security_cjs_1.tryWithinRootLexical)(relFile, root);
182
+ if (resolved === null)
179
183
  return null;
180
184
  try {
181
185
  if (!node_fs_1.default.lstatSync(resolved).isFile())
@@ -321,6 +325,32 @@ function loadWindowsOrDegrade(cwd, windowsOverride) {
321
325
  }
322
326
  return { ok: true, windows, ledger };
323
327
  }
328
+ /**
329
+ * #3780: lazy (module-load cost, see file header) require of the ledger's
330
+ * serialization seam — the same `.planning/.WINDOWS.lock` the windows cmd*
331
+ * writers hold, so this router's own read-compute-write cycles on
332
+ * WINDOWS.md cannot lose updates against them. Required from the REAL
333
+ * compiled module, never through the injectable `windowsOverride` seam:
334
+ * the lock is infrastructure, not a parser stand-in.
335
+ */
336
+ function withLedgerLock(cwd, fn) {
337
+ let lockMod;
338
+ try {
339
+ /* eslint-disable @typescript-eslint/no-require-imports */
340
+ lockMod = require('./broken-windows.cjs');
341
+ /* eslint-enable @typescript-eslint/no-require-imports */
342
+ }
343
+ catch {
344
+ // Ledger module unavailable (the #1953 degrade world — see the row-86
345
+ // notesLedgerUnavailableWithoutBrokenWindows contract): no lock-holding
346
+ // writer can exist either, because every WINDOWS.md writer requires this
347
+ // same module. Run the body unlocked and let loadWindowsOrDegrade
348
+ // produce the canonical degrade note — wrapping that world in lock
349
+ // ceremony would only rewrite the note the degrade contract pins.
350
+ return fn();
351
+ }
352
+ return lockMod.withLedgerLock(cwd, fn);
353
+ }
324
354
  /**
325
355
  * Strict-mode window append (step 9 of `evaluate`). Degrades to
326
356
  * `{ recorded: false, note }` per `loadWindowsOrDegrade` — never an error,
@@ -329,6 +359,23 @@ function loadWindowsOrDegrade(cwd, windowsOverride) {
329
359
  * file/line) and does not append a second one.
330
360
  */
331
361
  function recordStrictWindow(cwd, padded, target, windowsOverride) {
362
+ // #3780: hold the same cross-process ledger lock the windows cmd* writers
363
+ // hold — this site's read-compute-write on WINDOWS.md is otherwise the
364
+ // same lost-update race: a concurrent `gsd_run windows append` (or another
365
+ // evaluator) could silently overwrite this entry, or be overwritten by
366
+ // it. The lock comes from the real ledger module, not the injectable
367
+ // `windowsOverride` seam — it is infrastructure, not a parser stand-in.
368
+ // The wrapper keeps the #1953-defect-2 degrade contract: the lock's typed
369
+ // refusal degrades to `{ recorded: false, note }` like every other
370
+ // failure, it never throws out of this function.
371
+ try {
372
+ return withLedgerLock(cwd, () => recordStrictWindowLocked(cwd, padded, target, windowsOverride));
373
+ }
374
+ catch (e) {
375
+ return { recorded: false, note: `failed to record broken-windows entry: ${e instanceof Error ? e.message : String(e)}` };
376
+ }
377
+ }
378
+ function recordStrictWindowLocked(cwd, padded, target, windowsOverride) {
332
379
  const loaded = loadWindowsOrDegrade(cwd, windowsOverride);
333
380
  if (!loaded.ok)
334
381
  return { recorded: false, note: loaded.note };
@@ -357,6 +404,18 @@ function recordStrictWindow(cwd, padded, target, windowsOverride) {
357
404
  * `accept`/`decline` never tell a user "it failed" without saying why.
358
405
  */
359
406
  function resolveLedgerWindow(cwd, padded, file, line, kind, reasonText, windowsOverride) {
407
+ // #3780: same serialization as recordStrictWindow — a concurrent ledger
408
+ // writer holding a stale snapshot would silently revert this resolve (or
409
+ // lose its own write to this one). Same degrade contract: the lock's
410
+ // typed refusal degrades, never throws.
411
+ try {
412
+ return withLedgerLock(cwd, () => resolveLedgerWindowLocked(cwd, padded, file, line, kind, reasonText, windowsOverride));
413
+ }
414
+ catch (e) {
415
+ return { resolved: false, note: `failed to resolve broken-windows entry: ${e instanceof Error ? e.message : String(e)}` };
416
+ }
417
+ }
418
+ function resolveLedgerWindowLocked(cwd, padded, file, line, kind, reasonText, windowsOverride) {
360
419
  const loaded = loadWindowsOrDegrade(cwd, windowsOverride);
361
420
  if (!loaded.ok)
362
421
  return { resolved: false, note: loaded.note };
@@ -15,6 +15,10 @@ const node_os_1 = __importDefault(require("node:os"));
15
15
  const node_path_1 = __importDefault(require("node:path"));
16
16
  const node_crypto_1 = __importDefault(require("node:crypto"));
17
17
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
18
+ // ADR-4650 decision 6: lexical family — the key is validated before
19
+ // `fs.mkdirSync` creates the store dir, so the candidate legitimately does
20
+ // not exist yet at check time.
21
+ const security_cjs_1 = require("./security.cjs");
18
22
  // ---------------------------------------------------------------------------
19
23
  // Constants
20
24
  // ---------------------------------------------------------------------------
@@ -94,15 +98,12 @@ function putResearch(cwd, key, payload, { clock = Date, homeDir = node_os_1.defa
94
98
  const fetched_at = new Date(clock.now()).toISOString();
95
99
  const entry = { content, source, provider, confidence, fetched_at, ttl, kind };
96
100
  const dir = resolveStorePath(cwd, source, { homeDir });
97
- // Belt-and-suspenders: ensure the resolved file path stays inside the store dir.
98
- const resolvedDir = node_path_1.default.resolve(dir);
99
- const filePath = node_path_1.default.join(dir, `${key}.json`);
100
- const resolvedFile = node_path_1.default.resolve(filePath);
101
- if (!resolvedFile.startsWith(resolvedDir + node_path_1.default.sep)) {
101
+ const containedFile = (0, security_cjs_1.tryWithinRootLexical)(`${key}.json`, dir);
102
+ if (containedFile === null || containedFile === node_path_1.default.resolve(dir)) {
102
103
  throw new Error('invalid research key');
103
104
  }
104
105
  node_fs_1.default.mkdirSync(dir, { recursive: true });
105
- (0, shell_command_projection_cjs_1.platformWriteSync)(filePath, JSON.stringify(entry));
106
+ (0, shell_command_projection_cjs_1.platformWriteSync)(containedFile, JSON.stringify(entry));
106
107
  return entry;
107
108
  }
108
109
  // ---------------------------------------------------------------------------
@@ -120,16 +121,14 @@ function getResearch(cwd, key, { clock = Date, homeDir = node_os_1.default.homed
120
121
  const tierDirs = [userDir, projectDir];
121
122
  const candidates = [];
122
123
  for (const dir of tierDirs) {
123
- const resolvedDir = node_path_1.default.resolve(dir);
124
- const filePath = node_path_1.default.join(dir, `${key}.json`);
125
- // Belt-and-suspenders: ensure path stays inside tier dir
126
- if (!node_path_1.default.resolve(filePath).startsWith(resolvedDir + node_path_1.default.sep))
124
+ const containedFile = (0, security_cjs_1.tryWithinRootLexical)(`${key}.json`, dir);
125
+ if (containedFile === null || containedFile === node_path_1.default.resolve(dir))
127
126
  continue;
128
- if (!node_fs_1.default.existsSync(filePath))
127
+ if (!node_fs_1.default.existsSync(containedFile))
129
128
  continue;
130
129
  let entry;
131
130
  try {
132
- entry = JSON.parse(node_fs_1.default.readFileSync(filePath, 'utf8'));
131
+ entry = JSON.parse(node_fs_1.default.readFileSync(containedFile, 'utf8'));
133
132
  }
134
133
  catch {
135
134
  // Corrupt file in this tier — skip it
@@ -45,6 +45,9 @@
45
45
  * 4. `flags: string[]` — Antigravity is selected by BOTH `--antigravity` and
46
46
  * `--agy`, which a single-valued field cannot express. This also flattens
47
47
  * D8's uniqueness invariant across every lane's flags.
48
+ * 5. `NATIVE_TIMEOUT` — a lane whose CLI takes its own native inner timeout flag (today only
49
+ * antigravity's `--print-timeout`) declares where the resolved value goes; `resolveLanePlan`
50
+ * computes what it is from the same resolved outer `timeoutMs` (#3274).
48
51
  *
49
52
  * Phase 2 (#2795) implements the manifest validator against the amended
50
53
  * vocabulary, which is the point of amending rather than leaving it to be
@@ -69,7 +72,7 @@ exports.checkReviewerDocsParity = checkReviewerDocsParity;
69
72
  * and vanishes when it has nothing to contribute (no model configured, no effort channel, prompt on
70
73
  * stdin), which is what lets one template serve the configured and unconfigured cases.
71
74
  *
72
- * This is a closed four-member vocabulary with no expressions, no nesting and no conditionals — a
75
+ * This is a closed five-member vocabulary with no expressions, no nesting and no conditionals — a
73
76
  * placeholder set, deliberately not a template language. The moment it needs a conditional, the
74
77
  * lane wants a `handler` instead (D6).
75
78
  */
@@ -82,6 +85,9 @@ exports.ARGV_PLACEHOLDER = Object.freeze({
82
85
  OUTPUT: '{{output}}',
83
86
  /** The argv-borne prompt, or nothing unless `promptChannel` is `argv`/`argv-file-ref`. */
84
87
  PROMPT: '{{prompt}}',
88
+ /** A lane's own CLI-native inner timeout duration, derived from the resolved outer `timeoutMs`
89
+ * (never independently configured) — see `resolveLanePlan`'s expansion of this token. */
90
+ NATIVE_TIMEOUT: '{{nativeTimeout}}',
85
91
  });
86
92
  const SPAWN_STDIN_STDOUT = {
87
93
  promptChannel: 'stdin',
@@ -107,12 +113,15 @@ exports.REVIEWER_LANES = Object.freeze([
107
113
  effortChannel: 'none',
108
114
  },
109
115
  timeoutFloorMs: 900_000,
116
+ timeoutConfigKey: 'review.timeouts.gemini',
110
117
  emptyOutput: 'stub-with-stderr',
111
118
  reviewsSection: 'Gemini',
112
119
  evidenceClass: 'source-grounded',
113
120
  requiresBinaries: [],
114
121
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.gemini',
115
122
  modelConfigKey: 'review.models.gemini',
123
+ effortConfigKey: null,
124
+ defaultEffort: null,
116
125
  handler: null,
117
126
  },
118
127
  {
@@ -139,12 +148,15 @@ exports.REVIEWER_LANES = Object.freeze([
139
148
  env: { CLAUDE_CODE_DISABLE_CLAUDE_MDS: '1', CLAUDE_CODE_DISABLE_AUTO_MEMORY: '1' },
140
149
  },
141
150
  timeoutFloorMs: 1_200_000,
151
+ timeoutConfigKey: 'review.timeouts.claude',
142
152
  emptyOutput: 'stub-with-stderr',
143
153
  reviewsSection: 'Claude',
144
154
  evidenceClass: 'source-grounded',
145
155
  requiresBinaries: [],
146
156
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.claude',
147
157
  modelConfigKey: 'review.models.claude',
158
+ effortConfigKey: 'review.effort.claude',
159
+ defaultEffort: 'high',
148
160
  handler: null,
149
161
  },
150
162
  {
@@ -167,12 +179,15 @@ exports.REVIEWER_LANES = Object.freeze([
167
179
  effortChannel: 'argv',
168
180
  },
169
181
  timeoutFloorMs: 1_200_000,
182
+ timeoutConfigKey: 'review.timeouts.codex',
170
183
  emptyOutput: 'stub-with-stderr',
171
184
  reviewsSection: 'Codex',
172
185
  evidenceClass: 'source-grounded',
173
186
  requiresBinaries: [],
174
187
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.codex',
175
188
  modelConfigKey: 'review.models.codex',
189
+ effortConfigKey: 'review.effort.codex',
190
+ defaultEffort: 'high',
176
191
  handler: null,
177
192
  },
178
193
  {
@@ -192,6 +207,7 @@ exports.REVIEWER_LANES = Object.freeze([
192
207
  effortChannel: 'none',
193
208
  },
194
209
  timeoutFloorMs: 360_000,
210
+ timeoutConfigKey: null,
195
211
  emptyOutput: 'stub-with-stderr',
196
212
  reviewsSection: 'CodeRabbit',
197
213
  evidenceClass: 'diff-only',
@@ -199,6 +215,8 @@ exports.REVIEWER_LANES = Object.freeze([
199
215
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.coderabbit',
200
216
  // Accepts no model flag at all (review.md:367) — not merely "none configured".
201
217
  modelConfigKey: null,
218
+ effortConfigKey: null,
219
+ defaultEffort: null,
202
220
  handler: null,
203
221
  },
204
222
  {
@@ -217,6 +235,7 @@ exports.REVIEWER_LANES = Object.freeze([
217
235
  effortChannel: 'argv',
218
236
  },
219
237
  timeoutFloorMs: 660_000,
238
+ timeoutConfigKey: 'review.timeouts.opencode',
220
239
  emptyOutput: 'stub-with-stderr',
221
240
  reviewsSection: 'OpenCode',
222
241
  evidenceClass: 'source-grounded',
@@ -225,6 +244,8 @@ exports.REVIEWER_LANES = Object.freeze([
225
244
  requiresBinaries: [],
226
245
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.opencode',
227
246
  modelConfigKey: 'review.models.opencode',
247
+ effortConfigKey: 'review.effort.opencode',
248
+ defaultEffort: 'high',
228
249
  // Phase 5b (#2799): was `null`. The review is REBUILT from assistant `text` parts; a plain
229
250
  // stdout copy would write the raw JSON envelope as the review (#1936). See LaneHandler.
230
251
  handler: 'opencode',
@@ -242,12 +263,15 @@ exports.REVIEWER_LANES = Object.freeze([
242
263
  effortChannel: 'none',
243
264
  },
244
265
  timeoutFloorMs: 900_000,
266
+ timeoutConfigKey: null,
245
267
  emptyOutput: 'stub-with-stderr',
246
268
  reviewsSection: 'Qwen',
247
269
  evidenceClass: 'source-grounded',
248
270
  requiresBinaries: [],
249
271
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.qwen',
250
272
  modelConfigKey: null,
273
+ effortConfigKey: null,
274
+ defaultEffort: null,
251
275
  handler: null,
252
276
  },
253
277
  {
@@ -260,19 +284,23 @@ exports.REVIEWER_LANES = Object.freeze([
260
284
  probe: { kind: 'command-exists', binary: 'cursor-agent' },
261
285
  invoke: {
262
286
  binary: 'cursor-agent',
263
- args: ['-p', '--mode', 'ask', '--trust', '--output-format', 'text', '{{prompt}}'],
287
+ args: ['-p', '{{model}}', '--mode', 'ask', '--trust', '--output-format', 'text', '{{prompt}}'],
264
288
  promptChannel: 'argv-file-ref',
265
289
  outputChannel: 'stdout',
266
- modelArg: null,
290
+ modelArg: '--model',
267
291
  effortChannel: 'none',
268
292
  },
269
293
  timeoutFloorMs: 900_000,
294
+ timeoutConfigKey: null,
270
295
  emptyOutput: 'stub-with-stderr',
271
296
  reviewsSection: 'Cursor',
272
297
  evidenceClass: 'source-grounded',
273
298
  requiresBinaries: [],
274
299
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.cursor',
275
- modelConfigKey: null,
300
+ // #3653: cursor-agent exposes --model (204 selectable models); wired the same as codex.
301
+ modelConfigKey: 'review.models.cursor',
302
+ effortConfigKey: null,
303
+ defaultEffort: null,
276
304
  handler: null,
277
305
  },
278
306
  {
@@ -286,13 +314,19 @@ exports.REVIEWER_LANES = Object.freeze([
286
314
  probe: { kind: 'command-exists', binary: 'agy' },
287
315
  invoke: {
288
316
  binary: 'agy',
289
- args: ['--print-timeout', '540s', '{{model}}', '-p', '{{prompt}}'],
317
+ // `{{nativeTimeout}}` is the fifth ARGV_PLACEHOLDER member (#3274) — `resolveLanePlan`
318
+ // (review-lane-invocation.cts) expands it to a value DERIVED from this same lane's resolved
319
+ // outer `timeoutMs`, so the native `--print-timeout` and the outer wall-clock cap can never
320
+ // drift apart. No other shipped lane's `args` template contains this token, so the expansion
321
+ // is inert everywhere else.
322
+ args: ['--print-timeout', '{{nativeTimeout}}', '{{model}}', '-p', '{{prompt}}'],
290
323
  promptChannel: 'argv-file-ref',
291
324
  outputChannel: 'stdout',
292
325
  modelArg: '--model',
293
326
  effortChannel: 'none',
294
327
  },
295
328
  timeoutFloorMs: 600_000,
329
+ timeoutConfigKey: 'review.timeouts.antigravity',
296
330
  emptyOutput: 'handler-owned',
297
331
  reviewsSection: 'Antigravity',
298
332
  evidenceClass: 'source-grounded',
@@ -302,6 +336,8 @@ exports.REVIEWER_LANES = Object.freeze([
302
336
  // NOT `review.models.antigravity` — the shipped key is `review.models.agy` (review.md:291) and
303
337
  // Phase 4 federated it under that name. This lane is why the key is declared, not derived.
304
338
  modelConfigKey: 'review.models.agy',
339
+ effortConfigKey: null,
340
+ defaultEffort: null,
305
341
  handler: 'antigravity',
306
342
  },
307
343
  {
@@ -323,6 +359,7 @@ exports.REVIEWER_LANES = Object.freeze([
323
359
  effortChannel: 'none',
324
360
  },
325
361
  timeoutFloorMs: 120_000,
362
+ timeoutConfigKey: 'review.timeouts.ollama',
326
363
  emptyOutput: 'stub-with-stderr',
327
364
  reviewsSection: 'Ollama',
328
365
  evidenceClass: 'source-grounded',
@@ -331,6 +368,8 @@ exports.REVIEWER_LANES = Object.freeze([
331
368
  requiresBinaries: [],
332
369
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.ollama',
333
370
  modelConfigKey: 'review.models.ollama',
371
+ effortConfigKey: null,
372
+ defaultEffort: null,
334
373
  handler: 'openai-compatible',
335
374
  },
336
375
  {
@@ -352,12 +391,15 @@ exports.REVIEWER_LANES = Object.freeze([
352
391
  effortChannel: 'none',
353
392
  },
354
393
  timeoutFloorMs: 120_000,
394
+ timeoutConfigKey: 'review.timeouts.lm_studio',
355
395
  emptyOutput: 'stub-with-stderr',
356
396
  reviewsSection: 'LM Studio',
357
397
  evidenceClass: 'source-grounded',
358
398
  requiresBinaries: [],
359
399
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.lm_studio',
360
400
  modelConfigKey: 'review.models.lm_studio',
401
+ effortConfigKey: null,
402
+ defaultEffort: null,
361
403
  handler: 'openai-compatible',
362
404
  },
363
405
  {
@@ -379,12 +421,15 @@ exports.REVIEWER_LANES = Object.freeze([
379
421
  effortChannel: 'none',
380
422
  },
381
423
  timeoutFloorMs: 120_000,
424
+ timeoutConfigKey: 'review.timeouts.llama_cpp',
382
425
  emptyOutput: 'stub-with-stderr',
383
426
  reviewsSection: 'llama.cpp',
384
427
  evidenceClass: 'source-grounded',
385
428
  requiresBinaries: [],
386
429
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.llama_cpp',
387
430
  modelConfigKey: 'review.models.llama_cpp',
431
+ effortConfigKey: null,
432
+ defaultEffort: null,
388
433
  handler: 'openai-compatible',
389
434
  },
390
435
  {
@@ -424,12 +469,15 @@ exports.REVIEWER_LANES = Object.freeze([
424
469
  effortChannel: 'none',
425
470
  },
426
471
  timeoutFloorMs: 900_000,
472
+ timeoutConfigKey: 'review.timeouts.kimi-code',
427
473
  emptyOutput: 'stub-with-stderr',
428
474
  reviewsSection: 'Kimi Code',
429
475
  evidenceClass: 'source-grounded',
430
476
  requiresBinaries: [],
431
477
  promptBudgetKey: 'review.max_prompt_tokens_per_reviewer.kimi-code',
432
478
  modelConfigKey: 'review.models.kimi-code',
479
+ effortConfigKey: null,
480
+ defaultEffort: null,
433
481
  handler: null,
434
482
  },
435
483
  ].map((lane) => Object.freeze(lane)));
@@ -26,8 +26,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.LANE_UNAVAILABLE = void 0;
27
27
  exports.configString = configString;
28
28
  exports.normalizeHost = normalizeHost;
29
+ exports.resolveLaneEffort = resolveLaneEffort;
30
+ exports.resolveTimeoutMs = resolveTimeoutMs;
31
+ exports.nativeTimeoutToken = nativeTimeoutToken;
29
32
  exports.isEmptyReview = isEmptyReview;
30
33
  exports.fileRefPrompt = fileRefPrompt;
34
+ exports.artifactPaths = artifactPaths;
35
+ exports.resolveLaneBudget = resolveLaneBudget;
31
36
  exports.resolveLanePlan = resolveLanePlan;
32
37
  const review_lane_descriptor_cjs_1 = require("./review-lane-descriptor.cjs");
33
38
  /* ------------------------------------------------------------------ *
@@ -122,6 +127,73 @@ function normalizeHost(raw) {
122
127
  const pathPart = u.pathname.replace(/\/+$/, '');
123
128
  return `${scheme}//${host}${port ? `:${port}` : ''}${pathPart}`;
124
129
  }
130
+ /** Levels GSD's effort axis accepts (#3533). `inherit` selects the no-argument path. */
131
+ const EFFORT_LEVELS = new Set([
132
+ 'minimal', 'low', 'medium', 'high', 'xhigh', 'max', 'inherit',
133
+ ]);
134
+ /**
135
+ * Resolve one lane's reasoning effort from REVIEW configuration (#4255).
136
+ *
137
+ * Resolution order, highest first:
138
+ * 1. `lane.effortConfigKey` — the per-lane review effort the operator set
139
+ * 2. `lane.defaultEffort` — the lane's declared review default (`high` for prompt-fed,
140
+ * source-grounded lanes)
141
+ * 3. nothing — no effort argument is emitted and the reviewer CLI's own configuration decides
142
+ *
143
+ * A configured `'inherit'` selects (3) explicitly. An unrecognized level is REFUSED rather than
144
+ * passed to the host: it falls back to the lane default, because forwarding a typo would render an
145
+ * argument the CLI rejects and kill the lane outright.
146
+ *
147
+ * What this function deliberately does NOT do is consult any agent's execution settings. Before
148
+ * #4255 the level came from `gsd-plan-checker`'s installed frontmatter through a hardcoded agent
149
+ * id, so every lane ran at a fast structural verifier's `low` — and, because the rendered argument
150
+ * is a CLI config override, it silently beat the effort the operator had configured for that CLI
151
+ * itself. A value inherited from an unrelated agent is worse than no value at all, which is why
152
+ * (3) emits nothing rather than falling back to some other agent's number.
153
+ *
154
+ * `renderArgv` is injected (the host table and the ADR-2481 surface negotiation live in
155
+ * `model-catalog` / `commands`, above this module's layer) so this stays a pure function of its
156
+ * inputs and the golden lane table can assert it without a spawn.
157
+ */
158
+ function resolveLaneEffort(lane, configGet, renderArgv) {
159
+ const none = { argv: [], value: null, source: 'none' };
160
+ if (!lane || typeof lane !== 'object')
161
+ return none;
162
+ const configured = lane.effortConfigKey ? configString(configGet(lane.effortConfigKey)) : null;
163
+ const valid = configured !== null && EFFORT_LEVELS.has(configured) ? configured : null;
164
+ const level = valid ?? configString(lane.defaultEffort);
165
+ if (level === null || level === 'inherit')
166
+ return none;
167
+ const rendered = renderArgv(lane.slug, level);
168
+ const argv = (rendered.argv ?? []).filter((a) => typeof a === 'string' && a !== '');
169
+ if (argv.length === 0)
170
+ return none;
171
+ return {
172
+ argv,
173
+ value: configString(rendered.value) ?? level,
174
+ source: valid !== null ? 'config' : 'lane-default',
175
+ };
176
+ }
177
+ function resolveTimeoutMs(timeoutConfigKey, floorMs, configGet) {
178
+ const configuredSeconds = typeof timeoutConfigKey === 'string' ? configGet(timeoutConfigKey) : undefined;
179
+ return typeof configuredSeconds === 'number' && Number.isFinite(configuredSeconds) && configuredSeconds > 0
180
+ ? configuredSeconds * 1000
181
+ : floorMs;
182
+ }
183
+ /** Buffer (seconds) a lane's native inner timeout sits under its resolved outer wall-clock cap
184
+ * (#3274). Matches the shipped 600s outer / 540s native relationship exactly when unconfigured:
185
+ * floor(600000/1000) - 60 = 540. */
186
+ const NATIVE_TIMEOUT_BUFFER_SECONDS = 60;
187
+ /**
188
+ * Render the `{{nativeTimeout}}` argv placeholder from a lane's resolved outer timeout (#3274).
189
+ *
190
+ * Clamped to a 1-second floor so a very small configured (or, today, only-ever-default) outer
191
+ * timeout never produces a zero or negative duration string a CLI would reject or misinterpret.
192
+ */
193
+ function nativeTimeoutToken(timeoutMs) {
194
+ const seconds = Math.max(1, Math.floor(timeoutMs / 1000) - NATIVE_TIMEOUT_BUFFER_SECONDS);
195
+ return `${seconds}s`;
196
+ }
125
197
  /**
126
198
  * Classify a lane's output as a review or as empty.
127
199
  *
@@ -166,6 +238,27 @@ function artifactPaths(runDir, slug) {
166
238
  errPath: `${base}/gsd-review-${slug}.err`,
167
239
  };
168
240
  }
241
+ /**
242
+ * Per-lane prompt budget (#2797 semantics, preserved exactly).
243
+ *
244
+ * `-1` is the UNSET sentinel and falls back to the central `review.max_prompt_tokens`, because
245
+ * `0` is a legitimate value meaning "do not trim this lane". Treating 0 as unset would silently
246
+ * switch a user who deliberately disabled trimming onto the global budget.
247
+ *
248
+ * Single source of truth: `gsd-core/bin/gsd-tools.cjs`'s `review-lane plan`/`invoke` and
249
+ * `src/reviewer-step-dispatch.cts`'s `dispatchReviewerLanes` both resolve a lane's budget through
250
+ * this function rather than each carrying their own copy (#4209 R3 — two verbatim copies drift).
251
+ */
252
+ function resolveLaneBudget(lane, configGet) {
253
+ if (!lane.promptBudgetKey)
254
+ return null;
255
+ const per = configGet(lane.promptBudgetKey);
256
+ const isNum = (v) => typeof v === 'number' && Number.isFinite(v);
257
+ if (isNum(per) && per !== -1)
258
+ return per;
259
+ const global = configGet('review.max_prompt_tokens');
260
+ return isNum(global) ? global : null;
261
+ }
169
262
  /* ------------------------------------------------------------------ *
170
263
  * Resolution
171
264
  * ------------------------------------------------------------------ */
@@ -212,9 +305,10 @@ function resolveLanePlan(input) {
212
305
  return fail(exports.LANE_UNAVAILABLE.UNKNOWN_HANDLER, `lane '${slug}' names handler '${String(handler)}', which this GSD version does not provide`);
213
306
  }
214
307
  const { promptPath, reviewPath, errPath } = artifactPaths(input.runDir, slug);
215
- const timeoutMs = typeof lane.timeoutFloorMs === 'number' && Number.isFinite(lane.timeoutFloorMs) && lane.timeoutFloorMs > 0
308
+ const floorMs = typeof lane.timeoutFloorMs === 'number' && Number.isFinite(lane.timeoutFloorMs) && lane.timeoutFloorMs > 0
216
309
  ? lane.timeoutFloorMs
217
310
  : 900_000;
311
+ const timeoutMs = resolveTimeoutMs(lane.timeoutConfigKey, floorMs, input.configGet);
218
312
  const emptyOutput = lane.emptyOutput === 'handler-owned' ? 'handler-owned' : 'stub-with-stderr';
219
313
  // #3194: only an EXACT 'diff-only' declaration exempts a lane from evidence verification.
220
314
  // Anything else — including a missing or garbage value on a third-party overlay body —
@@ -319,6 +413,7 @@ function resolveLanePlan(input) {
319
413
  '{{effort}}': effortExpansion,
320
414
  '{{output}}': outputExpansion,
321
415
  '{{prompt}}': promptExpansion,
416
+ '{{nativeTimeout}}': [nativeTimeoutToken(timeoutMs)],
322
417
  };
323
418
  const template = Array.isArray(inv.args)
324
419
  ? inv.args.filter((a) => typeof a === 'string')