@opengsd/gsd-core 1.9.1 → 1.11.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 (426) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +2 -3
  3. package/.opencode/plugins/gsd-core.js +8 -1
  4. package/agents/gsd-code-fixer.md +27 -3
  5. package/agents/gsd-debug-session-manager.md +11 -0
  6. package/agents/gsd-debugger.md +12 -246
  7. package/agents/gsd-doc-synthesizer.md +2 -4
  8. package/agents/gsd-executor.md +12 -10
  9. package/agents/gsd-integration-checker.md +3 -0
  10. package/agents/gsd-mempalace-curator.md +5 -2
  11. package/agents/gsd-phase-researcher.md +20 -1
  12. package/agents/gsd-plan-checker.md +46 -0
  13. package/agents/gsd-planner.md +49 -54
  14. package/agents/gsd-roadmapper.md +21 -3
  15. package/agents/gsd-user-profiler.md +3 -0
  16. package/agents/gsd-verifier.md +26 -73
  17. package/bin/install.js +1272 -1238
  18. package/bin/lib/ui-safety-gate.cjs +2 -0
  19. package/commands/gsd/code-review.md +1 -1
  20. package/commands/gsd/execute-phase.md +1 -1
  21. package/commands/gsd/map-codebase.md +1 -1
  22. package/commands/gsd/mempalace-capture.md +2 -2
  23. package/commands/gsd/mempalace-recall.md +1 -1
  24. package/commands/gsd/new-milestone.md +2 -2
  25. package/commands/gsd/plan-phase.md +1 -1
  26. package/commands/gsd/quick.md +1 -1
  27. package/commands/gsd/review-backlog.md +2 -1
  28. package/commands/gsd/verify-work.md +1 -1
  29. package/gsd-core/bin/gsd-tools.cjs +1009 -115
  30. package/gsd-core/bin/lib/active-workstream-store.cjs +153 -12
  31. package/gsd-core/bin/lib/agent-install-check.cjs +268 -38
  32. package/gsd-core/bin/lib/api-coverage.cjs +123 -5
  33. package/gsd-core/bin/lib/artifacts.cjs +3 -0
  34. package/gsd-core/bin/lib/assumption-delta.cjs +2 -4
  35. package/gsd-core/bin/lib/audit-command-router.cjs +9 -2
  36. package/gsd-core/bin/lib/audit.cjs +926 -202
  37. package/gsd-core/bin/lib/broken-windows.cjs +36 -6
  38. package/gsd-core/bin/lib/capability-consent.cjs +149 -15
  39. package/gsd-core/bin/lib/capability-lifecycle.cjs +45 -0
  40. package/gsd-core/bin/lib/capability-registry.cjs +608 -148
  41. package/gsd-core/bin/lib/capability-source.cjs +92 -0
  42. package/gsd-core/bin/lib/capability-trust.cjs +444 -25
  43. package/gsd-core/bin/lib/capability-validator.cjs +507 -24
  44. package/gsd-core/bin/lib/capability-writer.cjs +3 -2
  45. package/gsd-core/bin/lib/check-command-router.cjs +114 -38
  46. package/gsd-core/bin/lib/claude-orchestration.cjs +56 -3
  47. package/gsd-core/bin/lib/codex-agent-toml.cjs +329 -0
  48. package/gsd-core/bin/lib/command-aliases.cjs +94 -0
  49. package/gsd-core/bin/lib/command-roster.cjs +44 -1
  50. package/gsd-core/bin/lib/commands.cjs +665 -99
  51. package/gsd-core/bin/lib/commonjs-marker.cjs +142 -0
  52. package/gsd-core/bin/lib/complexity-trigger.cjs +1172 -0
  53. package/gsd-core/bin/lib/config-loader.cjs +76 -0
  54. package/gsd-core/bin/lib/config.cjs +22 -2
  55. package/gsd-core/bin/lib/context-composer.cjs +278 -0
  56. package/gsd-core/bin/lib/context-predicates.cjs +506 -0
  57. package/gsd-core/bin/lib/core-utils.cjs +217 -40
  58. package/gsd-core/bin/lib/decisions.cjs +23 -0
  59. package/gsd-core/bin/lib/docs.cjs +3 -2
  60. package/gsd-core/bin/lib/external-job.cjs +19 -4
  61. package/gsd-core/bin/lib/fallow-runner.cjs +20 -44
  62. package/gsd-core/bin/lib/frontmatter.cjs +239 -32
  63. package/gsd-core/bin/lib/gap-checker.cjs +68 -7
  64. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
  65. package/gsd-core/bin/lib/git-base-branch.cjs +160 -15
  66. package/gsd-core/bin/lib/graphify.cjs +142 -27
  67. package/gsd-core/bin/lib/gsd2-import.cjs +37 -5
  68. package/gsd-core/bin/lib/health-diagnostic-rules/agent-install.cjs +101 -0
  69. package/gsd-core/bin/lib/health-diagnostic-rules/config-validation.cjs +348 -0
  70. package/gsd-core/bin/lib/health-diagnostic-rules/consistency.cjs +145 -0
  71. package/gsd-core/bin/lib/health-diagnostic-rules/install-surface-shadowing.cjs +98 -0
  72. package/gsd-core/bin/lib/health-diagnostic-rules/milestone-archive-hygiene.cjs +100 -0
  73. package/gsd-core/bin/lib/health-diagnostic-rules/phase-structure.cjs +222 -0
  74. package/gsd-core/bin/lib/health-diagnostic-rules/roadmap-disk-consistency.cjs +265 -0
  75. package/gsd-core/bin/lib/health-diagnostic-rules/root-existence.cjs +161 -0
  76. package/gsd-core/bin/lib/health-diagnostic-rules/state-consistency.cjs +303 -0
  77. package/gsd-core/bin/lib/health-diagnostic-rules/worktree-health.cjs +173 -0
  78. package/gsd-core/bin/lib/health-diagnostic-types.cjs +68 -0
  79. package/gsd-core/bin/lib/health-diagnostic.cjs +431 -0
  80. package/gsd-core/bin/lib/host-integration.cjs +13 -1
  81. package/gsd-core/bin/lib/host-runtime-detection.cjs +134 -0
  82. package/gsd-core/bin/lib/init-command-router.cjs +83 -8
  83. package/gsd-core/bin/lib/init.cjs +1325 -169
  84. package/gsd-core/bin/lib/install-effort-resolver.cjs +73 -30
  85. package/gsd-core/bin/lib/install-engine.cjs +805 -264
  86. package/gsd-core/bin/lib/install-fs-adapter.cjs +262 -0
  87. package/gsd-core/bin/lib/install-model-override-resolver.cjs +203 -0
  88. package/gsd-core/bin/lib/install-profiles.cjs +160 -57
  89. package/gsd-core/bin/lib/install-scope.cjs +270 -0
  90. package/gsd-core/bin/lib/install-shadow-report.cjs +385 -0
  91. package/gsd-core/bin/lib/installed-surface-resolver.cjs +381 -0
  92. package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
  93. package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
  94. package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
  95. package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
  96. package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
  97. package/gsd-core/bin/lib/installer-migrations.cjs +206 -13
  98. package/gsd-core/bin/lib/io.cjs +38 -3
  99. package/gsd-core/bin/lib/markdown-sectionizer.cjs +8 -1
  100. package/gsd-core/bin/lib/markdown-table.cjs +133 -20
  101. package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
  102. package/gsd-core/bin/lib/mcp-server.cjs +135 -3
  103. package/gsd-core/bin/lib/milestone-lock.cjs +248 -0
  104. package/gsd-core/bin/lib/milestone.cjs +821 -109
  105. package/gsd-core/bin/lib/model-catalog.cjs +59 -1
  106. package/gsd-core/bin/lib/model-resolver.cjs +183 -40
  107. package/gsd-core/bin/lib/normalize-test-command.cjs +1 -1
  108. package/gsd-core/bin/lib/pattern.cjs +122 -0
  109. package/gsd-core/bin/lib/phase-estimation.cjs +1 -1
  110. package/gsd-core/bin/lib/phase-id.cjs +507 -36
  111. package/gsd-core/bin/lib/phase-lifecycle.cjs +28 -3
  112. package/gsd-core/bin/lib/phase-locator.cjs +258 -58
  113. package/gsd-core/bin/lib/phase.cjs +891 -156
  114. package/gsd-core/bin/lib/plan-dependency-graph.cjs +303 -0
  115. package/gsd-core/bin/lib/plan-drift-guard.cjs +120 -0
  116. package/gsd-core/bin/lib/plan-scan.cjs +86 -2
  117. package/gsd-core/bin/lib/planning-scope.cjs +31 -0
  118. package/gsd-core/bin/lib/planning-snapshot.cjs +890 -0
  119. package/gsd-core/bin/lib/planning-workspace.cjs +60 -6
  120. package/gsd-core/bin/lib/probe-core.cjs +1 -1
  121. package/gsd-core/bin/lib/profile-output.cjs +1 -1
  122. package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
  123. package/gsd-core/bin/lib/refactor-trigger-command-router.cjs +740 -0
  124. package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +85 -0
  125. package/gsd-core/bin/lib/review-lane-descriptor.cjs +108 -0
  126. package/gsd-core/bin/lib/review-lane-invocation.cjs +30 -0
  127. package/gsd-core/bin/lib/review-lane-runner.cjs +447 -68
  128. package/gsd-core/bin/lib/review-reviewer-selection.cjs +13 -18
  129. package/gsd-core/bin/lib/roadmap-command-router.cjs +76 -9
  130. package/gsd-core/bin/lib/roadmap-parser.cjs +1035 -194
  131. package/gsd-core/bin/lib/roadmap-upgrade.cjs +37 -10
  132. package/gsd-core/bin/lib/roadmap.cjs +405 -84
  133. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +795 -100
  134. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +14 -2
  135. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +440 -57
  136. package/gsd-core/bin/lib/runtime-config-adapter-registry.cjs +3 -2
  137. package/gsd-core/bin/lib/runtime-homes.cjs +220 -41
  138. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +220 -44
  139. package/gsd-core/bin/lib/runtime-name-policy.cjs +3 -1
  140. package/gsd-core/bin/lib/runtime-slash.cjs +27 -9
  141. package/gsd-core/bin/lib/section-manifest.cjs +209 -0
  142. package/gsd-core/bin/lib/security.cjs +104 -5
  143. package/gsd-core/bin/lib/shell-command-projection.cjs +388 -30
  144. package/gsd-core/bin/lib/smart-entry.cjs +154 -22
  145. package/gsd-core/bin/lib/state-command-router.cjs +5 -1
  146. package/gsd-core/bin/lib/state-document.cjs +152 -8
  147. package/gsd-core/bin/lib/state-transition.cjs +424 -105
  148. package/gsd-core/bin/lib/state.cjs +1927 -401
  149. package/gsd-core/bin/lib/surface.cjs +35 -10
  150. package/gsd-core/bin/lib/text-lines.cjs +80 -0
  151. package/gsd-core/bin/lib/token-scanner.cjs +76 -0
  152. package/gsd-core/bin/lib/uat-predicate.cjs +20 -4
  153. package/gsd-core/bin/lib/uat.cjs +706 -64
  154. package/gsd-core/bin/lib/ui-frontend-evidence.cjs +157 -0
  155. package/gsd-core/bin/lib/ui-safety-gate.cjs +14 -5
  156. package/gsd-core/bin/lib/unusable-input.cjs +33 -0
  157. package/gsd-core/bin/lib/update-context.cjs +8 -2
  158. package/gsd-core/bin/lib/user-artifact-staging.cjs +705 -0
  159. package/gsd-core/bin/lib/validate.cjs +20 -6
  160. package/gsd-core/bin/lib/vendor/README.md +37 -0
  161. package/gsd-core/bin/lib/vendor/re2js.cjs +6480 -0
  162. package/gsd-core/bin/lib/vendor/re2js.d.cts +938 -0
  163. package/gsd-core/bin/lib/verification-command-router.cjs +2 -1
  164. package/gsd-core/bin/lib/verification.cjs +287 -20
  165. package/gsd-core/bin/lib/verify.cjs +368 -880
  166. package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
  167. package/gsd-core/bin/lib/workstream-inventory-builder.cjs +203 -19
  168. package/gsd-core/bin/lib/workstream-inventory.cjs +576 -31
  169. package/gsd-core/bin/lib/workstream.cjs +8 -2
  170. package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
  171. package/gsd-core/bin/lib/worktree-safety.cjs +450 -125
  172. package/gsd-core/bin/shared/config-defaults.manifest.json +1 -0
  173. package/gsd-core/bin/shared/config-schema.manifest.json +9 -1
  174. package/gsd-core/references/agent-contracts.md +43 -26
  175. package/gsd-core/references/artifact-types.md +10 -3
  176. package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
  177. package/gsd-core/references/checkpoints.md +2 -2
  178. package/gsd-core/references/context-budget.md +1 -1
  179. package/gsd-core/references/debugger-techniques.md +255 -0
  180. package/gsd-core/references/dispatch-isolation-gate.md +138 -0
  181. package/gsd-core/references/doc-conflict-engine.md +1 -1
  182. package/gsd-core/references/execute-mvp-tdd.md +3 -3
  183. package/gsd-core/references/execute-phase-between-wave-reset.md +6 -2
  184. package/gsd-core/references/execute-phase-context-guard.md +1 -1
  185. package/gsd-core/references/execute-phase-response-language.md +1 -1
  186. package/gsd-core/references/execute-phase-wave-guard.md +6 -2
  187. package/gsd-core/references/gate-prompts.md +1 -1
  188. package/gsd-core/references/git-planning-commit.md +2 -1
  189. package/gsd-core/references/loop-hook-dispatch.md +39 -2
  190. package/gsd-core/references/model-profiles.md +12 -4
  191. package/gsd-core/references/mvp-concepts.md +9 -9
  192. package/gsd-core/references/planner-guidance.md +3 -9
  193. package/gsd-core/references/planner-preconditions.md +1 -1
  194. package/gsd-core/references/planner-reviews.md +1 -1
  195. package/gsd-core/references/planning-config.md +8 -6
  196. package/gsd-core/references/research-documentation-lookup.md +5 -3
  197. package/gsd-core/references/revision-loop.md +1 -1
  198. package/gsd-core/references/specless-probe-fallback.md +8 -7
  199. package/gsd-core/references/universal-anti-patterns.md +3 -3
  200. package/gsd-core/references/verifier-phase-gates.md +192 -0
  201. package/gsd-core/references/verifier-wiring-patterns.md +100 -0
  202. package/gsd-core/references/verify-mvp-mode.md +1 -1
  203. package/gsd-core/references/workstream-flag.md +22 -6
  204. package/gsd-core/references/worktree-branch-check.md +2 -2
  205. package/gsd-core/templates/discussion-log.md +1 -1
  206. package/gsd-core/templates/phase-prompt.md +2 -4
  207. package/gsd-core/templates/state.md +4 -4
  208. package/gsd-core/templates/summary-complex.md +2 -0
  209. package/gsd-core/templates/summary-minimal.md +2 -0
  210. package/gsd-core/templates/summary-standard.md +2 -0
  211. package/gsd-core/templates/summary.md +2 -0
  212. package/gsd-core/templates/verification-report.md +9 -1
  213. package/gsd-core/workflows/ai-integration-phase.md +9 -11
  214. package/gsd-core/workflows/audit-milestone.md +3 -0
  215. package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
  216. package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
  217. package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
  218. package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
  219. package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
  220. package/gsd-core/workflows/autonomous.md +33 -70
  221. package/gsd-core/workflows/cleanup.md +62 -3
  222. package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
  223. package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +93 -0
  224. package/gsd-core/workflows/code-review-fix.md +37 -10
  225. package/gsd-core/workflows/code-review.md +74 -166
  226. package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
  227. package/gsd-core/workflows/complete-milestone.md +160 -95
  228. package/gsd-core/workflows/debug.md +16 -17
  229. package/gsd-core/workflows/diagnose-issues.md +56 -8
  230. package/gsd-core/workflows/discuss-phase/modes/chain.md +2 -1
  231. package/gsd-core/workflows/discuss-phase/modes/default.md +1 -1
  232. package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
  233. package/gsd-core/workflows/discuss-phase-assumptions.md +7 -17
  234. package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
  235. package/gsd-core/workflows/docs-update.md +8 -51
  236. package/gsd-core/workflows/edit-phase.md +26 -1
  237. package/gsd-core/workflows/eval-review.md +3 -5
  238. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +64 -7
  239. package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
  240. package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
  241. package/gsd-core/workflows/execute-phase/steps/per-plan-executor-routing.md +77 -0
  242. package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +21 -0
  243. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
  244. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
  245. package/gsd-core/workflows/execute-phase.md +103 -187
  246. package/gsd-core/workflows/execute-plan.md +36 -4
  247. package/gsd-core/workflows/explore.md +131 -4
  248. package/gsd-core/workflows/fast.md +10 -2
  249. package/gsd-core/workflows/health.md +73 -4
  250. package/gsd-core/workflows/help/modes/full.md +6 -1
  251. package/gsd-core/workflows/import.md +4 -4
  252. package/gsd-core/workflows/ingest-docs.md +7 -6
  253. package/gsd-core/workflows/mvp-phase.md +6 -3
  254. package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
  255. package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
  256. package/gsd-core/workflows/new-milestone.md +35 -47
  257. package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
  258. package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
  259. package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
  260. package/gsd-core/workflows/new-project.md +27 -240
  261. package/gsd-core/workflows/next.md +12 -0
  262. package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
  263. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
  264. package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
  265. package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
  266. package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
  267. package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
  268. package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
  269. package/gsd-core/workflows/plan-phase.md +89 -209
  270. package/gsd-core/workflows/plan-review-convergence.md +50 -2
  271. package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
  272. package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
  273. package/gsd-core/workflows/progress.md +45 -159
  274. package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
  275. package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
  276. package/gsd-core/workflows/quick/steps/quick-verification.md +67 -0
  277. package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
  278. package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
  279. package/gsd-core/workflows/quick.md +55 -405
  280. package/gsd-core/workflows/resume-project.md +3 -0
  281. package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
  282. package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
  283. package/gsd-core/workflows/review.md +41 -13
  284. package/gsd-core/workflows/section-manifest.json +219 -0
  285. package/gsd-core/workflows/secure-phase.md +1 -1
  286. package/gsd-core/workflows/session-report.md +2 -1
  287. package/gsd-core/workflows/settings.md +66 -2
  288. package/gsd-core/workflows/ship.md +104 -44
  289. package/gsd-core/workflows/sketch.md +1 -1
  290. package/gsd-core/workflows/spec-phase.md +41 -20
  291. package/gsd-core/workflows/spike-wrap-up.md +20 -5
  292. package/gsd-core/workflows/spike.md +50 -16
  293. package/gsd-core/workflows/sync-skills.md +106 -13
  294. package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
  295. package/gsd-core/workflows/transition.md +53 -31
  296. package/gsd-core/workflows/ui-phase.md +13 -12
  297. package/gsd-core/workflows/ui-review.md +2 -2
  298. package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
  299. package/gsd-core/workflows/update.md +19 -8
  300. package/gsd-core/workflows/validate-phase.md +1 -1
  301. package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
  302. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
  303. package/gsd-core/workflows/verify-work.md +17 -65
  304. package/hooks/dist/gsd-agent-isolation-guard.js +517 -0
  305. package/hooks/dist/gsd-check-update-worker.js +64 -12
  306. package/hooks/dist/gsd-check-update.js +19 -1
  307. package/hooks/dist/gsd-cursor-pre-tool.js +0 -3
  308. package/hooks/dist/gsd-cursor-subagent-start.js +607 -26
  309. package/hooks/dist/gsd-cursor-subagent-stop.js +3 -2
  310. package/hooks/dist/gsd-prompt-guard.js +21 -20
  311. package/hooks/dist/gsd-read-injection-scanner.js +45 -24
  312. package/hooks/dist/gsd-statusline.js +90 -6
  313. package/hooks/dist/gsd-update-banner.js +22 -1
  314. package/hooks/dist/gsd-workflow-guard.js +134 -36
  315. package/hooks/dist/gsd-worktree-path-guard.js +2 -1
  316. package/hooks/dist/gsd-write-guard.js +359 -0
  317. package/hooks/dist/lib/git-cmd.js +92 -59
  318. package/hooks/dist/lib/injection-patterns.js +45 -0
  319. package/hooks/dist/lib/isolation-deny-reason.js +39 -0
  320. package/hooks/dist/lib/isolation-sentinel.js +277 -0
  321. package/hooks/dist/managed-hooks-registry.cjs +2 -0
  322. package/hooks/gsd-agent-isolation-guard.js +517 -0
  323. package/hooks/gsd-check-update-worker.js +64 -12
  324. package/hooks/gsd-check-update.js +19 -1
  325. package/hooks/gsd-cursor-pre-tool.js +0 -3
  326. package/hooks/gsd-cursor-subagent-start.js +607 -26
  327. package/hooks/gsd-cursor-subagent-stop.js +3 -2
  328. package/hooks/gsd-prompt-guard.js +21 -20
  329. package/hooks/gsd-read-injection-scanner.js +45 -24
  330. package/hooks/gsd-statusline.js +90 -6
  331. package/hooks/gsd-update-banner.js +22 -1
  332. package/hooks/gsd-workflow-guard.js +134 -36
  333. package/hooks/gsd-worktree-path-guard.js +2 -1
  334. package/hooks/gsd-write-guard.js +359 -0
  335. package/hooks/hooks.json +12 -0
  336. package/hooks/lib/git-cmd.js +92 -59
  337. package/hooks/lib/injection-patterns.js +45 -0
  338. package/hooks/lib/isolation-deny-reason.js +39 -0
  339. package/hooks/lib/isolation-sentinel.js +277 -0
  340. package/hooks/managed-hooks-registry.cjs +2 -0
  341. package/package.json +31 -10
  342. package/pi/gsd.cjs +71 -12
  343. package/scripts/baselines/planning-prompt-drift-baseline.json +4 -0
  344. package/scripts/baselines/planning-snapshot-bypass-baseline.json +12 -0
  345. package/scripts/baselines/unreachable-guard-drift-baseline.json +4 -0
  346. package/scripts/build-hooks.js +9 -0
  347. package/scripts/changeset/lint.cjs +68 -6
  348. package/scripts/changeset/serialize.cjs +5 -1
  349. package/scripts/check-alias-drift.cjs +7 -43
  350. package/scripts/check-contract-drift.cjs +297 -0
  351. package/scripts/ci-test-scope.cjs +19 -2
  352. package/scripts/command-contract-helpers.cjs +903 -1
  353. package/scripts/gen-adr-index.cjs +728 -38
  354. package/scripts/gen-capability-matrix.cjs +1 -1
  355. package/scripts/gen-capability-registry.cjs +3 -15
  356. package/scripts/gen-context-index.cjs +439 -0
  357. package/scripts/gen-health-docs.cjs +390 -0
  358. package/scripts/gen-inventory-manifest.cjs +150 -4
  359. package/scripts/gen-loop-host-contract.cjs +4 -24
  360. package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
  361. package/scripts/gen-registry.cjs +3 -14
  362. package/scripts/gen-section-manifest.cjs +638 -0
  363. package/scripts/generate-package-identity.cjs +4 -2
  364. package/scripts/lib/alias-drift-families.cjs +46 -0
  365. package/scripts/lib/drift-scan.cjs +278 -0
  366. package/scripts/lint-allow-test-rule-refs.allowlist.json +15 -54
  367. package/scripts/lint-allow-test-rule-refs.effective-ceiling.json +4 -0
  368. package/scripts/lint-allow-test-rule-refs.unverified-ceiling.json +3 -0
  369. package/scripts/lint-canary-version-leak.cjs +73 -0
  370. package/scripts/lint-command-contract.cjs +96 -13
  371. package/scripts/lint-compiled-artifact-sync.cjs +6 -1
  372. package/scripts/lint-completion-predicate-drift.cjs +933 -0
  373. package/scripts/lint-completion-ratio-drift.cjs +214 -0
  374. package/scripts/lint-default-flip-documentation.cjs +193 -0
  375. package/scripts/lint-docs-command-form.cjs +195 -0
  376. package/scripts/lint-docs-required.cjs +9 -1
  377. package/scripts/lint-emitted-drift-ack.cjs +215 -20
  378. package/scripts/lint-eslint-glob-coverage.allowlist.json +34 -0
  379. package/scripts/lint-eslint-glob-coverage.cjs +340 -0
  380. package/scripts/lint-example-parser-parity.cjs +395 -0
  381. package/scripts/lint-frontmatter-scalar-broad-grep.cjs +237 -0
  382. package/scripts/lint-health-diagnostic-rule-table.cjs +404 -0
  383. package/scripts/lint-hooks-runtime-build-seam.cjs +262 -0
  384. package/scripts/lint-milestone-window-drift.cjs +468 -0
  385. package/scripts/lint-phase-enumeration-drift.cjs +479 -0
  386. package/scripts/lint-plan-count-drift.cjs +318 -0
  387. package/scripts/lint-planning-artifact-writer-drift.cjs +398 -0
  388. package/scripts/lint-planning-prompt-drift.cjs +434 -0
  389. package/scripts/lint-planning-snapshot-bypass-drift.cjs +544 -0
  390. package/scripts/lint-regression-test-names.cjs +15 -13
  391. package/scripts/lint-removed-but-needed.cjs +320 -0
  392. package/scripts/lint-state-field-drift.cjs +805 -0
  393. package/scripts/lint-state-write-path-drift.cjs +1045 -0
  394. package/scripts/lint-test-file-count.allowlist.json +40 -3
  395. package/scripts/lint-unreachable-guard-drift.cjs +843 -0
  396. package/scripts/lint-vendored-deps.cjs +124 -0
  397. package/scripts/mutation-matrix.cjs +13 -0
  398. package/scripts/pr-changed-files.cjs +63 -0
  399. package/scripts/pr-template-policy.cjs +14 -4
  400. package/scripts/prompt-injection-scan.sh +52 -6
  401. package/scripts/require-issue-link-policy.cjs +192 -0
  402. package/scripts/state-write-path-drift-baseline.json +19 -0
  403. package/scripts/sync-runtime-launcher.cjs +2 -4
  404. package/skills/gsd-autonomous/SKILL.md +0 -1
  405. package/skills/gsd-code-review/SKILL.md +1 -1
  406. package/skills/gsd-execute-phase/SKILL.md +1 -2
  407. package/skills/gsd-map-codebase/SKILL.md +1 -1
  408. package/skills/gsd-mempalace-capture/SKILL.md +2 -2
  409. package/skills/gsd-mempalace-recall/SKILL.md +1 -1
  410. package/skills/gsd-new-milestone/SKILL.md +2 -2
  411. package/skills/gsd-next/SKILL.md +0 -1
  412. package/skills/gsd-plan-phase/SKILL.md +1 -2
  413. package/skills/gsd-progress/SKILL.md +0 -1
  414. package/skills/gsd-quick/SKILL.md +1 -1
  415. package/skills/gsd-review-backlog/SKILL.md +2 -1
  416. package/skills/gsd-stats/SKILL.md +0 -1
  417. package/skills/gsd-verify-work/SKILL.md +1 -1
  418. package/vscode/package.json +1 -1
  419. package/gsd-core/workflows/discovery-phase.md +0 -298
  420. package/gsd-core/workflows/plan-milestone-gaps.md +0 -281
  421. package/gsd-core/workflows/verify-phase.md +0 -577
  422. package/scripts/affected-tests-lib.cjs +0 -554
  423. package/scripts/gen-emitted-baseline.cjs +0 -145
  424. package/scripts/lint-allow-test-rule-refs.cjs +0 -162
  425. package/scripts/run-affected-tests.cjs +0 -7
  426. package/scripts/run-tests.cjs +0 -1050
@@ -22,8 +22,13 @@ const { planningDir } = planningWorkspaceMod;
22
22
  const phaseLocatorMod = require("./phase-locator.cjs");
23
23
  const { findPhaseInternal } = phaseLocatorMod;
24
24
  const decisions_cjs_1 = require("./decisions.cjs");
25
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
26
+ const frontmatterMod = require("./frontmatter.cjs");
27
+ const { extractFrontmatter } = frontmatterMod;
25
28
  const markdown_sectionizer_cjs_1 = require("./markdown-sectionizer.cjs");
29
+ const security_cjs_1 = require("./security.cjs");
26
30
  const ui_safety_gate_cjs_1 = require("./ui-safety-gate.cjs");
31
+ const ui_frontend_evidence_cjs_1 = require("./ui-frontend-evidence.cjs");
27
32
  // eslint-disable-next-line @typescript-eslint/no-require-imports
28
33
  const verifyModule = require("./verify.cjs");
29
34
  const { cmdVerifySchemaDrift, cmdVerifyCodebaseDrift } = verifyModule;
@@ -41,6 +46,12 @@ const { evaluatePredicate } = gatePredicateEval;
41
46
  const apiCoverageMod = require("./api-coverage.cjs");
42
47
  const { detectApiIntegration, validateCoverageMatrix } = apiCoverageMod;
43
48
  const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs");
49
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
50
+ const planScanMod = require("./plan-scan.cjs");
51
+ const { scanPhasePlans } = planScanMod;
52
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
53
+ const planningScopeMod = require("./planning-scope.cjs");
54
+ const { SCOPE } = planningScopeMod;
44
55
  // ─── Helpers ──────────────────────────────────────────────────────────────────
45
56
  function normalizePhrase(text) {
46
57
  // eslint-disable-next-line @typescript-eslint/no-base-to-string
@@ -124,14 +135,13 @@ function gateEnabled(projectDir) {
124
135
  function loadPlanContents(phaseDir) {
125
136
  if (!node_fs_1.default.existsSync(phaseDir))
126
137
  return [];
127
- try {
128
- return node_fs_1.default.readdirSync(phaseDir)
129
- .filter((entry) => /-PLAN\.md$/.test(entry))
130
- .map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)));
131
- }
132
- catch {
133
- return [];
134
- }
138
+ // #3183 (lint-plan-count-drift): source live plan files from the single
139
+ // owner (scanPhasePlans) instead of a local `-PLAN.md` readdirSync filter
140
+ // — picks up bare PLAN.md and nested plans/, and excludes plans marked
141
+ // `status: superseded`, which the prior root-only exact-suffix filter did
142
+ // neither for.
143
+ return scanPhasePlans(phaseDir).planFiles
144
+ .map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)));
135
145
  }
136
146
  const DESIGNATED_HEADINGS_RE = /^#{1,6}\s+(?:must[_ ]haves?|truths?|tasks?|objective)\b/i;
137
147
  // #2372: scanned-tag set must match the planner-canonical surfaces where a D-NN citation
@@ -333,6 +343,7 @@ function recentCommitMessages(projectDir) {
333
343
  encoding: 'utf-8',
334
344
  maxBuffer: 4 * 1024 * 1024,
335
345
  windowsHide: true,
346
+ timeout: 15_000,
336
347
  });
337
348
  }
338
349
  catch {
@@ -407,8 +418,11 @@ function cmdDecisionCoverageVerify(projectDir, args, raw) {
407
418
  return;
408
419
  }
409
420
  const planContents = loadPlanContents(phaseDir);
421
+ // #3183 (lint-plan-count-drift): same single-owner sourcing as
422
+ // loadPlanContents above — scanPhasePlans's summaryFiles instead of a
423
+ // local `-SUMMARY.md` readdirSync filter.
410
424
  const summaryParts = node_fs_1.default.existsSync(phaseDir)
411
- ? node_fs_1.default.readdirSync(phaseDir).filter((entry) => /-SUMMARY\.md$/.test(entry)).map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)))
425
+ ? scanPhasePlans(phaseDir).summaryFiles.map((entry) => readIfExists(node_path_1.default.join(phaseDir, entry)))
412
426
  : [];
413
427
  const haystack = [
414
428
  planContents.join('\n\n'),
@@ -438,8 +452,9 @@ function cmdDecisionCoverageVerify(projectDir, args, raw) {
438
452
  * ui-plan-gate: given a phase number, checks whether the phase has frontend
439
453
  * indicators and whether a *-UI-SPEC.md already exists in the phase directory.
440
454
  *
441
- * Returns JSON: { frontend: boolean, hasUiSpec: boolean, block: boolean }
442
- * block = frontend && !hasUiSpec (gate fires when UI work is detected but no spec exists)
455
+ * Returns JSON: { frontend, hasFrontendEvidence, hasUiSpec, block, uiSpecPath, matchedToken, matchedLine }
456
+ * block = frontend && hasFrontendEvidence && !hasUiSpec (#3312: gate fires when
457
+ * UI work is detected AND the repo has static frontend evidence but no spec exists)
443
458
  *
444
459
  * Invocable as: gsd_run check ui-plan-gate <phase>
445
460
  *
@@ -473,10 +488,21 @@ function findUiSpecInDir(phaseDir) {
473
488
  * (b) Runs checkUiPresence (frontend detection) — no reimplementation.
474
489
  * (c) Resolves the phase directory via findPhaseInternal (phase-locator.cjs); checks for *-UI-SPEC.md.
475
490
  *
476
- * Returns: { frontend, hasUiSpec, block, uiSpecPath, phaseLookupFailed }
477
- * block = frontend && !hasUiSpec
491
+ * Returns: { frontend, hasFrontendEvidence, hasUiSpec, block, uiSpecPath, matchedToken, matchedLine, phaseLookupFailed }
492
+ * block = frontend && hasFrontendEvidence && !hasUiSpec (#3312)
478
493
  * phaseLookupFailed = ROADMAP.md present but phase header not found (surfaced for
479
494
  * onError:halt gates so a missing phase doesn't silently bypass)
495
+ *
496
+ * #3312 — structural corroboration: `frontend` is a vocabulary signal only. A
497
+ * hyphen is a word boundary, so a phase naming the repo `dashboard-financeiro`
498
+ * matches the token `dashboard` exactly like the real compound `micro-frontend`
499
+ * (the boundary rule of #3718 is intentional and untouched). The gate therefore
500
+ * blocks only when the token match is corroborated by static frontend evidence
501
+ * in the repo tree (hasStaticFrontendEvidence: package.json UI-framework dep or
502
+ * a component-framework file). This mirrors the sibling post-wave gate
503
+ * computeUiSafetyGate, which requires `hasUiFiles` (git diff) before blocking.
504
+ * matchedToken/matchedLine surface what tripped the sniffer so an operator can
505
+ * judge the flag in one second instead of reaching for --skip-ui.
480
506
  */
481
507
  function computeUiPlanGate(projectDir, phase) {
482
508
  // (a) Read the phase section text using the same two-pass lookup as roadmap.get-phase.
@@ -505,6 +531,9 @@ function computeUiPlanGate(projectDir, phase) {
505
531
  // (b) Run checkUiPresence (frontend detection) — reuse existing helper; no reimplementation
506
532
  const presenceResult = (0, ui_safety_gate_cjs_1.checkUiPresence)(phaseSection);
507
533
  const frontend = presenceResult.hasUI;
534
+ // (b') #3312 — static structural corroboration. Only probed when the sniffer
535
+ // matched (evidence is irrelevant otherwise); failures degrade to false.
536
+ const hasFrontendEvidence = frontend ? (0, ui_frontend_evidence_cjs_1.hasStaticFrontendEvidence)(projectDir) : false;
508
537
  // (c) Resolve phase directory via findPhaseInternal and check for *-UI-SPEC.md
509
538
  let phaseDir = '';
510
539
  try {
@@ -524,10 +553,13 @@ function computeUiPlanGate(projectDir, phase) {
524
553
  catch { /* phase dir lookup failure → hasUiSpec=false */ }
525
554
  const uiSpecPath = findUiSpecInDir(phaseDir);
526
555
  const hasUiSpec = uiSpecPath !== '';
527
- // block = frontend phase with no UI-SPEC
528
- const block = frontend && !hasUiSpec;
556
+ // block = frontend phase with structural frontend evidence and no UI-SPEC (#3312)
557
+ const block = frontend && hasFrontendEvidence && !hasUiSpec;
529
558
  const result = {
530
- frontend, hasUiSpec, block, uiSpecPath: hasUiSpec ? uiSpecPath : null,
559
+ frontend, hasFrontendEvidence, hasUiSpec, block,
560
+ uiSpecPath: hasUiSpec ? uiSpecPath : null,
561
+ matchedToken: presenceResult.matchedToken,
562
+ matchedLine: presenceResult.matchedLine,
531
563
  };
532
564
  if (phaseLookupFailed)
533
565
  result.phaseLookupFailed = true;
@@ -609,6 +641,7 @@ function computeUiSafetyGate(projectDir, phase) {
609
641
  encoding: 'utf-8',
610
642
  maxBuffer: 2 * 1024 * 1024,
611
643
  windowsHide: true,
644
+ timeout: 10_000,
612
645
  });
613
646
  hasUiFiles = changed.split('\n').some((f) => f.trim() && (UI_FILE_EXTENSIONS_RE.test(f) || UI_PATH_PATTERNS_RE.test(f)));
614
647
  }
@@ -675,7 +708,9 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
675
708
  const tddPlanFiles = [];
676
709
  if (phaseDir) {
677
710
  try {
678
- const files = node_fs_1.default.readdirSync(phaseDir).filter(f => f.endsWith('-PLAN.md'));
711
+ // #3183: canonical plan set (root+nested, superseded-excluded) from the
712
+ // single owner, rather than a root-only hand-rolled readdirSync filter.
713
+ const files = scanPhasePlans(phaseDir).planFiles;
679
714
  for (const file of files) {
680
715
  const planPath = node_path_1.default.join(phaseDir, file);
681
716
  const content = readIfExists(planPath);
@@ -724,17 +759,17 @@ function cmdTddReviewCheckpoint(projectDir, args, raw) {
724
759
  let green = false;
725
760
  let refactor = false;
726
761
  try {
727
- const redCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^test(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
762
+ const redCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^test(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true, timeout: 10_000 });
728
763
  red = redCommit.trim().length > 0;
729
764
  }
730
765
  catch { /* git unavailable or no match */ }
731
766
  try {
732
- const greenCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^feat(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
767
+ const greenCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^feat(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true, timeout: 10_000 });
733
768
  green = greenCommit.trim().length > 0;
734
769
  }
735
770
  catch { /* git unavailable or no match */ }
736
771
  try {
737
- const refactorCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^refactor(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true });
772
+ const refactorCommit = (0, node_child_process_1.execFileSync)('git', ['log', '--oneline', `--grep=^refactor(${planId}):`, '--', '.'], { cwd: projectDir, encoding: 'utf-8', maxBuffer: 1024 * 1024, windowsHide: true, timeout: 10_000 });
738
773
  refactor = refactorCommit.trim().length > 0;
739
774
  }
740
775
  catch { /* git unavailable or no match */ }
@@ -849,9 +884,47 @@ function buildPredicateDeps() {
849
884
  stdout: r.stdout,
850
885
  stderr: r.stderr,
851
886
  signal: r.signal,
852
- timedOut: r.signal === 'SIGTERM',
887
+ timedOut: r.timedOut,
853
888
  };
854
889
  },
890
+ findPhaseArtifact(phaseDir, artifactSuffix) {
891
+ if (!node_fs_1.default.existsSync(phaseDir))
892
+ return null;
893
+ if (artifactSuffix === '.' ||
894
+ artifactSuffix === '..' ||
895
+ artifactSuffix.includes('\0') ||
896
+ node_path_1.default.basename(artifactSuffix) !== artifactSuffix ||
897
+ node_path_1.default.win32.basename(artifactSuffix) !== artifactSuffix) {
898
+ return null;
899
+ }
900
+ const directPath = (0, security_cjs_1.validatePath)(artifactSuffix, phaseDir);
901
+ if (directPath.safe && node_fs_1.default.existsSync(directPath.resolved) && node_fs_1.default.statSync(directPath.resolved).isFile()) {
902
+ return directPath.resolved;
903
+ }
904
+ const planningPath = (0, security_cjs_1.validatePath)(node_path_1.default.join('.planning', artifactSuffix), phaseDir);
905
+ if (planningPath.safe && node_fs_1.default.existsSync(planningPath.resolved) && node_fs_1.default.statSync(planningPath.resolved).isFile()) {
906
+ return planningPath.resolved;
907
+ }
908
+ try {
909
+ const files = node_fs_1.default.readdirSync(phaseDir);
910
+ for (const f of files) {
911
+ if (f.endsWith('-' + artifactSuffix) || f === artifactSuffix) {
912
+ const candidate = (0, security_cjs_1.validatePath)(f, phaseDir);
913
+ if (candidate.safe && node_fs_1.default.statSync(candidate.resolved).isFile())
914
+ return candidate.resolved;
915
+ }
916
+ }
917
+ }
918
+ catch { /* ignore */ }
919
+ return null;
920
+ },
921
+ readFrontmatter(filePath) {
922
+ const content = (0, shell_command_projection_cjs_1.platformReadSync)(filePath);
923
+ if (content === null)
924
+ throw new Error(`predicate artifact disappeared before it could be read: ${filePath}`);
925
+ const parsed = extractFrontmatter(content, filePath);
926
+ return parsed;
927
+ }
855
928
  };
856
929
  }
857
930
  /** Parse `--flag value` pairs from an args array into a map (last write wins). */
@@ -1165,12 +1238,26 @@ function isRealReadFailure(err) {
1165
1238
  function readPhaseScope(projectDir, phaseDir, phaseNumber) {
1166
1239
  const chunks = [];
1167
1240
  let readError = null;
1168
- try {
1169
- const entries = node_fs_1.default.readdirSync(phaseDir, { withFileTypes: true });
1170
- const plans = entries
1171
- .filter((e) => e.isFile() && /-PLAN\.md$/i.test(e.name))
1172
- .map((e) => e.name)
1173
- .sort();
1241
+ // A MISSING phase directory is fine (no plans yet → fall through to the
1242
+ // roadmap). Checked up front (rather than via a readdirSync catch) because
1243
+ // #3183 (lint-plan-count-drift) now sources the plan-file list from the
1244
+ // single owner (scanPhasePlans) instead of a local `-PLAN\.md$` readdirSync
1245
+ // filter — picks up bare PLAN.md and nested plans/, and excludes
1246
+ // superseded plans, none of which the prior root-only exact-suffix filter
1247
+ // did.
1248
+ if (node_fs_1.default.existsSync(phaseDir)) {
1249
+ const scan = scanPhasePlans(phaseDir);
1250
+ if (scan.scope === SCOPE.UNREADABLE) {
1251
+ // Directory exists but scanPhasePlans's own readdirSync(phaseDir) call
1252
+ // failed (EACCES/EIO race) — a real read failure the gate must not
1253
+ // silently pass (#2365 review), mirroring the prior isRealReadFailure
1254
+ // branch below for the readdirSync-throws case.
1255
+ return {
1256
+ text: '',
1257
+ readError: 'could not read the phase directory: scanPhasePlans reported scope UNREADABLE',
1258
+ };
1259
+ }
1260
+ const plans = [...scan.planFiles].sort();
1174
1261
  for (const p of plans) {
1175
1262
  try {
1176
1263
  chunks.push(node_fs_1.default.readFileSync(node_path_1.default.join(phaseDir, p), 'utf8'));
@@ -1184,17 +1271,6 @@ function readPhaseScope(projectDir, phaseDir, phaseNumber) {
1184
1271
  }
1185
1272
  }
1186
1273
  }
1187
- catch (err) {
1188
- // A MISSING phase directory is fine (no plans yet → fall through to the
1189
- // roadmap). A directory that exists but cannot be enumerated (EACCES/EIO)
1190
- // is a real read failure the gate must not silently pass (#2365 review).
1191
- if (isRealReadFailure(err)) {
1192
- return {
1193
- text: '',
1194
- readError: `could not read the phase directory: ${err instanceof Error ? err.message : String(err)}`,
1195
- };
1196
- }
1197
- }
1198
1274
  if (readError)
1199
1275
  return { text: chunks.join('\n\n'), readError };
1200
1276
  if (chunks.join('').trim().length > 0)
@@ -470,13 +470,43 @@ function emitWorkflowScript(input) {
470
470
  lines.push('// read-only in a Workflow script — set it via the caller\'s token directive.');
471
471
  }
472
472
  lines.push('');
473
+ // #3302: the generated script must hand the per-agent executor results back
474
+ // to the orchestrator so it can feed the wave merge chain. Emitted right
475
+ // after the header comments (meta stays the first statement): a helper that
476
+ // extracts the executor's <worktree_metadata> JSON (agents/gsd-executor.md
477
+ // <worktree_metadata_capture>) from one agent() result, plus the outcomes
478
+ // accumulator the stage barriers below push into. `metadata` is null when
479
+ // the result carried no parseable block — the LOUD-failure input the
480
+ // orchestrator halts on for expects_worktree plans (never a silent skip).
481
+ lines.push('// #3302: extract the executor-returned <worktree_metadata> JSON so the');
482
+ lines.push('// orchestrator can record it into WAVE_WORKTREE_MANIFEST after the run');
483
+ lines.push('// (worktree.record-agent -> worktree.cleanup-wave, the same manifest-scoped');
484
+ lines.push('// merge chain inline dispatch feeds). null = absent/unparseable/interrupted.');
485
+ lines.push('function gsdWorktreeMetadata(agentResult) {');
486
+ lines.push(' if (typeof agentResult !== \'string\') return null;');
487
+ lines.push(' const m = agentResult.match(/<worktree_metadata>([\\s\\S]*?)<\\/worktree_metadata>/);');
488
+ lines.push(' if (m === null) return null;');
489
+ lines.push(' try {');
490
+ lines.push(' const parsed = JSON.parse(m[1]);');
491
+ lines.push(' return (parsed !== null && typeof parsed === \'object\') ? parsed : null;');
492
+ lines.push(' } catch (e) {');
493
+ lines.push(' return null;');
494
+ lines.push(' }');
495
+ lines.push('}');
496
+ lines.push('const gsdAgentOutcomes = [];');
497
+ lines.push('');
473
498
  const stagesByWave = [];
474
499
  let totalPlans = 0;
500
+ let worktreePlans = 0;
475
501
  for (let wi = 0; wi < waves.length; wi++) {
476
502
  const wave = waves[wi];
477
503
  const stages = partitionStages(wave.plans);
478
504
  stagesByWave.push(stages);
479
505
  totalPlans += wave.plans.length;
506
+ for (const p of wave.plans) {
507
+ if (p.use_worktree !== false)
508
+ worktreePlans += 1;
509
+ }
480
510
  lines.push('// Wave ' + wave.id);
481
511
  // Title must match this wave's meta.phases entry EXACTLY.
482
512
  lines.push('phase(' + quoteString('Wave ' + wave.id) + ')');
@@ -491,17 +521,37 @@ function emitWorkflowScript(input) {
491
521
  // threw "parallel() expects an array of functions" (#2590). Passing
492
522
  // agent() results directly would also start every agent eagerly, before
493
523
  // parallel() could bound concurrency.
494
- lines.push('await parallel([');
524
+ // #3302: capture the barrier's resolved results (one per thunk, in thunk
525
+ // order — the documented parallel() contract) so each plan's outcome can
526
+ // be tagged and returned below. Discarding them stranded every
527
+ // worktree-wf_* branch: the merge chain had no input (#3302).
528
+ lines.push('const gsdStage_' + wi + '_' + si + ' = await parallel([');
495
529
  for (const p of stagePlans) {
496
530
  lines.push(' () => agent(' + quoteString(p.brief) + ', ' + agentOptions(p, executorModel) + '),');
497
531
  }
498
532
  lines.push('])');
533
+ // Positional tagging is decided at EMIT time from the validated manifest,
534
+ // so attribution survives out-of-order completion and needs no runtime
535
+ // introspection. expects_worktree mirrors agentOptions' own per-plan
536
+ // decision (use_worktree !== false).
537
+ lines.push('gsdAgentOutcomes.push(');
538
+ for (let pi = 0; pi < stagePlans.length; pi++) {
539
+ const p = stagePlans[pi];
540
+ const tail = pi < stagePlans.length - 1 ? ',' : '';
541
+ lines.push(' { plan: ' + quoteString(p.id) + ', expects_worktree: ' + (p.use_worktree !== false) + ', metadata: gsdWorktreeMetadata(gsdStage_' + wi + '_' + si + '[' + pi + ']) }' + tail);
542
+ }
543
+ lines.push(')');
499
544
  }
500
545
  if (wi < waves.length - 1)
501
546
  lines.push('');
502
547
  }
503
- lines.push('// Each agent writes SUMMARY.md on its worktree branch; commits land there');
504
- lines.push('// and are merged by the orchestrator exactly as in inline wave dispatch.');
548
+ // #3302: the script's top-level return value is what the Workflow tool hands
549
+ // back to the orchestrator. One { plan, expects_worktree, metadata } entry
550
+ // per dispatched plan; the orchestrator records every worktree entry via
551
+ // `gsd_run query worktree.record-agent` and HALTS on a null metadata entry
552
+ // for an expects_worktree plan (see the execute:wave:pre fragment) — a
553
+ // silently-empty manifest is the exact #3302 failure mode.
554
+ lines.push('return gsdAgentOutcomes');
505
555
  const script = lines.join('\n');
506
556
  return {
507
557
  ok: true,
@@ -509,6 +559,9 @@ function emitWorkflowScript(input) {
509
559
  summary: {
510
560
  waves: waves.length,
511
561
  plans: totalPlans,
562
+ // #3302: the number of record-agent entries the orchestrator must end up
563
+ // with in WAVE_WORKTREE_MANIFEST after the run — the loud count check.
564
+ worktreePlans,
512
565
  stagesByWave,
513
566
  resumeRunId: runId,
514
567
  budgetTokens,