@opengsd/gsd-core 1.11.0 → 1.13.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (498) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -1
  3. package/.opencode/plugins/gsd-core.js +12 -0
  4. package/agents/gsd-code-fixer.md +1 -1
  5. package/agents/gsd-debug-session-manager.md +1 -1
  6. package/agents/gsd-debugger.md +1 -1
  7. package/agents/gsd-dom-verifier.md +169 -0
  8. package/agents/gsd-eval-auditor.md +1 -1
  9. package/agents/gsd-executor.md +78 -42
  10. package/agents/gsd-framework-selector.md +1 -3
  11. package/agents/gsd-intel-updater.md +1 -1
  12. package/agents/gsd-mempalace-curator.md +0 -1
  13. package/agents/gsd-pattern-mapper.md +11 -0
  14. package/agents/gsd-phase-researcher.md +3 -1
  15. package/agents/gsd-plan-checker.md +91 -112
  16. package/agents/gsd-planner.md +20 -4
  17. package/agents/gsd-project-researcher.md +1 -1
  18. package/agents/gsd-research-synthesizer.md +2 -2
  19. package/agents/gsd-roadmapper.md +15 -11
  20. package/agents/gsd-ui-checker.md +82 -7
  21. package/agents/gsd-ui-researcher.md +70 -3
  22. package/agents/gsd-verifier.md +24 -2
  23. package/bin/install.js +847 -200
  24. package/commands/gsd/discuss-phase.md +1 -1
  25. package/commands/gsd/execute-phase.md +1 -1
  26. package/commands/gsd/import.md +1 -1
  27. package/commands/gsd/ns-workflow.md +2 -1
  28. package/commands/gsd/phase.md +1 -1
  29. package/commands/gsd/quick-batch.md +105 -0
  30. package/commands/gsd/quick.md +8 -4
  31. package/commands/gsd/surface.md +18 -8
  32. package/gsd-core/bin/gsd-tools.cjs +761 -100
  33. package/gsd-core/bin/lib/active-workstream-store.cjs +8 -0
  34. package/gsd-core/bin/lib/adr-parser.cjs +13 -7
  35. package/gsd-core/bin/lib/agent-install-check.cjs +162 -0
  36. package/gsd-core/bin/lib/api-coverage.cjs +30 -9
  37. package/gsd-core/bin/lib/artifacts.cjs +2 -0
  38. package/gsd-core/bin/lib/assumption-delta.cjs +30 -11
  39. package/gsd-core/bin/lib/audit.cjs +163 -41
  40. package/gsd-core/bin/lib/broken-windows.cjs +306 -28
  41. package/gsd-core/bin/lib/capability-activation.cjs +27 -0
  42. package/gsd-core/bin/lib/capability-lock.cjs +10 -4
  43. package/gsd-core/bin/lib/capability-registry.cjs +785 -144
  44. package/gsd-core/bin/lib/capability-state.cjs +25 -4
  45. package/gsd-core/bin/lib/capability-validator.cjs +321 -18
  46. package/gsd-core/bin/lib/capability-writer.cjs +14 -4
  47. package/gsd-core/bin/lib/check-command-router.cjs +229 -6
  48. package/gsd-core/bin/lib/claude-orchestration.cjs +10 -25
  49. package/gsd-core/bin/lib/cli-exit.cjs +496 -10
  50. package/gsd-core/bin/lib/clusters.cjs +1 -0
  51. package/gsd-core/bin/lib/code-review-depth.cjs +288 -0
  52. package/gsd-core/bin/lib/codex-agent-toml.cjs +410 -4
  53. package/gsd-core/bin/lib/command-aliases.cjs +16 -0
  54. package/gsd-core/bin/lib/command-arg-projection.cjs +144 -14
  55. package/gsd-core/bin/lib/command-routing-hub.cjs +31 -2
  56. package/gsd-core/bin/lib/commands.cjs +877 -54
  57. package/gsd-core/bin/lib/complexity-trigger.cjs +26 -6
  58. package/gsd-core/bin/lib/config-loader.cjs +121 -29
  59. package/gsd-core/bin/lib/config.cjs +92 -2
  60. package/gsd-core/bin/lib/configuration.cjs +129 -37
  61. package/gsd-core/bin/lib/core-utils.cjs +118 -14
  62. package/gsd-core/bin/lib/decisions.cjs +213 -1
  63. package/gsd-core/bin/lib/edge-probe.cjs +23 -2
  64. package/gsd-core/bin/lib/estimate-cli.cjs +55 -11
  65. package/gsd-core/bin/lib/exit-code-registry.cjs +98 -0
  66. package/gsd-core/bin/lib/file-overlap-partitioner.cjs +74 -0
  67. package/gsd-core/bin/lib/frontmatter.cjs +975 -326
  68. package/gsd-core/bin/lib/gap-checker.cjs +41 -8
  69. package/gsd-core/bin/lib/git-base-branch.cjs +182 -39
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +7 -3
  71. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +8 -2
  72. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +60 -14
  73. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +75 -22
  74. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +22 -8
  75. package/gsd-core/bin/lib/health-diagnostic.cjs +23 -3
  76. package/gsd-core/bin/lib/host-integration.cjs +96 -11
  77. package/gsd-core/bin/lib/init-command-router.cjs +132 -21
  78. package/gsd-core/bin/lib/init.cjs +252 -56
  79. package/gsd-core/bin/lib/install-engine.cjs +252 -15
  80. package/gsd-core/bin/lib/install-model-override-resolver.cjs +78 -1
  81. package/gsd-core/bin/lib/install-profiles.cjs +100 -18
  82. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  83. package/gsd-core/bin/lib/installer-migrations/010-antigravity-retire-confighome-artifacts.cjs +169 -0
  84. package/gsd-core/bin/lib/installer-migrations.cjs +10 -7
  85. package/gsd-core/bin/lib/intel.cjs +101 -26
  86. package/gsd-core/bin/lib/io.cjs +195 -15
  87. package/gsd-core/bin/lib/learnings.cjs +85 -14
  88. package/gsd-core/bin/lib/legacy-cleanup.cjs +8 -2
  89. package/gsd-core/bin/lib/loop-resolver.cjs +14 -8
  90. package/gsd-core/bin/lib/markdown-table.cjs +175 -4
  91. package/gsd-core/bin/lib/milestone.cjs +112 -7
  92. package/gsd-core/bin/lib/model-catalog.cjs +177 -19
  93. package/gsd-core/bin/lib/model-resolver.cjs +10 -28
  94. package/gsd-core/bin/lib/onboard-projection.cjs +5 -1
  95. package/gsd-core/bin/lib/phase-command-router.cjs +13 -6
  96. package/gsd-core/bin/lib/phase-estimation.cjs +17 -8
  97. package/gsd-core/bin/lib/phase-id.cjs +321 -13
  98. package/gsd-core/bin/lib/phase-lifecycle.cjs +24 -16
  99. package/gsd-core/bin/lib/phase-locator.cjs +138 -17
  100. package/gsd-core/bin/lib/phase.cjs +1175 -115
  101. package/gsd-core/bin/lib/plan-document.cjs +273 -0
  102. package/gsd-core/bin/lib/plan-scan.cjs +13 -2
  103. package/gsd-core/bin/lib/planning-command-router.cjs +61 -0
  104. package/gsd-core/bin/lib/planning-inspect.cjs +1168 -0
  105. package/gsd-core/bin/lib/planning-snapshot.cjs +165 -34
  106. package/gsd-core/bin/lib/planning-workspace.cjs +159 -28
  107. package/gsd-core/bin/lib/probe-core.cjs +4 -1
  108. package/gsd-core/bin/lib/profile-pipeline-command-router.cjs +50 -7
  109. package/gsd-core/bin/lib/profile-pipeline.cjs +6 -3
  110. package/gsd-core/bin/lib/quick-batch-command-router.cjs +285 -0
  111. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +250 -0
  112. package/gsd-core/bin/lib/quick-batch.cjs +840 -0
  113. package/gsd-core/bin/lib/real-home-guard.cjs +419 -0
  114. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +71 -45
  115. package/gsd-core/bin/lib/review-lane-descriptor.cjs +62 -14
  116. package/gsd-core/bin/lib/review-lane-invocation.cjs +73 -1
  117. package/gsd-core/bin/lib/review-lane-runner.cjs +136 -10
  118. package/gsd-core/bin/lib/roadmap-command-router.cjs +45 -31
  119. package/gsd-core/bin/lib/roadmap-parser.cjs +577 -41
  120. package/gsd-core/bin/lib/roadmap.cjs +248 -64
  121. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +329 -41
  122. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +16 -17
  123. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +320 -109
  124. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +487 -83
  125. package/gsd-core/bin/lib/runtime-identity.cjs +234 -0
  126. package/gsd-core/bin/lib/runtime-slash.cjs +72 -2
  127. package/gsd-core/bin/lib/shell-command-projection.cjs +75 -8
  128. package/gsd-core/bin/lib/smart-entry.cjs +19 -31
  129. package/gsd-core/bin/lib/spec-section.cjs +12 -7
  130. package/gsd-core/bin/lib/state-command-router.cjs +47 -18
  131. package/gsd-core/bin/lib/state-contract.cjs +359 -0
  132. package/gsd-core/bin/lib/state-document.cjs +216 -5
  133. package/gsd-core/bin/lib/state-md-schema.cjs +231 -0
  134. package/gsd-core/bin/lib/state-transition.cjs +850 -145
  135. package/gsd-core/bin/lib/state.cjs +1629 -287
  136. package/gsd-core/bin/lib/surface.cjs +33 -10
  137. package/gsd-core/bin/lib/task-command-router.cjs +111 -1
  138. package/gsd-core/bin/lib/task-content-resolution.cjs +368 -0
  139. package/gsd-core/bin/lib/tdd-red-evidence.cjs +133 -0
  140. package/gsd-core/bin/lib/teams-status.cjs +4 -1
  141. package/gsd-core/bin/lib/uat-predicate.cjs +58 -20
  142. package/gsd-core/bin/lib/uat.cjs +2542 -387
  143. package/gsd-core/bin/lib/ui-consideration-probe.cjs +9 -1
  144. package/gsd-core/bin/lib/ui-safety-gate.cjs +37 -7
  145. package/gsd-core/bin/lib/unusable-input.cjs +13 -0
  146. package/gsd-core/bin/lib/update-context.cjs +6 -2
  147. package/gsd-core/bin/lib/validate-command-router.cjs +2 -2
  148. package/gsd-core/bin/lib/validate.cjs +230 -12
  149. package/gsd-core/bin/lib/vendor/README.md +43 -5
  150. package/gsd-core/bin/lib/vendor/js-yaml.cjs +3014 -0
  151. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  152. package/gsd-core/bin/lib/verification.cjs +287 -13
  153. package/gsd-core/bin/lib/verify-command-grounding.cjs +846 -0
  154. package/gsd-core/bin/lib/verify-command-router.cjs +1 -0
  155. package/gsd-core/bin/lib/verify.cjs +441 -56
  156. package/gsd-core/bin/lib/workstream-inventory.cjs +20 -2
  157. package/gsd-core/bin/lib/workstream-name-policy.cjs +25 -4
  158. package/gsd-core/bin/lib/worktree-base-ref.cjs +66 -12
  159. package/gsd-core/bin/lib/worktree-safety.cjs +185 -21
  160. package/gsd-core/bin/shared/config-defaults.manifest.json +7 -1
  161. package/gsd-core/bin/shared/config-schema.manifest.json +13 -0
  162. package/gsd-core/bin/shared/exit-codes.json +8 -0
  163. package/gsd-core/bin/shared/exit-codes.sh +20 -0
  164. package/gsd-core/bin/shared/model-catalog.json +8 -1
  165. package/gsd-core/bin/verify-reapply-patches.cjs +70 -3
  166. package/gsd-core/references/agent-contracts.md +6 -5
  167. package/gsd-core/references/api-coverage.md +24 -2
  168. package/gsd-core/references/autonomous-smart-discuss.md +3 -3
  169. package/gsd-core/references/checkpoints.md +37 -19
  170. package/gsd-core/references/decimal-phase-calculation.md +5 -5
  171. package/gsd-core/references/edge-probe.md +17 -5
  172. package/gsd-core/references/execute-mvp-tdd.md +18 -18
  173. package/gsd-core/references/execute-phase-between-wave-reset.md +9 -12
  174. package/gsd-core/references/execute-phase-response-language.md +6 -0
  175. package/gsd-core/references/execute-phase-wave-guard.md +11 -9
  176. package/gsd-core/references/executor-examples.md +42 -0
  177. package/gsd-core/references/failing-direction.md +78 -0
  178. package/gsd-core/references/few-shot-examples/plan-checker.md +15 -15
  179. package/gsd-core/references/gate-prompts.md +1 -1
  180. package/gsd-core/references/git-integration.md +5 -5
  181. package/gsd-core/references/git-planning-commit.md +3 -3
  182. package/gsd-core/references/gsd-run-resolver.md +1 -1
  183. package/gsd-core/references/loop-hook-dispatch.md +22 -0
  184. package/gsd-core/references/model-profiles.md +1 -1
  185. package/gsd-core/references/mvp-concepts.md +2 -2
  186. package/gsd-core/references/nyquist-compliance.md +74 -0
  187. package/gsd-core/references/offer-next.md +3 -5
  188. package/gsd-core/references/phase-argument-parsing.md +3 -3
  189. package/gsd-core/references/plan-checker-examples.md +41 -0
  190. package/gsd-core/references/planner-antipatterns.md +25 -0
  191. package/gsd-core/references/planner-chunked.md +5 -1
  192. package/gsd-core/references/planner-coupling.md +42 -0
  193. package/gsd-core/references/planner-failing-direction.md +53 -0
  194. package/gsd-core/references/planner-human-verify-mode.md +15 -1
  195. package/gsd-core/references/planner-quick-batch.md +71 -0
  196. package/gsd-core/references/planner-reviews.md +47 -0
  197. package/gsd-core/references/planner-revision.md +76 -3
  198. package/gsd-core/references/planner-verify-command-grounding.md +17 -0
  199. package/gsd-core/references/planning-config.md +39 -9
  200. package/gsd-core/references/response-language-directive.md +9 -0
  201. package/gsd-core/references/reviewer-instances.md +31 -0
  202. package/gsd-core/references/revision-loop.md +118 -11
  203. package/gsd-core/references/runtime-aware-dispatch.md +1 -1
  204. package/gsd-core/references/tdd.md +15 -12
  205. package/gsd-core/references/ui-brand.md +65 -21
  206. package/gsd-core/references/ui-consideration-probe.md +1 -1
  207. package/gsd-core/references/universal-anti-patterns.md +2 -2
  208. package/gsd-core/references/verifier-evidence-gate.md +160 -0
  209. package/gsd-core/references/verify-command-path-resolvability.md +42 -0
  210. package/gsd-core/references/verify-mvp-mode.md +1 -1
  211. package/gsd-core/references/workstream-flag.md +11 -11
  212. package/gsd-core/templates/README.md +1 -1
  213. package/gsd-core/templates/SECURITY.md +3 -3
  214. package/gsd-core/templates/UI-SPEC.md +25 -3
  215. package/gsd-core/templates/VALIDATION.md +3 -3
  216. package/gsd-core/templates/phase-prompt.md +7 -0
  217. package/gsd-core/templates/state.md +7 -0
  218. package/gsd-core/templates/verification-report.md +5 -0
  219. package/gsd-core/workflows/_runtime-launcher.snippet.sh +1 -1
  220. package/gsd-core/workflows/add-backlog.md +3 -1
  221. package/gsd-core/workflows/add-phase.md +5 -3
  222. package/gsd-core/workflows/add-tests.md +4 -9
  223. package/gsd-core/workflows/add-todo.md +2 -2
  224. package/gsd-core/workflows/ai-integration-phase.md +5 -10
  225. package/gsd-core/workflows/analyze-dependencies.md +2 -0
  226. package/gsd-core/workflows/audit-fix.md +14 -3
  227. package/gsd-core/workflows/audit-milestone.md +11 -9
  228. package/gsd-core/workflows/audit-uat.md +19 -2
  229. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +2 -2
  230. package/gsd-core/workflows/autonomous.md +12 -26
  231. package/gsd-core/workflows/check-todos.md +2 -2
  232. package/gsd-core/workflows/cleanup.md +3 -3
  233. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +16 -14
  234. package/gsd-core/workflows/code-review-fix.md +3 -1
  235. package/gsd-core/workflows/code-review.md +192 -69
  236. package/gsd-core/workflows/complete-milestone.md +28 -14
  237. package/gsd-core/workflows/debug.md +6 -4
  238. package/gsd-core/workflows/diagnose-issues.md +17 -7
  239. package/gsd-core/workflows/discuss-phase/modes/advisor.md +3 -1
  240. package/gsd-core/workflows/discuss-phase/modes/all.md +2 -0
  241. package/gsd-core/workflows/discuss-phase/modes/analyze.md +2 -0
  242. package/gsd-core/workflows/discuss-phase/modes/auto.md +2 -0
  243. package/gsd-core/workflows/discuss-phase/modes/batch.md +2 -0
  244. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -7
  245. package/gsd-core/workflows/discuss-phase/modes/default.md +2 -0
  246. package/gsd-core/workflows/discuss-phase/modes/power.md +2 -0
  247. package/gsd-core/workflows/discuss-phase/modes/text.md +3 -1
  248. package/gsd-core/workflows/discuss-phase/templates/context.md +2 -0
  249. package/gsd-core/workflows/discuss-phase/templates/discussion-log.md +2 -0
  250. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +1 -3
  251. package/gsd-core/workflows/discuss-phase-assumptions.md +3 -3
  252. package/gsd-core/workflows/discuss-phase-power.md +2 -0
  253. package/gsd-core/workflows/discuss-phase.md +2 -2
  254. package/gsd-core/workflows/do.md +46 -19
  255. package/gsd-core/workflows/docs-update.md +6 -5
  256. package/gsd-core/workflows/edit-phase.md +3 -1
  257. package/gsd-core/workflows/eval-review.md +5 -10
  258. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +3 -1
  259. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +129 -11
  260. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +1 -1
  261. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +1 -1
  262. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +1 -1
  263. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +29 -5
  264. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +2 -2
  265. package/gsd-core/workflows/execute-phase/steps/protected-branch.md +21 -0
  266. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +4 -2
  267. package/gsd-core/workflows/execute-phase/steps/tdd-applicability-resolution.md +25 -0
  268. package/gsd-core/workflows/execute-phase/steps/wave-post-gate-hooks.md +39 -0
  269. package/gsd-core/workflows/execute-phase/steps/worktree-recovery-policy.md +2 -0
  270. package/gsd-core/workflows/execute-phase.md +68 -66
  271. package/gsd-core/workflows/execute-plan.md +25 -20
  272. package/gsd-core/workflows/explore.md +3 -1
  273. package/gsd-core/workflows/extract-learnings.md +3 -1
  274. package/gsd-core/workflows/fast.md +8 -2
  275. package/gsd-core/workflows/forensics.md +3 -1
  276. package/gsd-core/workflows/graduation.md +6 -6
  277. package/gsd-core/workflows/health.md +4 -7
  278. package/gsd-core/workflows/help/modes/brief.md +2 -0
  279. package/gsd-core/workflows/help/modes/default.md +2 -0
  280. package/gsd-core/workflows/help/modes/full.md +12 -0
  281. package/gsd-core/workflows/help/modes/topic.md +2 -0
  282. package/gsd-core/workflows/help.md +2 -0
  283. package/gsd-core/workflows/import.md +17 -14
  284. package/gsd-core/workflows/inbox.md +5 -6
  285. package/gsd-core/workflows/ingest-docs.md +45 -12
  286. package/gsd-core/workflows/insert-phase.md +7 -5
  287. package/gsd-core/workflows/list-phase-assumptions.md +2 -0
  288. package/gsd-core/workflows/list-seeds.md +7 -3
  289. package/gsd-core/workflows/list-workspaces.md +3 -1
  290. package/gsd-core/workflows/manager.md +15 -26
  291. package/gsd-core/workflows/map-codebase.md +3 -1
  292. package/gsd-core/workflows/milestone-summary.md +3 -1
  293. package/gsd-core/workflows/mvp-phase.md +3 -3
  294. package/gsd-core/workflows/new-milestone.md +10 -22
  295. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +1 -1
  296. package/gsd-core/workflows/new-project.md +17 -29
  297. package/gsd-core/workflows/new-workspace.md +2 -2
  298. package/gsd-core/workflows/next.md +4 -2
  299. package/gsd-core/workflows/node-repair.md +2 -0
  300. package/gsd-core/workflows/note.md +2 -0
  301. package/gsd-core/workflows/onboard.md +1 -1
  302. package/gsd-core/workflows/pause-work.md +20 -5
  303. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +1 -1
  304. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +100 -18
  305. package/gsd-core/workflows/plan-phase/steps/prd-express-path.md +4 -4
  306. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +12 -3
  307. package/gsd-core/workflows/plan-phase.md +251 -54
  308. package/gsd-core/workflows/plan-review-convergence.md +148 -19
  309. package/gsd-core/workflows/plant-seed.md +3 -3
  310. package/gsd-core/workflows/pr-branch.md +195 -51
  311. package/gsd-core/workflows/profile-user.md +17 -15
  312. package/gsd-core/workflows/progress/steps/forensic-audit.md +1 -1
  313. package/gsd-core/workflows/progress.md +52 -15
  314. package/gsd-core/workflows/quick/steps/discussion-phase.md +1 -3
  315. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +38 -5
  316. package/gsd-core/workflows/quick/steps/quick-verification.md +2 -4
  317. package/gsd-core/workflows/quick/steps/research-phase.md +5 -7
  318. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +3 -3
  319. package/gsd-core/workflows/quick-batch/steps/batch-init.md +55 -0
  320. package/gsd-core/workflows/quick-batch/steps/completion.md +65 -0
  321. package/gsd-core/workflows/quick-batch/steps/merge-wave.md +100 -0
  322. package/gsd-core/workflows/quick-batch/steps/plan-checker-loop.md +147 -0
  323. package/gsd-core/workflows/quick-batch/steps/planner-wave.md +158 -0
  324. package/gsd-core/workflows/quick-batch/steps/research-phase.md +95 -0
  325. package/gsd-core/workflows/quick-batch/steps/resume-mode.md +49 -0
  326. package/gsd-core/workflows/quick-batch/steps/verification-wave.md +73 -0
  327. package/gsd-core/workflows/quick-batch/steps/worktree-dispatch.md +169 -0
  328. package/gsd-core/workflows/quick-batch.md +203 -0
  329. package/gsd-core/workflows/quick.md +33 -32
  330. package/gsd-core/workflows/reapply-patches.md +2 -0
  331. package/gsd-core/workflows/remove-phase.md +6 -4
  332. package/gsd-core/workflows/remove-workspace.md +3 -3
  333. package/gsd-core/workflows/resume-project.md +14 -14
  334. package/gsd-core/workflows/review.md +404 -21
  335. package/gsd-core/workflows/scan.md +3 -1
  336. package/gsd-core/workflows/section-manifest.json +12 -0
  337. package/gsd-core/workflows/secure-phase.md +3 -3
  338. package/gsd-core/workflows/session-report.md +2 -0
  339. package/gsd-core/workflows/settings-advanced.md +9 -9
  340. package/gsd-core/workflows/settings-integrations.md +66 -32
  341. package/gsd-core/workflows/settings.md +4 -6
  342. package/gsd-core/workflows/ship.md +22 -16
  343. package/gsd-core/workflows/sketch-wrap-up.md +13 -17
  344. package/gsd-core/workflows/sketch.md +13 -19
  345. package/gsd-core/workflows/smart-entry.md +4 -6
  346. package/gsd-core/workflows/spec-phase.md +31 -4
  347. package/gsd-core/workflows/spike-wrap-up.md +9 -11
  348. package/gsd-core/workflows/spike.md +21 -32
  349. package/gsd-core/workflows/stats.md +4 -2
  350. package/gsd-core/workflows/sync-skills.md +13 -5
  351. package/gsd-core/workflows/thread.md +13 -7
  352. package/gsd-core/workflows/transition.md +7 -5
  353. package/gsd-core/workflows/ui-phase.md +36 -21
  354. package/gsd-core/workflows/ui-review.md +7 -11
  355. package/gsd-core/workflows/ultraplan-phase.md +7 -13
  356. package/gsd-core/workflows/undo.md +9 -17
  357. package/gsd-core/workflows/update.md +47 -48
  358. package/gsd-core/workflows/validate-phase.md +3 -3
  359. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +25 -1
  360. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +1 -1
  361. package/gsd-core/workflows/verify-work.md +106 -21
  362. package/hooks/dist/gsd-agent-isolation-guard.js +77 -38
  363. package/hooks/dist/gsd-check-update-worker.js +19 -2
  364. package/hooks/dist/gsd-config-reload.js +18 -12
  365. package/hooks/dist/gsd-context-monitor.js +302 -22
  366. package/hooks/dist/gsd-cursor-post-tool.js +3 -1
  367. package/hooks/dist/gsd-cursor-pre-tool.js +3 -1
  368. package/hooks/dist/gsd-cursor-session-start.js +2 -1
  369. package/hooks/dist/gsd-cursor-stop.js +2 -1
  370. package/hooks/dist/gsd-cursor-subagent-start.js +28 -23
  371. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -1
  372. package/hooks/dist/gsd-ensure-canonical-path.js +2 -1
  373. package/hooks/dist/gsd-graphify-update.sh +22 -18
  374. package/hooks/dist/gsd-node-runner.sh +77 -0
  375. package/hooks/dist/gsd-phase-boundary.sh +1 -0
  376. package/hooks/dist/gsd-prompt-guard.js +46 -12
  377. package/hooks/dist/gsd-read-guard.js +18 -7
  378. package/hooks/dist/gsd-read-injection-scanner.js +22 -13
  379. package/hooks/dist/gsd-secret-read-guard.js +1079 -0
  380. package/hooks/dist/gsd-session-state.sh +1 -0
  381. package/hooks/dist/gsd-statusline.js +222 -29
  382. package/hooks/dist/gsd-validate-commit.sh +523 -12
  383. package/hooks/dist/gsd-windsurf-pre-command.js +16 -11
  384. package/hooks/dist/gsd-windsurf-pre-write.js +22 -13
  385. package/hooks/dist/gsd-workflow-guard.js +36 -17
  386. package/hooks/dist/gsd-worktree-path-guard.js +36 -21
  387. package/hooks/dist/gsd-write-guard.js +35 -25
  388. package/hooks/dist/lib/cli-exit.js +560 -0
  389. package/hooks/dist/lib/exit-code-registry.js +98 -0
  390. package/hooks/dist/lib/git-cmd.js +210 -1
  391. package/hooks/dist/lib/git-probe.js +84 -0
  392. package/hooks/dist/lib/hook-exit.js +81 -0
  393. package/hooks/dist/lib/injection-patterns.js +36 -6
  394. package/hooks/dist/managed-hooks-registry.cjs +4 -0
  395. package/hooks/gsd-agent-isolation-guard.js +77 -38
  396. package/hooks/gsd-check-update-worker.js +19 -2
  397. package/hooks/gsd-config-reload.js +18 -12
  398. package/hooks/gsd-context-monitor.js +302 -22
  399. package/hooks/gsd-cursor-post-tool.js +3 -1
  400. package/hooks/gsd-cursor-pre-tool.js +3 -1
  401. package/hooks/gsd-cursor-session-start.js +2 -1
  402. package/hooks/gsd-cursor-stop.js +2 -1
  403. package/hooks/gsd-cursor-subagent-start.js +28 -23
  404. package/hooks/gsd-cursor-subagent-stop.js +3 -1
  405. package/hooks/gsd-ensure-canonical-path.js +2 -1
  406. package/hooks/gsd-graphify-update.sh +22 -18
  407. package/hooks/gsd-node-runner.sh +77 -0
  408. package/hooks/gsd-phase-boundary.sh +1 -0
  409. package/hooks/gsd-prompt-guard.js +46 -12
  410. package/hooks/gsd-read-guard.js +18 -7
  411. package/hooks/gsd-read-injection-scanner.js +22 -13
  412. package/hooks/gsd-secret-read-guard.js +1079 -0
  413. package/hooks/gsd-session-state.sh +1 -0
  414. package/hooks/gsd-statusline.js +222 -29
  415. package/hooks/gsd-validate-commit.sh +523 -12
  416. package/hooks/gsd-windsurf-pre-command.js +16 -11
  417. package/hooks/gsd-windsurf-pre-write.js +22 -13
  418. package/hooks/gsd-workflow-guard.js +36 -17
  419. package/hooks/gsd-worktree-path-guard.js +36 -21
  420. package/hooks/gsd-write-guard.js +35 -25
  421. package/hooks/hooks.json +6 -0
  422. package/hooks/lib/cli-exit.js +560 -0
  423. package/hooks/lib/exit-code-registry.js +98 -0
  424. package/hooks/lib/git-cmd.js +210 -1
  425. package/hooks/lib/git-probe.js +84 -0
  426. package/hooks/lib/hook-exit.js +81 -0
  427. package/hooks/lib/injection-patterns.js +36 -6
  428. package/hooks/managed-hooks-registry.cjs +4 -0
  429. package/package.json +14 -9
  430. package/scripts/base64-scan.sh +74 -12
  431. package/scripts/build-hooks.js +12 -0
  432. package/scripts/check-glossary-refs.cjs +77 -15
  433. package/scripts/check-mutation-score-ratchet.cjs +156 -0
  434. package/scripts/ci-check-job-near-cap.cjs +49 -0
  435. package/scripts/ci-pr-mergeability.cjs +262 -0
  436. package/scripts/ci-test-scope.cjs +52 -12
  437. package/scripts/ci-timeout-report.cjs +230 -0
  438. package/scripts/docs-guard-registry.cjs +406 -0
  439. package/scripts/gen-capability-registry.cjs +8 -6
  440. package/scripts/gen-exit-code-docs.cjs +318 -0
  441. package/scripts/gen-exit-code-registry.cjs +891 -0
  442. package/scripts/gen-features.cjs +836 -0
  443. package/scripts/gen-hooks-cli-exit.cjs +239 -0
  444. package/scripts/gen-install-tree-fixtures.cjs +2 -2
  445. package/scripts/gen-loop-host-contract.cjs +189 -4
  446. package/scripts/gen-scripts-cli-exit.cjs +185 -0
  447. package/scripts/gen-state-md-docs.cjs +727 -0
  448. package/scripts/{test-failure-reasons.cjs → gsd-test-gate-reasons.cjs} +6 -0
  449. package/scripts/lib/ci-job-timing.cjs +72 -0
  450. package/scripts/lib/cli-exit.cjs +546 -44
  451. package/scripts/lib/drift-scan.cjs +32 -2
  452. package/scripts/lib/exit-code-registry.cjs +98 -0
  453. package/scripts/lib/ndjson-reporter.cjs +119 -0
  454. package/scripts/lib/shellcheck-fetch.cjs +247 -0
  455. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -6
  456. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +1 -1
  457. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +1 -1
  458. package/scripts/lint-docs-guard-registration.cjs +495 -0
  459. package/scripts/lint-docs-guard-registration.exempt-baseline.cjs +198 -0
  460. package/scripts/lint-eslint-glob-coverage.allowlist.json +4 -0
  461. package/scripts/{lint-fix-has-regression-test.cjs → lint-fix-has-regression-tests.cjs} +12 -6
  462. package/scripts/lint-health-diagnostic-rule-table.cjs +65 -8
  463. package/scripts/lint-mutation-test-derivation-drift.cjs +86 -0
  464. package/scripts/lint-phase-enumeration-drift.cjs +45 -14
  465. package/scripts/lint-phase-id-drift.cjs +133 -8
  466. package/scripts/lint-planning-prompt-drift.cjs +38 -1
  467. package/scripts/lint-portable-grep.cjs +176 -0
  468. package/scripts/lint-removed-but-needed.cjs +184 -16
  469. package/scripts/lint-response-language-coverage.cjs +524 -0
  470. package/scripts/lint-seam-enforcement.cjs +182 -0
  471. package/scripts/lint-slug-derivation-drift.cjs +921 -0
  472. package/scripts/lint-source-test-name-collision.cjs +241 -0
  473. package/scripts/lint-state-write-path-drift.cjs +337 -432
  474. package/scripts/lint-test-file-count.allowlist.json +124 -4
  475. package/scripts/lint-test-file-count.cjs +25 -3
  476. package/scripts/lint-unreachable-guard-drift.cjs +51 -64
  477. package/scripts/lint-vendored-deps.cjs +208 -35
  478. package/scripts/lint-workflow-shellcheck-baseline.json +1027 -0
  479. package/scripts/lint-workflow-shellcheck.cjs +614 -0
  480. package/scripts/mutation-matrix.cjs +599 -50
  481. package/scripts/npm-audit-baseline.cjs +376 -0
  482. package/scripts/prompt-injection-scan.sh +83 -14
  483. package/scripts/require-issue-link-policy.cjs +16 -1
  484. package/scripts/secret-scan.sh +75 -13
  485. package/scripts/select-docs-guards.cjs +56 -0
  486. package/scripts/sync-runtime-launcher.cjs +22 -3
  487. package/skills/gsd-discuss-phase/SKILL.md +1 -1
  488. package/skills/gsd-execute-phase/SKILL.md +1 -1
  489. package/skills/gsd-import/SKILL.md +1 -1
  490. package/skills/gsd-ns-workflow/SKILL.md +1 -0
  491. package/skills/gsd-phase/SKILL.md +1 -1
  492. package/skills/gsd-quick/SKILL.md +8 -4
  493. package/skills/gsd-quick-batch/SKILL.md +105 -0
  494. package/skills/gsd-surface/SKILL.md +18 -8
  495. package/vscode/package.json +1 -1
  496. package/bin/lib/ui-safety-gate.cjs +0 -109
  497. package/scripts/lint-emitted-drift-ack.cjs +0 -344
  498. package/scripts/state-write-path-drift-baseline.json +0 -19
@@ -39,13 +39,13 @@ const ioMod = require("./io.cjs");
39
39
  const { output: coreOutput, error: coreError } = ioMod;
40
40
  // eslint-disable-next-line @typescript-eslint/no-require-imports
41
41
  const activationMod = require("./capability-activation.cjs");
42
- const { _resolveActivationValue } = activationMod;
42
+ const { _resolveActivationValue, _resolvePointGate } = activationMod;
43
43
  // eslint-disable-next-line @typescript-eslint/no-require-imports
44
44
  const configLoaderMod = require("./config-loader.cjs");
45
45
  const { loadConfig } = configLoaderMod;
46
46
  // eslint-disable-next-line @typescript-eslint/no-require-imports
47
47
  const installProfilesMod = require("./install-profiles.cjs");
48
- const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents } = installProfilesMod;
48
+ const { readActiveProfile, loadSkillsManifest, resolveProfile, parseRequires, parseCallsAgents, workflowAgentRefs } = installProfilesMod;
49
49
  // eslint-disable-next-line @typescript-eslint/no-require-imports
50
50
  const surfaceMod = require("./surface.cjs");
51
51
  const { resolveSurface } = surfaceMod;
@@ -179,6 +179,12 @@ function resolveCapabilityState(input) {
179
179
  // (mirrors loop-resolver.isActive: `typeof when !== 'string' || when.length === 0` → false)
180
180
  configured = false;
181
181
  }
182
+ // #3661: optional point-selection gate, ANDed in — mirrors loop-resolver.isActive
183
+ // EXACTLY (see capability-activation.cts's _resolvePointGate doc comment; this
184
+ // parity is load-bearing, see tests/capability-precedence-parity.test.cjs).
185
+ if (configured) {
186
+ configured = _resolvePointGate(h['pointFrom'], point, config, cwd, registry);
187
+ }
182
188
  // Hook active = capability-level active AND hook's own config gate.
183
189
  // The capability's `active` constant (= enabled && configActivation) is
184
190
  // used here so that a config-disabled capability (active=false) cannot
@@ -299,7 +305,17 @@ function _loadInstalledSkillsManifest(configDir) {
299
305
  * no gsd-*.md files (so _resolveManifest can use size>0 as the "flat layout
300
306
  * present" signal and fall through to the installed-skills branch otherwise).
301
307
  */
302
- function _loadFlatCommandsGsdManifest(commandsParentDir) {
308
+ function _loadFlatCommandsGsdManifest(commandsParentDir, workflowsDir) {
309
+ // #3798: derive agents from the command body PLUS the workflow files it
310
+ // references, exactly like loadSkillsManifest does for the nested layout —
311
+ // the flat (#1858) layout retained the original defect otherwise, and the
312
+ // parity test in tests/capability-state.test.cjs asserts the two loaders
313
+ // produce identical _calls_agents_* sets. Default: <pkg>/gsd-core/workflows
314
+ // one level above the commands dir — the shape of both the repo checkout
315
+ // (<repo>/commands/) and a flat runtime install (the runtime config dir's
316
+ // commands/ with the package's gsd-core/ beside it).
317
+ const flatWorkflowsDir = workflowsDir
318
+ || node_path_1.default.resolve(commandsParentDir, '..', 'gsd-core', 'workflows');
303
319
  const manifest = new Map();
304
320
  let entries;
305
321
  try {
@@ -327,7 +343,12 @@ function _loadFlatCommandsGsdManifest(commandsParentDir) {
327
343
  try {
328
344
  const content = node_fs_1.default.readFileSync(node_path_1.default.join(commandsParentDir, entry.name), 'utf8');
329
345
  manifest.set(stem, parseRequires(content));
330
- manifest.set(`_calls_agents_${stem}`, parseCallsAgents(content));
346
+ manifest.set(`_calls_agents_${stem}`, [
347
+ ...new Set([
348
+ ...parseCallsAgents(content),
349
+ ...workflowAgentRefs(content, flatWorkflowsDir),
350
+ ]),
351
+ ]);
331
352
  }
332
353
  catch {
333
354
  manifest.set(stem, []);
@@ -396,6 +396,11 @@ function validateCapability(cap, folderId) {
396
396
  errors.push(...validateRuntimeBody(cap));
397
397
  // A host that is ALSO a reviewer keeps exactly one manifest (ADR-2782 D1).
398
398
  errors.push(...validateReviewerBody(cap));
399
+ // ADR-3646: a runtime capability installs a host CLI, it does not resolve
400
+ // task content — taskContentResolver is feature-only.
401
+ if (cap.taskContentResolver !== undefined) {
402
+ errors.push('role:runtime capability must not have a "taskContentResolver" body (feature-only field)');
403
+ }
399
404
  } else if (cap.role === 'reviewer') {
400
405
  // ADR-2782 D3 — a lane that is not an install target. No runtime body, no
401
406
  // install surface, no runtimeCompat (it surfaces through no host runtime).
@@ -718,6 +723,118 @@ function validateFeatureBody(cap) {
718
723
  }
719
724
  }
720
725
 
726
+ // ADR-3646: optional per-task external-tracker content-resolution seam.
727
+ errors.push(...validateTaskContentResolver(cap));
728
+
729
+ return errors;
730
+ }
731
+
732
+ /**
733
+ * ADR-3646 — validate an OPTIONAL `taskContentResolver` body on a `role:
734
+ * "feature"` capability. Absence is never an error (most manifests won't
735
+ * have one); presence is strictly validated.
736
+ *
737
+ * Wrapped in try/catch to degrade any unexpected throw (a hostile Proxy, a
738
+ * throwing getter, etc.) to a single validation error rather than crashing
739
+ * every consumer of loadRegistry, per the #1461 OVL-1 discipline that
740
+ * `validateReviewerBody` follows.
741
+ *
742
+ * @param {object} cap The parsed capability manifest.
743
+ * @returns {string[]} Array of error strings; empty = valid or absent.
744
+ */
745
+ function validateTaskContentResolver(cap) {
746
+ try {
747
+ return validateTaskContentResolverFields(cap);
748
+ } catch (err) {
749
+ return ['capability taskContentResolver body could not be validated: ' + safeErrorMessage(err)];
750
+ }
751
+ }
752
+
753
+ /**
754
+ * Upper bound for `taskContentResolver.invoke.timeoutMs`, specific to this
755
+ * field only. `isPositiveIntegerMs()` has no ceiling and stays that way — it
756
+ * is shared with the reviewer lane's `timeoutFloorMs` and probe `timeoutMs`,
757
+ * which may legitimately need a longer or unbounded value. Without a ceiling
758
+ * here, a manifest could declare `Number.MAX_SAFE_INTEGER` and let
759
+ * `resolve-content` hang near-indefinitely on a stuck/malicious resolver,
760
+ * defeating the feature's "bounded subprocess" design intent.
761
+ */
762
+ const TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS = 120000;
763
+
764
+ function validateTaskContentResolverFields(cap) {
765
+ const errors = [];
766
+ if (typeof cap !== 'object' || cap === null || Array.isArray(cap)) return errors;
767
+
768
+ const tcr = cap.taskContentResolver;
769
+ if (tcr === undefined) return errors; // optional — never an error to omit
770
+
771
+ const ctx = 'capability "' + (typeof cap.id === 'string' ? cap.id : '(unknown)') + '"';
772
+
773
+ if (typeof tcr !== 'object' || tcr === null || Array.isArray(tcr)) {
774
+ const got = tcr === null ? 'null' : Array.isArray(tcr) ? 'array' : typeof tcr;
775
+ errors.push(
776
+ ctx + ' taskContentResolver must be an object (got: ' + got + '). ' +
777
+ 'Omit the key entirely to declare no resolver — an explicit null is not an omission.',
778
+ );
779
+ return errors; // cannot validate fields of a non-object
780
+ }
781
+
782
+ // ── trackerPrefix — same grammar as a capability id ─────────────────────
783
+ if (typeof tcr.trackerPrefix !== 'string' || tcr.trackerPrefix.length === 0 || !KEBAB_RE.test(tcr.trackerPrefix)) {
784
+ errors.push(
785
+ ctx + ' taskContentResolver.trackerPrefix must be a non-empty kebab-case string matching ' +
786
+ String(KEBAB_RE) + ' (got: ' + describeValue(tcr.trackerPrefix) + ')',
787
+ );
788
+ }
789
+
790
+ // ── invoke ────────────────────────────────────────────────────────────
791
+ const inv = tcr.invoke;
792
+ if (typeof inv !== 'object' || inv === null || Array.isArray(inv)) {
793
+ const got = inv === null ? 'null' : Array.isArray(inv) ? 'array' : typeof inv;
794
+ errors.push(ctx + ' taskContentResolver.invoke must be an object (got: ' + got + ')');
795
+ return errors; // cannot validate sub-fields of a non-object
796
+ }
797
+
798
+ if (typeof inv.binary !== 'string' || inv.binary.length === 0) {
799
+ errors.push(ctx + ' taskContentResolver.invoke.binary must be a non-empty string');
800
+ }
801
+
802
+ if (!Array.isArray(inv.args)) {
803
+ errors.push(ctx + ' taskContentResolver.invoke.args must be an array of strings');
804
+ } else {
805
+ let hasPlaceholder = false;
806
+ for (const a of inv.args) {
807
+ if (typeof a !== 'string') {
808
+ errors.push(ctx + ' taskContentResolver.invoke.args entries must be strings (got: ' + describeValue(a) + ')');
809
+ } else if (a === '{{id}}') {
810
+ hasPlaceholder = true;
811
+ }
812
+ }
813
+ if (!hasPlaceholder) {
814
+ errors.push(
815
+ ctx + ' taskContentResolver.invoke.args must contain a "{{id}}" placeholder — ' +
816
+ 'without it the resolved tracker id could never reach the resolver subprocess',
817
+ );
818
+ }
819
+ }
820
+
821
+ if (!isPositiveIntegerMs(inv.timeoutMs)) {
822
+ errors.push(
823
+ ctx + ' taskContentResolver.invoke.timeoutMs must be a positive integer of milliseconds — ' +
824
+ 'an unbounded resolver call could hang task execution indefinitely ' +
825
+ '(got: ' + describeValue(inv.timeoutMs) + ')',
826
+ );
827
+ } else if (inv.timeoutMs > TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS) {
828
+ // Ceiling specific to this field — `isPositiveIntegerMs()` itself stays
829
+ // unbounded because it is shared with the reviewer lane's
830
+ // `timeoutFloorMs`/probe `timeoutMs`, which have no such ceiling.
831
+ errors.push(
832
+ ctx + ' taskContentResolver.invoke.timeoutMs must not exceed ' +
833
+ TASK_CONTENT_RESOLVER_TIMEOUT_CEILING_MS + 'ms — an unbounded-in-practice value defeats the ' +
834
+ '"bounded subprocess" design intent (got: ' + inv.timeoutMs + ')',
835
+ );
836
+ }
837
+
721
838
  return errors;
722
839
  }
723
840
 
@@ -943,7 +1060,7 @@ const HTTP_ONLY_INVOKE_FIELDS = ['hostConfigKey', 'defaultHost', 'path', 'model
943
1060
 
944
1061
  // Feature-only fields are as forbidden on a lane-only capability as on a runtime
945
1062
  // one; a `role: "reviewer"` capability owns no artefacts and wires no loop point.
946
- const FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey'];
1063
+ const FEATURE_FIELDS_FORBIDDEN_ON_REVIEWER = ['skills', 'agents', 'steps', 'contributions', 'gates', 'hooks', 'activationKey', 'taskContentResolver'];
947
1064
 
948
1065
  // GATE A: installSurface → allowed hooksSurface values (DEFECT.GENERATIVE-FIX: parity invariant)
949
1066
  // Derived from the actual pairings in the 16 real runtime descriptors.
@@ -1526,6 +1643,28 @@ function validateRuntimeBody(cap) {
1526
1643
  ' (or "undocumented") (got: ' + JSON.stringify(d.isolation) + ')',
1527
1644
  );
1528
1645
  }
1646
+
1647
+ // maxConcurrency — ADR-1239 Phase 1 (#3673). A numeric dispatch
1648
+ // sub-field (not a closed-vocabulary enum member — same numeric
1649
+ // dispatch sub-field treatment as maxDepth above; unlike maxDepth,
1650
+ // 0 and negative values are invalid — 1 is the fail-closed floor, not
1651
+ // a legitimate "no concurrency" declaration).
1652
+ // OPTIONAL, like isolation: added after existing descriptors, so an
1653
+ // omitted maxConcurrency is legitimate — negotiateHostCapabilities
1654
+ // degrades it to 1 (the safe floor) and warns, exactly as for any
1655
+ // other undeclared dispatch sub-field. Only a PRESENT value is
1656
+ // checked against the positive-safe-integer contract.
1657
+ if (d.maxConcurrency === undefined) {
1658
+ // absent — nothing to validate; negotiateHostCapabilities fails it closed.
1659
+ } else if (
1660
+ d.maxConcurrency !== 'undocumented' &&
1661
+ (!Number.isSafeInteger(d.maxConcurrency) || d.maxConcurrency < 1)
1662
+ ) {
1663
+ errors.push(
1664
+ 'runtime.hostIntegration.dispatch.maxConcurrency must be a positive safe integer or "undocumented" ' +
1665
+ '(got: ' + JSON.stringify(d.maxConcurrency) + ')',
1666
+ );
1667
+ }
1529
1668
  }
1530
1669
  }
1531
1670
 
@@ -1588,6 +1727,16 @@ function validateRuntimeBody(cap) {
1588
1727
  'runtime.orchestratorExec.promptFlag must be a string or null (got: ' + JSON.stringify(oe.promptFlag) + ')',
1589
1728
  );
1590
1729
  }
1730
+
1731
+ // modelFlag — optional; string or null (#3714). `null`/absent means the
1732
+ // host offers no per-invocation model override on this exec path; a
1733
+ // string names the flag that pins the executor's model (codex: --model).
1734
+ // Deliberately asymmetric: only codex declares this today.
1735
+ if (oe.modelFlag !== undefined && oe.modelFlag !== null && typeof oe.modelFlag !== 'string') {
1736
+ errors.push(
1737
+ 'runtime.orchestratorExec.modelFlag must be a string or null (got: ' + JSON.stringify(oe.modelFlag) + ')',
1738
+ );
1739
+ }
1591
1740
  }
1592
1741
  }
1593
1742
 
@@ -1697,9 +1846,29 @@ const KNOWN_REVIEWER_FIELDS = new Set([
1697
1846
  // missed a configured model and silently disabled the pinned-model escape hatch
1698
1847
  // #2073 added. A convention one shipped lane already violates is not a contract.
1699
1848
  'modelConfigKey',
1849
+ // `timeoutConfigKey` added by #3274, same optional/backward-compatible shape as
1850
+ // `modelConfigKey` above: a manifest authored before this field existed must keep validating.
1851
+ 'timeoutConfigKey',
1852
+ // `effortConfigKey` / `defaultEffort` added by #4255, same optional/backward-compatible shape
1853
+ // as the two above. Lane effort used to be resolved by querying the `gsd-plan-checker` AGENT
1854
+ // through a hardcoded id, so every prompt-fed lane ran at that verifier's frontmatter effort;
1855
+ // it is a property of the review, so the lane declares it. A manifest authored before these
1856
+ // fields existed must keep validating — absent is read as "this lane declares no review
1857
+ // effort", which emits no effort argument at all.
1858
+ 'effortConfigKey',
1859
+ 'defaultEffort',
1700
1860
  'handler',
1701
1861
  ]);
1702
1862
 
1863
+ /**
1864
+ * The effort levels a lane may declare as its review default (#4255, #3533 vocabulary).
1865
+ *
1866
+ * `inherit` is deliberately NOT a member. It is a legitimate CONFIGURED value — it selects "emit
1867
+ * no argument, the CLI's own configuration decides" — but as a DECLARED default it would be a
1868
+ * second spelling of `null` and split one behaviour across two shapes.
1869
+ */
1870
+ const REVIEWER_EFFORT_LEVELS = new Set(['minimal', 'low', 'medium', 'high', 'xhigh', 'max']);
1871
+
1703
1872
  /**
1704
1873
  * The closed `runtime.hostBehaviors` vocabulary (ADR-1016, closed via #2801).
1705
1874
  *
@@ -2244,6 +2413,50 @@ function validateReviewerBodyFields(cap) {
2244
2413
  );
2245
2414
  }
2246
2415
 
2416
+ // OPTIONAL, mirroring modelConfigKey's D4 forward/backward-compat treatment (#3274): a manifest
2417
+ // authored before this field existed must keep validating. Absent/null means "no override for
2418
+ // this lane, use timeoutFloorMs". An empty string is neither absent nor a key, and is rejected.
2419
+ if (r.timeoutConfigKey !== undefined && r.timeoutConfigKey !== null &&
2420
+ (typeof r.timeoutConfigKey !== 'string' || r.timeoutConfigKey.length === 0)) {
2421
+ errors.push(
2422
+ ctx + ' reviewer.timeoutConfigKey must be a dotted config key or null ' +
2423
+ '(got: ' + describeValue(r.timeoutConfigKey) + ')',
2424
+ );
2425
+ }
2426
+
2427
+ // OPTIONAL, mirroring the two keys above (#4255). Absent/null means "this lane declares no
2428
+ // review effort", which is the shape that emits no effort argument at all — the correct state
2429
+ // for the nine lanes with no effort channel to feed. An empty string is neither.
2430
+ if (r.effortConfigKey !== undefined && r.effortConfigKey !== null &&
2431
+ (typeof r.effortConfigKey !== 'string' || r.effortConfigKey.length === 0)) {
2432
+ errors.push(
2433
+ ctx + ' reviewer.effortConfigKey must be a dotted config key or null ' +
2434
+ '(got: ' + describeValue(r.effortConfigKey) + ')',
2435
+ );
2436
+ }
2437
+
2438
+ // A declared default must be a level the effort axis knows, or the lane would render an
2439
+ // argument the reviewer CLI rejects and kill itself on every run. Closed vocabulary, checked
2440
+ // here rather than at resolution time so a malformed manifest fails at the trust boundary.
2441
+ if (r.defaultEffort !== undefined && r.defaultEffort !== null
2442
+ && !(typeof r.defaultEffort === 'string' && REVIEWER_EFFORT_LEVELS.has(r.defaultEffort))) {
2443
+ errors.push(
2444
+ ctx + ' reviewer.defaultEffort must be one of ' +
2445
+ Array.from(REVIEWER_EFFORT_LEVELS).join('/') + ' or null ' +
2446
+ '(got: ' + describeValue(r.defaultEffort) + ')',
2447
+ );
2448
+ }
2449
+
2450
+ // A default with nothing to configure it through is a value the operator cannot change — the
2451
+ // shape #4255 exists to end. Declared together or not at all.
2452
+ if ((r.defaultEffort !== undefined && r.defaultEffort !== null)
2453
+ && (r.effortConfigKey === undefined || r.effortConfigKey === null)) {
2454
+ errors.push(
2455
+ ctx + ' reviewer.defaultEffort is declared without an effortConfigKey, so the level ' +
2456
+ 'could never be overridden',
2457
+ );
2458
+ }
2459
+
2247
2460
  // `null` is the declared "no per-lane budget"; an empty string is not.
2248
2461
  if (r.promptBudgetKey !== null && (typeof r.promptBudgetKey !== 'string' || r.promptBudgetKey.length === 0)) {
2249
2462
  errors.push(
@@ -2731,6 +2944,10 @@ function validateStep(step, prefix, declaredSkills, declaredAgents) {
2731
2944
  errors.push(prefix + '.when must be a string if present');
2732
2945
  }
2733
2946
 
2947
+ if (step.pointFrom !== undefined && typeof step.pointFrom !== 'string') {
2948
+ errors.push(prefix + '.pointFrom must be a string if present');
2949
+ }
2950
+
2734
2951
  if (step.fragment !== undefined) {
2735
2952
  errors.push(...validateFragment(step.fragment, prefix + '.fragment'));
2736
2953
  }
@@ -2870,6 +3087,31 @@ function validateAgainstContract(cap, capId) {
2870
3087
  }
2871
3088
  }
2872
3089
 
3090
+ // pointFrom (#3661): selects which of possibly several same-capability steps is
3091
+ // active for its own `point`, based on an enum config key. Require it references
3092
+ // an enum key in cap.config whose values include THIS step's own point — otherwise
3093
+ // the step could never activate (a silently-dead step).
3094
+ for (const step of cap.steps) {
3095
+ if (step.pointFrom !== undefined) {
3096
+ if (typeof step.pointFrom !== 'string') continue; // already reported above
3097
+ const slice = typeof cap.config === 'object' && cap.config !== null ? cap.config[step.pointFrom] : undefined;
3098
+ if (!slice || typeof slice !== 'object') {
3099
+ errors.push(
3100
+ prefix + ' step.pointFrom "' + step.pointFrom + '" is not defined in capability config keys',
3101
+ );
3102
+ } else if (slice.type !== 'enum') {
3103
+ errors.push(
3104
+ prefix + ' step.pointFrom "' + step.pointFrom + '" must reference an enum config key (got type: ' + slice.type + ')',
3105
+ );
3106
+ } else if (!Array.isArray(slice.values) || !slice.values.includes(step.point)) {
3107
+ errors.push(
3108
+ prefix + ' step.pointFrom "' + step.pointFrom + '" enum values do not include this step\'s own point "' +
3109
+ step.point + '" — the step could never activate',
3110
+ );
3111
+ }
3112
+ }
3113
+ }
3114
+
2873
3115
  for (const contrib of cap.contributions) {
2874
3116
  if (contrib.when !== undefined) {
2875
3117
  if (typeof contrib.when !== 'string') continue;
@@ -3174,6 +3416,11 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
3174
3416
  const laneSlugClaims = new Map(); // slug → capId[]
3175
3417
  const laneFlagClaims = new Map(); // flag → capId[]
3176
3418
  const laneSectionClaims = new Map(); // reviewsSection → capId[]
3419
+ // ADR-3646: task-content resolver tracker-prefix uniqueness across the
3420
+ // MERGED first-party ∪ overlay set, mirroring the reviewer-lane collision
3421
+ // pattern above — two resolvers claiming the same prefix would make
3422
+ // `execute:task` dispatch ambiguous (which capability's resolver runs?).
3423
+ const trackerPrefixClaims = new Map(); // trackerPrefix → capId[]
3177
3424
 
3178
3425
  // Claims are ACCUMULATED and reported after the sweep, never reported on the
3179
3426
  // second claimant. Reporting pairwise-on-collision looks equivalent and is not:
@@ -3194,6 +3441,15 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
3194
3441
  };
3195
3442
 
3196
3443
  for (const [capId, cap] of capMap) {
3444
+ // ADR-3646: a MALFORMED taskContentResolver body was already reported by
3445
+ // validateCapability — do not double-report; only claim well-shaped
3446
+ // bodies. Independent of the reviewer-lane checks below, so it runs even
3447
+ // for capabilities that carry no `reviewer` body at all.
3448
+ const tcr = cap.taskContentResolver;
3449
+ if (typeof tcr === 'object' && tcr !== null && !Array.isArray(tcr)) {
3450
+ claim(trackerPrefixClaims, tcr.trackerPrefix, capId);
3451
+ }
3452
+
3197
3453
  const r = cap.reviewer;
3198
3454
  // A capability with no lane contributes to no uniqueness set. A MALFORMED
3199
3455
  // body was already reported by validateCapability — do not double-report.
@@ -3215,6 +3471,7 @@ function validateCrossCapability(capMap, centralKeys, centralPatterns = []) {
3215
3471
  [laneSlugClaims, 'slug'],
3216
3472
  [laneFlagClaims, 'flag'],
3217
3473
  [laneSectionClaims, 'reviewsSection'],
3474
+ [trackerPrefixClaims, 'taskContentResolver.trackerPrefix'],
3218
3475
  ]) {
3219
3476
  for (const [key, claimants] of claims) {
3220
3477
  if (claimants.length < 2) continue;
@@ -3435,26 +3692,56 @@ function topoSortContributions(entries) {
3435
3692
 
3436
3693
  // ─── Gen-time wired guard ─────────────────────────────────────────────────────
3437
3694
 
3695
+ /**
3696
+ * Hook group (capability.json array name) → hook kind (dispatch discriminator).
3697
+ * Single source of truth for both validateHooksWired and the scanner-parity
3698
+ * test in tests/capability-registry.test.cjs (#3606).
3699
+ */
3700
+ const HOOK_GROUP_KINDS = Object.freeze({
3701
+ steps: 'step',
3702
+ contributions: 'contribution',
3703
+ gates: 'gate',
3704
+ });
3705
+
3438
3706
  /**
3439
3707
  * Validate that every hook point declared by a capability has a corresponding
3440
- * `loop render-hooks <point>` call site in one of the host-loop workflow files.
3708
+ * `loop render-hooks <point>` call site in one of the host-loop workflow files,
3709
+ * AND — #3606 — that the call site's dispatch text covers the hook's KIND.
3710
+ *
3711
+ * A call site proves hooks are rendered, not dispatched: a consumer that
3712
+ * iterates only `kind == "gate"` (or narrows `kind == "step"` to one
3713
+ * `ref.skill`) silently drops every other registered kind — a capability can
3714
+ * be wired, enabled, resolved active, and never run. See
3715
+ * gsd-core/references/loop-hook-dispatch.md ("A point whose workflow
3716
+ * hand-rolls one kind does not implement this contract").
3441
3717
  *
3442
3718
  * Only valid loop points (in VALID_LOOP_POINTS) are checked here. Invalid points
3443
3719
  * are already caught by validateStep/validateContribution/validateGate — do not
3444
3720
  * double-report.
3445
3721
  *
3722
+ * KNOWN LIMITATION (#3606): coverage is the UNION across all call sites for a
3723
+ * point in HOST_LOOP_FILES. Quick's plan:pre planner contribution seam has an
3724
+ * additional generator-owned per-file check. Other consumers outside that
3725
+ * universe (autonomous.md, code-review*.md, audit-milestone.md,
3726
+ * secure-phase.md, validate-phase.md) are not per-file checked — a narrowed
3727
+ * consumer there passes as long as one host file covers the point. More
3728
+ * per-file coverage maps are the tightening path.
3729
+ *
3446
3730
  * @param {object} cap Validated capability object.
3447
- * @param {Set<string>} wiredSet Set of points that have call sites in host workflows.
3448
- * @returns {string[]} Array of error strings; empty means all points are wired.
3731
+ * @param {Map<string, Set<string>>} wiredKinds Per point, the hook kinds the
3732
+ * host workflows' call-site dispatch text covers (getWiredKinds). A point
3733
+ * absent from the map is unwired.
3734
+ * @returns {string[]} Array of error strings; empty means all points
3735
+ * are wired and every registered kind is covered.
3449
3736
  */
3450
- function validateHooksWired(cap, wiredSet) {
3737
+ function validateHooksWired(cap, wiredKinds) {
3451
3738
  const errors = [];
3452
3739
  const capId = cap.id || '(unknown)';
3453
3740
 
3454
- function checkPoint(point, groupName, idx) {
3741
+ function checkPoint(point, groupName, kind, idx) {
3455
3742
  // Only flag valid points that are unwired — invalid points are schema-validator's job.
3456
3743
  if (!VALID_LOOP_POINTS.has(point)) return;
3457
- if (!wiredSet.has(point)) {
3744
+ if (!wiredKinds.has(point)) {
3458
3745
  errors.push(
3459
3746
  'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
3460
3747
  '" is declared but not wired in any host-loop workflow ' +
@@ -3462,20 +3749,34 @@ function validateHooksWired(cap, wiredSet) {
3462
3749
  'Wire the call site in the host workflow ' +
3463
3750
  '(see scripts/gen-loop-host-contract.cjs STEP_WORKFLOWS) or remove the hook.',
3464
3751
  );
3752
+ return;
3753
+ }
3754
+ const covered = wiredKinds.get(point);
3755
+ if (covered.size === 0) {
3756
+ errors.push(
3757
+ 'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
3758
+ '" has `loop render-hooks ' + point + '` call site(s), but their dispatch text covers NO ' +
3759
+ 'hook kind — every consumer is narrowed to specific hooks. Dispatch every registered kind ' +
3760
+ 'per gsd-core/references/loop-hook-dispatch.md.',
3761
+ );
3762
+ return;
3763
+ }
3764
+ if (!covered.has(kind)) {
3765
+ errors.push(
3766
+ 'capability "' + capId + '" ' + groupName + '[' + idx + '].point "' + point +
3767
+ '" registers a ' + kind + ' hook, but the host call site\'s dispatch text never ' +
3768
+ 'covers `kind == "' + kind + '"` (it covers: ' + [...covered].sort().join(', ') + '). ' +
3769
+ 'A hand-rolled single-kind consumer silently never dispatches the other kinds — ' +
3770
+ 'dispatch every registered kind per gsd-core/references/loop-hook-dispatch.md.',
3771
+ );
3465
3772
  }
3466
3773
  }
3467
3774
 
3468
- for (let i = 0; i < (cap.steps || []).length; i++) {
3469
- const hook = cap.steps[i];
3470
- if (hook.point !== undefined) checkPoint(hook.point, 'steps', i);
3471
- }
3472
- for (let i = 0; i < (cap.contributions || []).length; i++) {
3473
- const hook = cap.contributions[i];
3474
- if (hook.point !== undefined) checkPoint(hook.point, 'contributions', i);
3475
- }
3476
- for (let i = 0; i < (cap.gates || []).length; i++) {
3477
- const hook = cap.gates[i];
3478
- if (hook.point !== undefined) checkPoint(hook.point, 'gates', i);
3775
+ for (const [group, kind] of Object.entries(HOOK_GROUP_KINDS)) {
3776
+ for (let i = 0; i < (cap[group] || []).length; i++) {
3777
+ const hook = cap[group][i];
3778
+ if (hook.point !== undefined) checkPoint(hook.point, group, kind, i);
3779
+ }
3479
3780
  }
3480
3781
 
3481
3782
  return errors;
@@ -3681,6 +3982,7 @@ module.exports = {
3681
3982
  validateCommandEntry,
3682
3983
  validateRuntimeCompat,
3683
3984
  validateFeatureBody,
3985
+ validateTaskContentResolver,
3684
3986
  validateConfigHome,
3685
3987
  validateArtifactKindEntry,
3686
3988
  validateArtifactLayout,
@@ -3699,6 +4001,7 @@ module.exports = {
3699
4001
  topoSortSteps,
3700
4002
  topoSortContributions,
3701
4003
  validateHooksWired,
4004
+ HOOK_GROUP_KINDS,
3702
4005
  validateConfigSliceEntry,
3703
4006
  classifyCrossErrors,
3704
4007
  runConfigFormatParityGate,
@@ -184,6 +184,7 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
184
184
  };
185
185
  }
186
186
  // ── Step 4: APPLY PASS ────────────────────────────────────────────────────
187
+ let pendingSurfaceForCommit;
187
188
  // ── Surface writes ────────────────────────────────────────────────────────
188
189
  if (needsSurface && (idsToDisable.length > 0 || idsToEnable.length > 0)) {
189
190
  const existing = readSurface(resolvedConfigDir);
@@ -242,13 +243,19 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
242
243
  }
243
244
  }
244
245
  if (surfaceChanged) {
245
- writeSurface(resolvedConfigDir, pendingSurface);
246
+ pendingSurfaceForCommit = pendingSurface;
246
247
  }
247
248
  }
248
249
  // ── Config writes (once, batched) ─────────────────────────────────────────
249
250
  if (pendingGateWrites.length > 0) {
250
251
  setConfigValues(cwd, pendingGateWrites);
251
252
  }
253
+ // State-only callers preserve the existing direct-write behavior. A caller
254
+ // that requests materialization lets applySurface publish the candidate only
255
+ // after every artifact kind has staged and synchronized successfully.
256
+ if (pendingSurfaceForCommit && !opts?.materialize) {
257
+ writeSurface(resolvedConfigDir, pendingSurfaceForCommit);
258
+ }
252
259
  // ── Materialize (optional) ────────────────────────────────────────────────
253
260
  if (opts?.materialize) {
254
261
  const { runtime, scope } = opts.materialize;
@@ -274,9 +281,12 @@ function setCapabilityState(cwd, runtimeConfigDir, desired, opts) {
274
281
  // folded:issue-1575-agent-descriptor-parity describe block in
275
282
  // tests/golden-parity-single-source.test.cjs).
276
283
  // eslint-disable-next-line @typescript-eslint/no-unsafe-argument
277
- applySurface(resolvedConfigDir, layout, manifest, undefined, registry, opts?.materialize?.resolveAttribution
278
- ? { resolveAttribution: opts.materialize.resolveAttribution }
279
- : undefined);
284
+ applySurface(resolvedConfigDir, layout, manifest, undefined, registry, {
285
+ ...(pendingSurfaceForCommit ? { surfaceState: pendingSurfaceForCommit } : {}),
286
+ ...(opts.materialize.resolveAttribution
287
+ ? { resolveAttribution: opts.materialize.resolveAttribution }
288
+ : {}),
289
+ });
280
290
  }
281
291
  catch (err) {
282
292
  const msg = err instanceof Error ? err.message : String(err);