gsd-pi 2.82.0-dev.725028083 → 2.82.0-dev.9d5798940

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 (210) hide show
  1. package/README.md +3 -2
  2. package/dist/resources/.managed-resources-content-hash +1 -1
  3. package/dist/resources/GSD-WORKFLOW.md +3 -1
  4. package/dist/resources/extensions/claude-code-cli/partial-builder.js +2 -1
  5. package/dist/resources/extensions/cmux/index.js +5 -0
  6. package/dist/resources/extensions/gsd/auto/orchestrator.js +113 -6
  7. package/dist/resources/extensions/gsd/auto-dispatch.js +13 -6
  8. package/dist/resources/extensions/gsd/auto-post-unit.js +232 -126
  9. package/dist/resources/extensions/gsd/auto-prompts.js +2 -2
  10. package/dist/resources/extensions/gsd/auto-recovery.js +31 -1
  11. package/dist/resources/extensions/gsd/auto-start.js +7 -3
  12. package/dist/resources/extensions/gsd/auto-verification.js +28 -22
  13. package/dist/resources/extensions/gsd/auto-worktree.js +96 -0
  14. package/dist/resources/extensions/gsd/auto.js +128 -52
  15. package/dist/resources/extensions/gsd/bootstrap/agent-end-recovery.js +4 -1
  16. package/dist/resources/extensions/gsd/bootstrap/subagent-input.js +16 -7
  17. package/dist/resources/extensions/gsd/bootstrap/write-gate.js +16 -2
  18. package/dist/resources/extensions/gsd/clean-root-preflight.js +170 -8
  19. package/dist/resources/extensions/gsd/commands/catalog.js +4 -1
  20. package/dist/resources/extensions/gsd/commands/handlers/core.js +37 -0
  21. package/dist/resources/extensions/gsd/commands-bootstrap.js +5 -0
  22. package/dist/resources/extensions/gsd/db/unit-dispatches.js +2 -2
  23. package/dist/resources/extensions/gsd/export-html.js +27 -425
  24. package/dist/resources/extensions/gsd/md-importer.js +1 -1
  25. package/dist/resources/extensions/gsd/migrate/command.js +5 -0
  26. package/dist/resources/extensions/gsd/migrate/preview.js +9 -0
  27. package/dist/resources/extensions/gsd/migrate/transformer.js +51 -4
  28. package/dist/resources/extensions/gsd/migrate/writer.js +11 -1
  29. package/dist/resources/extensions/gsd/milestone-actions.js +11 -4
  30. package/dist/resources/extensions/gsd/native-git-bridge.js +8 -3
  31. package/dist/resources/extensions/gsd/state-reconciliation/drift/merge-state.js +6 -1
  32. package/dist/resources/extensions/gsd/tools/plan-slice.js +2 -1
  33. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +119 -0
  34. package/dist/resources/extensions/gsd/unit-context-manifest.js +32 -10
  35. package/dist/resources/extensions/gsd/verification-verdict.js +26 -0
  36. package/dist/resources/extensions/gsd/worktree-lifecycle.js +49 -9
  37. package/dist/resources/extensions/shared/html-shell.js +388 -0
  38. package/dist/resources/extensions/subagent/index.js +448 -78
  39. package/dist/resources/extensions/subagent/launch.js +77 -0
  40. package/dist/resources/extensions/subagent/run-store.js +148 -0
  41. package/dist/resources/extensions/visual-brief/artifact-policy.js +29 -0
  42. package/dist/resources/extensions/visual-brief/extension-manifest.json +8 -0
  43. package/dist/resources/extensions/visual-brief/index.js +5 -0
  44. package/dist/resources/extensions/visual-brief/page-contract.js +124 -0
  45. package/dist/resources/extensions/visual-brief/prompts.js +140 -0
  46. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  47. package/dist/web/standalone/.next/BUILD_ID +1 -1
  48. package/dist/web/standalone/.next/app-path-routes-manifest.json +15 -15
  49. package/dist/web/standalone/.next/build-manifest.json +3 -3
  50. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  51. package/dist/web/standalone/.next/react-loadable-manifest.json +3 -3
  52. package/dist/web/standalone/.next/server/app/_global-error/page_client-reference-manifest.js +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  54. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found/page.js +2 -2
  62. package/dist/web/standalone/.next/server/app/_not-found/page.js.nft.json +1 -1
  63. package/dist/web/standalone/.next/server/app/_not-found/page_client-reference-manifest.js +1 -1
  64. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  65. package/dist/web/standalone/.next/server/app/_not-found.rsc +4 -7
  66. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +4 -7
  67. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +4 -5
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +2 -5
  72. package/dist/web/standalone/.next/server/app/index.html +1 -1
  73. package/dist/web/standalone/.next/server/app/index.rsc +4 -7
  74. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +4 -7
  76. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +4 -5
  78. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +2 -5
  79. package/dist/web/standalone/.next/server/app/page.js +2 -2
  80. package/dist/web/standalone/.next/server/app/page.js.nft.json +1 -1
  81. package/dist/web/standalone/.next/server/app/page_client-reference-manifest.js +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +15 -15
  83. package/dist/web/standalone/.next/server/chunks/4266.js +2 -0
  84. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  85. package/dist/web/standalone/.next/server/middleware-react-loadable-manifest.js +1 -1
  86. package/dist/web/standalone/.next/server/next-font-manifest.js +1 -1
  87. package/dist/web/standalone/.next/server/next-font-manifest.json +1 -1
  88. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  89. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  90. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  91. package/dist/web/standalone/.next/static/chunks/2973.33f26573894b6153.js +2 -0
  92. package/dist/web/standalone/.next/static/chunks/{8359.e059d86b255fce1c.js → 8359.7eb3bb8f8ecf4c01.js} +2 -2
  93. package/dist/web/standalone/.next/static/chunks/app/layout-8c10ec293ae0f1d5.js +1 -0
  94. package/dist/web/standalone/.next/static/chunks/{webpack-de742b64187e13fe.js → webpack-9a4db269f9ed63ad.js} +1 -1
  95. package/dist/web/standalone/.next/static/css/746ee28c929d1880.css +1 -0
  96. package/package.json +3 -3
  97. package/packages/contracts/dist/rpc.test.js +7 -0
  98. package/packages/contracts/dist/rpc.test.js.map +1 -1
  99. package/packages/contracts/dist/workflow.d.ts +21 -0
  100. package/packages/contracts/dist/workflow.d.ts.map +1 -1
  101. package/packages/contracts/dist/workflow.js +24 -0
  102. package/packages/contracts/dist/workflow.js.map +1 -1
  103. package/packages/contracts/src/rpc.test.ts +8 -0
  104. package/packages/contracts/src/workflow.ts +24 -0
  105. package/packages/mcp-server/README.md +13 -4
  106. package/packages/mcp-server/dist/workflow-tools.d.ts +0 -3
  107. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  108. package/packages/mcp-server/dist/workflow-tools.js +80 -0
  109. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  110. package/packages/mcp-server/src/workflow-tools.test.ts +22 -0
  111. package/packages/mcp-server/src/workflow-tools.ts +168 -0
  112. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  113. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -1
  114. package/packages/pi-tui/dist/tui.d.ts.map +1 -1
  115. package/packages/pi-tui/dist/tui.js +5 -0
  116. package/packages/pi-tui/dist/tui.js.map +1 -1
  117. package/packages/pi-tui/src/tui.ts +6 -0
  118. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -1
  119. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -1
  120. package/src/resources/GSD-WORKFLOW.md +3 -1
  121. package/src/resources/extensions/claude-code-cli/partial-builder.ts +2 -1
  122. package/src/resources/extensions/claude-code-cli/tests/partial-builder.test.ts +19 -2
  123. package/src/resources/extensions/cmux/index.ts +6 -0
  124. package/src/resources/extensions/gsd/auto/contracts.ts +46 -11
  125. package/src/resources/extensions/gsd/auto/orchestrator.ts +118 -6
  126. package/src/resources/extensions/gsd/auto-dispatch.ts +14 -6
  127. package/src/resources/extensions/gsd/auto-post-unit.ts +265 -138
  128. package/src/resources/extensions/gsd/auto-prompts.ts +2 -2
  129. package/src/resources/extensions/gsd/auto-recovery.ts +29 -0
  130. package/src/resources/extensions/gsd/auto-start.ts +7 -3
  131. package/src/resources/extensions/gsd/auto-verification.ts +36 -34
  132. package/src/resources/extensions/gsd/auto-worktree.ts +104 -0
  133. package/src/resources/extensions/gsd/auto.ts +136 -51
  134. package/src/resources/extensions/gsd/bootstrap/agent-end-recovery.ts +6 -1
  135. package/src/resources/extensions/gsd/bootstrap/subagent-input.ts +16 -6
  136. package/src/resources/extensions/gsd/bootstrap/write-gate.ts +19 -3
  137. package/src/resources/extensions/gsd/clean-root-preflight.ts +174 -8
  138. package/src/resources/extensions/gsd/commands/catalog.ts +4 -1
  139. package/src/resources/extensions/gsd/commands/handlers/core.ts +40 -0
  140. package/src/resources/extensions/gsd/commands-bootstrap.ts +10 -0
  141. package/src/resources/extensions/gsd/db/unit-dispatches.ts +3 -3
  142. package/src/resources/extensions/gsd/export-html.ts +27 -427
  143. package/src/resources/extensions/gsd/md-importer.ts +1 -1
  144. package/src/resources/extensions/gsd/migrate/command.ts +5 -0
  145. package/src/resources/extensions/gsd/migrate/preview.ts +10 -0
  146. package/src/resources/extensions/gsd/migrate/transformer.ts +58 -4
  147. package/src/resources/extensions/gsd/migrate/writer.ts +14 -1
  148. package/src/resources/extensions/gsd/milestone-actions.ts +10 -4
  149. package/src/resources/extensions/gsd/native-git-bridge.ts +8 -3
  150. package/src/resources/extensions/gsd/state-reconciliation/drift/merge-state.ts +8 -1
  151. package/src/resources/extensions/gsd/tests/auto-deterministic-error-classification-4973.test.ts +116 -0
  152. package/src/resources/extensions/gsd/tests/auto-orchestrator.test.ts +408 -4
  153. package/src/resources/extensions/gsd/tests/auto-paused-ui-cleanup.test.ts +6 -5
  154. package/src/resources/extensions/gsd/tests/auto-post-unit-step-message.test.ts +12 -1
  155. package/src/resources/extensions/gsd/tests/auto-recovery.test.ts +15 -1
  156. package/src/resources/extensions/gsd/tests/auto-runtime-state.test.ts +4 -4
  157. package/src/resources/extensions/gsd/tests/auto-worktree-registry.test.ts +69 -1
  158. package/src/resources/extensions/gsd/tests/brief-command.test.ts +89 -0
  159. package/src/resources/extensions/gsd/tests/clean-root-preflight.test.ts +107 -2
  160. package/src/resources/extensions/gsd/tests/closeout-git-deferral.test.ts +16 -0
  161. package/src/resources/extensions/gsd/tests/deep-project-auto-loop.test.ts +57 -2
  162. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +39 -0
  163. package/src/resources/extensions/gsd/tests/evidence-cross-ref.test.ts +38 -0
  164. package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +8 -0
  165. package/src/resources/extensions/gsd/tests/integration/migrate-command.test.ts +48 -3
  166. package/src/resources/extensions/gsd/tests/migrate-transformer.test.ts +5 -1
  167. package/src/resources/extensions/gsd/tests/migrate-writer-integration.test.ts +6 -1
  168. package/src/resources/extensions/gsd/tests/park-db-sync.test.ts +55 -1
  169. package/src/resources/extensions/gsd/tests/plan-slice.test.ts +25 -0
  170. package/src/resources/extensions/gsd/tests/post-exec-retry-bypass.test.ts +79 -1
  171. package/src/resources/extensions/gsd/tests/post-unit-git-failure.test.ts +1 -1
  172. package/src/resources/extensions/gsd/tests/remediation-completion-guard.test.ts +46 -2
  173. package/src/resources/extensions/gsd/tests/session-switch-abort-misclassification.test.ts +10 -0
  174. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +6 -0
  175. package/src/resources/extensions/gsd/tests/stuck-state-via-db.test.ts +39 -0
  176. package/src/resources/extensions/gsd/tests/unit-context-manifest.test.ts +86 -7
  177. package/src/resources/extensions/gsd/tests/verification-verdict.test.ts +78 -0
  178. package/src/resources/extensions/gsd/tests/worktree-journal-events.test.ts +64 -12
  179. package/src/resources/extensions/gsd/tests/worktree-lifecycle.test.ts +25 -0
  180. package/src/resources/extensions/gsd/tests/write-gate-planning-unit.test.ts +47 -0
  181. package/src/resources/extensions/gsd/tools/plan-slice.ts +2 -0
  182. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +135 -0
  183. package/src/resources/extensions/gsd/unit-context-manifest.ts +47 -11
  184. package/src/resources/extensions/gsd/verification-verdict.ts +47 -0
  185. package/src/resources/extensions/gsd/worktree-lifecycle.ts +54 -9
  186. package/src/resources/extensions/shared/html-shell.ts +412 -0
  187. package/src/resources/extensions/subagent/index.ts +567 -103
  188. package/src/resources/extensions/subagent/launch.ts +131 -0
  189. package/src/resources/extensions/subagent/run-store.ts +218 -0
  190. package/src/resources/extensions/subagent/tests/launch.test.ts +115 -0
  191. package/src/resources/extensions/subagent/tests/run-store.test.ts +111 -0
  192. package/src/resources/extensions/visual-brief/artifact-policy.ts +41 -0
  193. package/src/resources/extensions/visual-brief/extension-manifest.json +8 -0
  194. package/src/resources/extensions/visual-brief/index.ts +8 -0
  195. package/src/resources/extensions/visual-brief/page-contract.ts +136 -0
  196. package/src/resources/extensions/visual-brief/prompts.ts +183 -0
  197. package/src/resources/extensions/visual-brief/tests/visual-brief.test.ts +212 -0
  198. package/dist/web/standalone/.next/server/chunks/5822.js +0 -2
  199. package/dist/web/standalone/.next/static/chunks/2556.0527fea66e123b7f.js +0 -1
  200. package/dist/web/standalone/.next/static/chunks/app/layout-a16c7a7ecdf0c2cf.js +0 -1
  201. package/dist/web/standalone/.next/static/css/54ec2745c1da488b.css +0 -1
  202. package/dist/web/standalone/.next/static/css/de70bee13400563f.css +0 -1
  203. package/dist/web/standalone/.next/static/media/4cf2300e9c8272f7-s.p.woff2 +0 -0
  204. package/dist/web/standalone/.next/static/media/747892c23ea88013-s.woff2 +0 -0
  205. package/dist/web/standalone/.next/static/media/8d697b304b401681-s.woff2 +0 -0
  206. package/dist/web/standalone/.next/static/media/93f479601ee12b01-s.p.woff2 +0 -0
  207. package/dist/web/standalone/.next/static/media/9610d9e46709d722-s.woff2 +0 -0
  208. package/dist/web/standalone/.next/static/media/ba015fad6dcf6784-s.woff2 +0 -0
  209. /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → BdZQhe8yKl6bdKLiXVEzh}/_buildManifest.js +0 -0
  210. /package/dist/web/standalone/.next/static/{KDRTXR-22LPCsa80X9dey → BdZQhe8yKl6bdKLiXVEzh}/_ssgManifest.js +0 -0
@@ -26,6 +26,7 @@ const PLANNING_DISPATCH_REVIEW: ToolsPolicy = {
26
26
  };
27
27
  const READ_ONLY: ToolsPolicy = { mode: 'read-only' };
28
28
  const ALL: ToolsPolicy = { mode: 'all' };
29
+ const VERIFICATION: ToolsPolicy = { mode: 'verification' };
29
30
  const DOCS: ToolsPolicy = {
30
31
  mode: 'docs',
31
32
  allowedPathGlobs: ['docs/**', 'README.md', 'README.*.md', 'CHANGELOG.md', '*.md'],
@@ -172,6 +173,22 @@ test('planning-dispatch: extracts subagent classes from single, parallel, and ch
172
173
  extractSubagentAgentClasses({ chain: [{ agent: 'reviewer' }, { agent: 'security' }] }),
173
174
  ['reviewer', 'security'],
174
175
  );
176
+ assert.deepEqual(
177
+ extractSubagentAgentClasses({
178
+ chain: [
179
+ { agent: 'scout' },
180
+ { parallel: [{ agent: 'reviewer' }, { agent: ' security ' }] },
181
+ ],
182
+ }),
183
+ ['scout', 'reviewer', 'security'],
184
+ );
185
+ });
186
+
187
+ test('planning-dispatch: extracts subagent classes without recursing through cycles', () => {
188
+ const input: { agent: string; parallel?: unknown[] } = { agent: 'scout' };
189
+ input.parallel = [input, { agent: 'reviewer' }];
190
+
191
+ assert.deepEqual(extractSubagentAgentClasses(input), ['scout', 'reviewer']);
175
192
  });
176
193
 
177
194
  test('planning-dispatch: blocks subagent dispatch when agentClasses is undefined (stale caller shim)', () => {
@@ -315,6 +332,36 @@ test('all-mode: execute-task can dispatch subagents', () => {
315
332
  assert.strictEqual(r.block, false);
316
333
  });
317
334
 
335
+ // ─── verification mode: bash allowed, writes still scoped ─────────────────
336
+
337
+ test('verification-mode: run-uat can run build commands', () => {
338
+ const r = shouldBlockPlanningUnit('bash', 'npm run build 2>&1', BASE, 'run-uat', VERIFICATION);
339
+ assert.strictEqual(r.block, false);
340
+ });
341
+
342
+ test('verification-mode: run-uat blocks destructive bash (rm -rf)', () => {
343
+ const r = shouldBlockPlanningUnit('bash', 'rm -rf dist', BASE, 'run-uat', VERIFICATION);
344
+ assert.strictEqual(r.block, true);
345
+ assert.match(r.reason!, /bash is restricted to build\/test verification commands/);
346
+ });
347
+
348
+ test('verification-mode: run-uat allows read-only investigative bash (git status)', () => {
349
+ const r = shouldBlockPlanningUnit('bash', 'git status', BASE, 'run-uat', VERIFICATION);
350
+ assert.strictEqual(r.block, false);
351
+ });
352
+
353
+ test('verification-mode: run-uat still blocks user source edits', () => {
354
+ const r = shouldBlockPlanningUnit('edit', join(BASE, 'src', 'main.ts'), BASE, 'run-uat', VERIFICATION);
355
+ assert.strictEqual(r.block, true);
356
+ assert.match(r.reason!, /tools-policy "verification"/);
357
+ });
358
+
359
+ test('verification-mode: run-uat still blocks subagent dispatch', () => {
360
+ const r = shouldBlockPlanningUnit('subagent', '', BASE, 'run-uat', VERIFICATION);
361
+ assert.strictEqual(r.block, true);
362
+ assert.match(r.reason!, /subagent dispatch is not permitted/);
363
+ });
364
+
318
365
  // ─── read-only mode ───────────────────────────────────────────────────────
319
366
 
320
367
  test('read-only: blocks any edit even to .gsd/', () => {
@@ -10,6 +10,7 @@ import {
10
10
  upsertTaskPlanning,
11
11
  insertGateRow,
12
12
  updateSliceStatus,
13
+ setSliceSketchFlag,
13
14
  } from "../gsd-db.js";
14
15
  import type { GateId } from "../types.js";
15
16
  import { invalidateStateCache } from "../state.js";
@@ -184,6 +185,7 @@ export async function handlePlanSlice(
184
185
  if (isDeferredStatus(parentSlice.status)) {
185
186
  updateSliceStatus(params.milestoneId, params.sliceId, "pending");
186
187
  }
188
+ setSliceSketchFlag(params.milestoneId, params.sliceId, false);
187
189
 
188
190
  upsertSlicePlanning(params.milestoneId, params.sliceId, {
189
191
  goal: params.goal,
@@ -1,3 +1,6 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Adapts shared GSD workflow handlers for MCP executor calls.
3
+
1
4
  import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
2
5
  import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
3
6
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot, shouldBlockRootArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
@@ -25,6 +28,12 @@ import type { PlanSliceParams } from "./plan-slice.js";
25
28
  import { handlePlanSlice } from "./plan-slice.js";
26
29
  import type { ReplanSliceParams } from "./replan-slice.js";
27
30
  import { handleReplanSlice } from "./replan-slice.js";
31
+ import type { ReopenMilestoneParams } from "./reopen-milestone.js";
32
+ import { handleReopenMilestone } from "./reopen-milestone.js";
33
+ import type { ReopenSliceParams } from "./reopen-slice.js";
34
+ import { handleReopenSlice } from "./reopen-slice.js";
35
+ import type { ReopenTaskParams } from "./reopen-task.js";
36
+ import { handleReopenTask } from "./reopen-task.js";
28
37
  import type { ReassessRoadmapParams } from "./reassess-roadmap.js";
29
38
  import { handleReassessRoadmap } from "./reassess-roadmap.js";
30
39
  import type { ValidateMilestoneParams } from "./validate-milestone.js";
@@ -311,6 +320,9 @@ export type SliceCompleteExecutorParams = CompleteSliceParams;
311
320
  export type PlanMilestoneExecutorParams = PlanMilestoneParams;
312
321
  export type PlanSliceExecutorParams = PlanSliceParams;
313
322
  export type ReplanSliceExecutorParams = ReplanSliceParams;
323
+ export type ReopenTaskExecutorParams = ReopenTaskParams;
324
+ export type ReopenSliceExecutorParams = ReopenSliceParams;
325
+ export type ReopenMilestoneExecutorParams = ReopenMilestoneParams;
314
326
  export type ValidateMilestoneExecutorParams = ValidateMilestoneParams;
315
327
  export type ReassessRoadmapExecutorParams = ReassessRoadmapParams;
316
328
 
@@ -371,6 +383,129 @@ export async function executeTaskComplete(
371
383
  }
372
384
  }
373
385
 
386
+ export async function executeTaskReopen(
387
+ params: ReopenTaskExecutorParams,
388
+ basePath: string = process.cwd(),
389
+ ): Promise<ToolExecutionResult> {
390
+ const dbAvailable = await ensureDbOpen(basePath);
391
+ if (!dbAvailable) {
392
+ return {
393
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reopen task." }],
394
+ details: { operation: "reopen_task", error: "db_unavailable" },
395
+ isError: true,
396
+ };
397
+ }
398
+ try {
399
+ const result = await handleReopenTask(params, basePath);
400
+ if ("error" in result) {
401
+ return {
402
+ content: [{ type: "text", text: `Error reopening task: ${result.error}` }],
403
+ details: { operation: "reopen_task", error: result.error },
404
+ isError: true,
405
+ };
406
+ }
407
+ return {
408
+ content: [{ type: "text", text: `Reopened task ${result.taskId} (${result.sliceId}/${result.milestoneId})` }],
409
+ details: {
410
+ operation: "reopen_task",
411
+ taskId: result.taskId,
412
+ sliceId: result.sliceId,
413
+ milestoneId: result.milestoneId,
414
+ },
415
+ };
416
+ } catch (err) {
417
+ const msg = err instanceof Error ? err.message : String(err);
418
+ logError("tool", `reopen_task tool failed: ${msg}`, { tool: "gsd_task_reopen", error: String(err) });
419
+ return {
420
+ content: [{ type: "text", text: `Error reopening task: ${msg}` }],
421
+ details: { operation: "reopen_task", error: msg },
422
+ isError: true,
423
+ };
424
+ }
425
+ }
426
+
427
+ export async function executeSliceReopen(
428
+ params: ReopenSliceExecutorParams,
429
+ basePath: string = process.cwd(),
430
+ ): Promise<ToolExecutionResult> {
431
+ const dbAvailable = await ensureDbOpen(basePath);
432
+ if (!dbAvailable) {
433
+ return {
434
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reopen slice." }],
435
+ details: { operation: "reopen_slice", error: "db_unavailable" },
436
+ isError: true,
437
+ };
438
+ }
439
+ try {
440
+ const result = await handleReopenSlice(params, basePath);
441
+ if ("error" in result) {
442
+ return {
443
+ content: [{ type: "text", text: `Error reopening slice: ${result.error}` }],
444
+ details: { operation: "reopen_slice", error: result.error },
445
+ isError: true,
446
+ };
447
+ }
448
+ return {
449
+ content: [{ type: "text", text: `Reopened slice ${result.sliceId} (${result.milestoneId})` }],
450
+ details: {
451
+ operation: "reopen_slice",
452
+ sliceId: result.sliceId,
453
+ milestoneId: result.milestoneId,
454
+ tasksReset: result.tasksReset,
455
+ },
456
+ };
457
+ } catch (err) {
458
+ const msg = err instanceof Error ? err.message : String(err);
459
+ logError("tool", `reopen_slice tool failed: ${msg}`, { tool: "gsd_slice_reopen", error: String(err) });
460
+ return {
461
+ content: [{ type: "text", text: `Error reopening slice: ${msg}` }],
462
+ details: { operation: "reopen_slice", error: msg },
463
+ isError: true,
464
+ };
465
+ }
466
+ }
467
+
468
+ export async function executeMilestoneReopen(
469
+ params: ReopenMilestoneExecutorParams,
470
+ basePath: string = process.cwd(),
471
+ ): Promise<ToolExecutionResult> {
472
+ const dbAvailable = await ensureDbOpen(basePath);
473
+ if (!dbAvailable) {
474
+ return {
475
+ content: [{ type: "text", text: "Error: GSD database is not available. Cannot reopen milestone." }],
476
+ details: { operation: "reopen_milestone", error: "db_unavailable" },
477
+ isError: true,
478
+ };
479
+ }
480
+ try {
481
+ const result = await handleReopenMilestone(params, basePath);
482
+ if ("error" in result) {
483
+ return {
484
+ content: [{ type: "text", text: `Error reopening milestone: ${result.error}` }],
485
+ details: { operation: "reopen_milestone", error: result.error },
486
+ isError: true,
487
+ };
488
+ }
489
+ return {
490
+ content: [{ type: "text", text: `Reopened milestone ${result.milestoneId}` }],
491
+ details: {
492
+ operation: "reopen_milestone",
493
+ milestoneId: result.milestoneId,
494
+ slicesReset: result.slicesReset,
495
+ tasksReset: result.tasksReset,
496
+ },
497
+ };
498
+ } catch (err) {
499
+ const msg = err instanceof Error ? err.message : String(err);
500
+ logError("tool", `reopen_milestone tool failed: ${msg}`, { tool: "gsd_milestone_reopen", error: String(err) });
501
+ return {
502
+ content: [{ type: "text", text: `Error reopening milestone: ${msg}` }],
503
+ details: { operation: "reopen_milestone", error: msg },
504
+ isError: true,
505
+ };
506
+ }
507
+ }
508
+
374
509
  export async function executeSliceComplete(
375
510
  params: SliceCompleteExecutorParams,
376
511
  basePath: string = process.cwd(),
@@ -132,6 +132,9 @@ export type ContextModePolicy =
132
132
  * the explicit `allowedPathGlobs` set; Bash safe-allowlist;
133
133
  * no subagents. Reserved for rewrite-docs, which legitimately
134
134
  * edits project markdown outside .gsd/.
135
+ * - "verification"
136
+ * — Read tools + Bash for verification commands, writes
137
+ * restricted to .gsd/**, no subagents.
135
138
  *
136
139
  * The allowlist for "docs" is declared per-manifest rather than hardcoded so
137
140
  * projects with non-standard doc layouts can extend it without forking the
@@ -143,7 +146,8 @@ export type ToolsPolicy =
143
146
  | { readonly mode: "read-only" }
144
147
  | { readonly mode: "planning" }
145
148
  | { readonly mode: "planning-dispatch"; readonly allowedSubagents: readonly string[] }
146
- | { readonly mode: "docs"; readonly allowedPathGlobs: readonly string[] };
149
+ | { readonly mode: "docs"; readonly allowedPathGlobs: readonly string[] }
150
+ | { readonly mode: "verification" };
147
151
 
148
152
  // ─── Computed-artifact registry (#4924 v2 contract) ───────────────────────
149
153
 
@@ -288,6 +292,7 @@ const COMMON_BUDGET_SMALL = 250_000; // ~65K tokens
288
292
 
289
293
  const TOOLS_ALL: ToolsPolicy = { mode: "all" };
290
294
  const TOOLS_PLANNING: ToolsPolicy = { mode: "planning" };
295
+ const TOOLS_VERIFICATION: ToolsPolicy = { mode: "verification" };
291
296
  // Like TOOLS_PLANNING but permits dispatch to read-only recon/planning
292
297
  // specialists. Runtime-enforced by write-gate.ts before the subagent tool runs.
293
298
  const TOOLS_PLANNING_DISPATCH_RECON: ToolsPolicy = {
@@ -406,8 +411,8 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
406
411
  contextMode: "verification",
407
412
  // planning-dispatch: validation is a verification-fan-out unit. It reads
408
413
  // the milestone surface and dispatches reviewer/security/tester subagents
409
- // to report findings without touching user source. Mirrors
410
- // complete-milestone's policy. Write isolation to .gsd/ is preserved.
414
+ // to report findings without touching user source. Write isolation to
415
+ // .gsd/ is preserved.
411
416
  tools: TOOLS_PLANNING_DISPATCH_REVIEW,
412
417
  artifacts: {
413
418
  inline: ["roadmap", "slice-summary", "slice-uat", "requirements", "decisions", "templates"],
@@ -423,11 +428,9 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
423
428
  codebaseMap: false,
424
429
  preferences: "active-only",
425
430
  contextMode: "verification",
426
- // planning-dispatch: completion is a high-leverage place to fan out to
427
- // reviewer / security / tester subagents. They read the diff and report
428
- // findings; they do not write user source. Write isolation to .gsd/ is
429
- // preserved.
430
- tools: TOOLS_PLANNING_DISPATCH_REVIEW,
431
+ // Milestone closeout must run unrestricted shell verification commands
432
+ // against the final diff before recording completion.
433
+ tools: TOOLS_ALL,
431
434
  artifacts: {
432
435
  // #4780 landed slice-summary as excerpt for this unit; phase 2 of
433
436
  // the architecture will read this manifest as the source of truth
@@ -447,7 +450,9 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
447
450
  codebaseMap: true,
448
451
  preferences: "active-only",
449
452
  contextMode: "research",
450
- tools: TOOLS_PLANNING,
453
+ // Multi-slice research dispatches use the research-slice unit contract to
454
+ // fan out scout subagents that write .gsd research artifacts.
455
+ tools: TOOLS_PLANNING_DISPATCH_RECON,
451
456
  artifacts: {
452
457
  inline: ["roadmap", "milestone-research", "dependency-summaries", "templates"],
453
458
  excerpt: [],
@@ -587,7 +592,7 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
587
592
  codebaseMap: false,
588
593
  preferences: "active-only",
589
594
  contextMode: "verification",
590
- tools: TOOLS_PLANNING,
595
+ tools: TOOLS_VERIFICATION,
591
596
  artifacts: {
592
597
  // Phase 3 migration (#4782): manifest matches today's actual
593
598
  // buildRunUatPrompt inlining. Prior phase-1 entry listed
@@ -606,7 +611,9 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
606
611
  codebaseMap: false,
607
612
  preferences: "active-only",
608
613
  contextMode: "verification",
609
- tools: TOOLS_PLANNING,
614
+ // Gate evaluation fans out tester-style subagents, which read the slice
615
+ // plan and report via the DB-backed gate-result tool.
616
+ tools: TOOLS_PLANNING_DISPATCH_REVIEW,
610
617
  artifacts: {
611
618
  inline: ["slice-plan", "prior-task-summaries"],
612
619
  excerpt: [],
@@ -734,3 +741,32 @@ export const UNIT_MANIFESTS: Record<UnitType, UnitContextManifest> = {
734
741
  export function resolveManifest(unitType: string): UnitContextManifest | null {
735
742
  return (UNIT_MANIFESTS as Record<string, UnitContextManifest>)[unitType] ?? null;
736
743
  }
744
+
745
+ export interface SubagentPermissionContract {
746
+ readonly allowed: boolean;
747
+ readonly allowedSubagents: readonly string[];
748
+ readonly toolsMode: ToolsPolicy["mode"] | "unknown";
749
+ }
750
+
751
+ export function compileSubagentPermissionContract(
752
+ policy: ToolsPolicy | null | undefined,
753
+ ): SubagentPermissionContract {
754
+ if (!policy) {
755
+ return { allowed: false, allowedSubagents: [], toolsMode: "unknown" };
756
+ }
757
+ if (policy.mode === "all") {
758
+ return { allowed: true, allowedSubagents: ["*"], toolsMode: policy.mode };
759
+ }
760
+ if (policy.mode === "planning-dispatch") {
761
+ return {
762
+ allowed: true,
763
+ allowedSubagents: [...policy.allowedSubagents],
764
+ toolsMode: policy.mode,
765
+ };
766
+ }
767
+ return { allowed: false, allowedSubagents: [], toolsMode: policy.mode };
768
+ }
769
+
770
+ export function resolveSubagentPermissionContract(unitType: string): SubagentPermissionContract {
771
+ return compileSubagentPermissionContract(resolveManifest(unitType)?.tools);
772
+ }
@@ -0,0 +1,47 @@
1
+ // Project/App: GSD-2
2
+ // File Purpose: Host-owned verification verdict policy for auto-mode units.
3
+
4
+ import type { VerificationResult as VerificationGateResult } from "./types.js";
5
+
6
+ export type VerificationVerdictReason =
7
+ | "passed"
8
+ | "no-host-checks"
9
+ | "checks-failed";
10
+
11
+ export interface VerificationVerdict {
12
+ passed: boolean;
13
+ reason: VerificationVerdictReason;
14
+ retryable: boolean;
15
+ failureContext: string;
16
+ }
17
+
18
+ export function decideVerificationVerdict(
19
+ unitType: string,
20
+ result: VerificationGateResult,
21
+ ): VerificationVerdict {
22
+ if (unitType === "execute-task" && result.discoverySource === "none" && result.checks.length === 0) {
23
+ return {
24
+ passed: false,
25
+ reason: "no-host-checks",
26
+ retryable: false,
27
+ failureContext:
28
+ "No runnable host-owned verification command was discovered. Add project verification_commands or a runnable task-plan Verify command before completing this execute-task.",
29
+ };
30
+ }
31
+
32
+ if (!result.passed) {
33
+ return {
34
+ passed: false,
35
+ reason: "checks-failed",
36
+ retryable: true,
37
+ failureContext: "",
38
+ };
39
+ }
40
+
41
+ return {
42
+ passed: true,
43
+ reason: "passed",
44
+ retryable: false,
45
+ failureContext: "",
46
+ };
47
+ }
@@ -76,6 +76,13 @@ import {
76
76
  teardownAutoWorktree,
77
77
  } from "./auto-worktree.js";
78
78
 
79
+ const recentWorktreeMergeFailures = new Map<string, number>();
80
+ const MERGE_FAILURE_DEDUPE_MS = 60_000;
81
+
82
+ export function resetRecentWorktreeMergeFailuresForTest(): void {
83
+ recentWorktreeMergeFailures.clear();
84
+ }
85
+
79
86
  // ─── Types ───────────────────────────────────────────────────────────────
80
87
 
81
88
  export interface NotifyCtx {
@@ -836,6 +843,32 @@ function rebuildGitService(
836
843
  s.gitService = deps.gitServiceFactory(s.basePath);
837
844
  }
838
845
 
846
+ function emitWorktreeMergeFailedOnce(
847
+ basePath: string,
848
+ milestoneId: string,
849
+ err: unknown,
850
+ ): void {
851
+ const msg = err instanceof Error ? err.message : String(err);
852
+ const errorCategory = err instanceof Error ? err.name : "Error";
853
+ const now = Date.now();
854
+ const key = `${basePath}\0${milestoneId}\0${errorCategory}`;
855
+ const previous = recentWorktreeMergeFailures.get(key);
856
+ if (previous && now - previous < MERGE_FAILURE_DEDUPE_MS) return;
857
+ for (const [candidate, ts] of recentWorktreeMergeFailures) {
858
+ if (now - ts >= MERGE_FAILURE_DEDUPE_MS) {
859
+ recentWorktreeMergeFailures.delete(candidate);
860
+ }
861
+ }
862
+ emitJournalEvent(basePath, {
863
+ ts: new Date().toISOString(),
864
+ flowId: randomUUID(),
865
+ seq: 0,
866
+ eventType: "worktree-merge-failed",
867
+ data: { milestoneId, error: msg },
868
+ });
869
+ recentWorktreeMergeFailures.set(key, now);
870
+ }
871
+
839
872
  // ─── Session-less merge entry (ADR-016 phase 2 / A1) ─────────────────────
840
873
 
841
874
  /**
@@ -985,13 +1018,7 @@ function _mergeWorktreeModeImpl(
985
1018
  error: msg,
986
1019
  fallback: "chdir-to-project-root",
987
1020
  });
988
- emitJournalEvent(originalBasePath || worktreeBasePath, {
989
- ts: new Date().toISOString(),
990
- flowId: randomUUID(),
991
- seq: 0,
992
- eventType: "worktree-merge-failed",
993
- data: { milestoneId, error: msg },
994
- });
1021
+ emitWorktreeMergeFailedOnce(originalBasePath || worktreeBasePath, milestoneId, err);
995
1022
  // Surface a clear, actionable error. Worktree and milestone branch
996
1023
  // are intentionally preserved — nothing has been deleted. User can
997
1024
  // retry /gsd dispatch complete-milestone or merge manually once the
@@ -1702,16 +1729,34 @@ export class WorktreeLifecycle {
1702
1729
  }
1703
1730
 
1704
1731
  /**
1705
- * Restore `s.basePath` to `s.originalBasePath` and rebuild `s.gitService`.
1706
- * No-op when `originalBasePath` is empty (fresh sessions).
1732
+ * Restore `s.basePath` to `s.originalBasePath`, chdir process cwd, and
1733
+ * rebuild `s.gitService`. No-op when `originalBasePath` is empty (fresh
1734
+ * sessions).
1707
1735
  *
1708
1736
  * Used by error/cleanup paths that need the session to behave as if the
1709
1737
  * worktree was never entered. Does NOT teardown the worktree directory —
1710
1738
  * callers that need teardown go through `exitMilestone({ merge: false })`.
1739
+ *
1740
+ * ADR-016 phase 3 (#5693): chdir lives inside the verb so callers do not
1741
+ * pair `restoreToProjectRoot()` with a redundant `process.chdir`. The
1742
+ * chdir runs BEFORE the throwable work (`rebuildGitService`, cache
1743
+ * invalidation) so that cleanup-path cwd is restored even if the
1744
+ * downstream rebuild throws. The chdir itself is best-effort; failure is
1745
+ * logged via debugLog and swallowed.
1711
1746
  */
1712
1747
  restoreToProjectRoot(): void {
1713
1748
  if (!this.s.originalBasePath) return;
1714
1749
  this.s.basePath = this.s.originalBasePath;
1750
+ try {
1751
+ process.chdir(this.s.basePath);
1752
+ } catch (err) {
1753
+ debugLog("WorktreeLifecycle", {
1754
+ action: "restoreToProjectRoot",
1755
+ result: "chdir-failed",
1756
+ basePath: this.s.basePath,
1757
+ error: err instanceof Error ? err.message : String(err),
1758
+ });
1759
+ }
1715
1760
  rebuildGitService(this.s, this.deps);
1716
1761
  invalidateAllCaches();
1717
1762
  }