gsd-pi 2.78.1-dev.b0759e59b → 2.78.1-dev.e9d88a536

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 (203) hide show
  1. package/README.md +8 -5
  2. package/dist/headless-recover.d.ts +23 -0
  3. package/dist/headless-recover.js +93 -0
  4. package/dist/headless.js +9 -0
  5. package/dist/help-text.js +1 -0
  6. package/dist/resources/.managed-resources-content-hash +1 -1
  7. package/dist/resources/extensions/browser-tools/tools/intent.js +8 -1
  8. package/dist/resources/extensions/gsd/auto-dispatch.js +4 -56
  9. package/dist/resources/extensions/gsd/auto-post-unit.js +7 -27
  10. package/dist/resources/extensions/gsd/auto-start.js +1 -8
  11. package/dist/resources/extensions/gsd/auto-worktree.js +59 -176
  12. package/dist/resources/extensions/gsd/auto.js +24 -6
  13. package/dist/resources/extensions/gsd/bootstrap/dynamic-tools.js +9 -77
  14. package/dist/resources/extensions/gsd/commands-codebase.js +2 -2
  15. package/dist/resources/extensions/gsd/commands-handlers.js +5 -5
  16. package/dist/resources/extensions/gsd/commands-logs.js +2 -2
  17. package/dist/resources/extensions/gsd/commands-scan.js +2 -2
  18. package/dist/resources/extensions/gsd/commands-ship.js +2 -2
  19. package/dist/resources/extensions/gsd/commands-workflow-templates.js +5 -5
  20. package/dist/resources/extensions/gsd/db-writer.js +16 -85
  21. package/dist/resources/extensions/gsd/dispatch-guard.js +6 -10
  22. package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -2
  23. package/dist/resources/extensions/gsd/gsd-db.js +74 -8
  24. package/dist/resources/extensions/gsd/guided-flow.js +31 -8
  25. package/dist/resources/extensions/gsd/markdown-renderer.js +14 -51
  26. package/dist/resources/extensions/gsd/parallel-merge.js +14 -13
  27. package/dist/resources/extensions/gsd/parallel-monitor-overlay.js +5 -2
  28. package/dist/resources/extensions/gsd/paths.js +35 -1
  29. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
  30. package/dist/resources/extensions/gsd/queue-order.js +6 -1
  31. package/dist/resources/extensions/gsd/rethink.js +2 -2
  32. package/dist/resources/extensions/gsd/state.js +91 -372
  33. package/dist/resources/extensions/gsd/tools/complete-milestone.js +6 -5
  34. package/dist/resources/extensions/gsd/tools/complete-slice.js +7 -12
  35. package/dist/resources/extensions/gsd/tools/complete-task.js +19 -31
  36. package/dist/resources/extensions/gsd/tools/validate-milestone.js +7 -5
  37. package/dist/resources/extensions/gsd/workflow-manifest.js +2 -1
  38. package/dist/resources/extensions/gsd/workflow-mcp-auto-prep.js +3 -21
  39. package/dist/resources/extensions/gsd/workflow-reconcile.js +3 -3
  40. package/dist/resources/extensions/gsd/worktree-command.js +4 -3
  41. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  42. package/dist/web/standalone/.next/BUILD_ID +1 -1
  43. package/dist/web/standalone/.next/app-path-routes-manifest.json +12 -12
  44. package/dist/web/standalone/.next/build-manifest.json +2 -2
  45. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  46. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  51. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  59. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/api/boot/route.js.nft.json +1 -1
  63. package/dist/web/standalone/.next/server/app/api/bridge-terminal/input/route.js.nft.json +1 -1
  64. package/dist/web/standalone/.next/server/app/api/bridge-terminal/resize/route.js.nft.json +1 -1
  65. package/dist/web/standalone/.next/server/app/api/bridge-terminal/stream/route.js.nft.json +1 -1
  66. package/dist/web/standalone/.next/server/app/api/captures/route.js.nft.json +1 -1
  67. package/dist/web/standalone/.next/server/app/api/cleanup/route.js.nft.json +1 -1
  68. package/dist/web/standalone/.next/server/app/api/doctor/route.js.nft.json +1 -1
  69. package/dist/web/standalone/.next/server/app/api/export-data/route.js.nft.json +1 -1
  70. package/dist/web/standalone/.next/server/app/api/files/route.js.nft.json +1 -1
  71. package/dist/web/standalone/.next/server/app/api/forensics/route.js.nft.json +1 -1
  72. package/dist/web/standalone/.next/server/app/api/git/route.js.nft.json +1 -1
  73. package/dist/web/standalone/.next/server/app/api/history/route.js.nft.json +1 -1
  74. package/dist/web/standalone/.next/server/app/api/hooks/route.js.nft.json +1 -1
  75. package/dist/web/standalone/.next/server/app/api/inspect/route.js.nft.json +1 -1
  76. package/dist/web/standalone/.next/server/app/api/knowledge/route.js.nft.json +1 -1
  77. package/dist/web/standalone/.next/server/app/api/live-state/route.js.nft.json +1 -1
  78. package/dist/web/standalone/.next/server/app/api/notifications/route.js.nft.json +1 -1
  79. package/dist/web/standalone/.next/server/app/api/onboarding/route.js.nft.json +1 -1
  80. package/dist/web/standalone/.next/server/app/api/projects/route.js.nft.json +1 -1
  81. package/dist/web/standalone/.next/server/app/api/recovery/route.js.nft.json +1 -1
  82. package/dist/web/standalone/.next/server/app/api/session/browser/route.js.nft.json +1 -1
  83. package/dist/web/standalone/.next/server/app/api/session/command/route.js.nft.json +1 -1
  84. package/dist/web/standalone/.next/server/app/api/session/events/route.js.nft.json +1 -1
  85. package/dist/web/standalone/.next/server/app/api/session/manage/route.js.nft.json +1 -1
  86. package/dist/web/standalone/.next/server/app/api/settings-data/route.js.nft.json +1 -1
  87. package/dist/web/standalone/.next/server/app/api/skill-health/route.js.nft.json +1 -1
  88. package/dist/web/standalone/.next/server/app/api/steer/route.js.nft.json +1 -1
  89. package/dist/web/standalone/.next/server/app/api/switch-root/route.js.nft.json +1 -1
  90. package/dist/web/standalone/.next/server/app/api/terminal/sessions/route.js.nft.json +1 -1
  91. package/dist/web/standalone/.next/server/app/api/terminal/stream/route.js.nft.json +1 -1
  92. package/dist/web/standalone/.next/server/app/api/undo/route.js.nft.json +1 -1
  93. package/dist/web/standalone/.next/server/app/api/visualizer/route.js.nft.json +1 -1
  94. package/dist/web/standalone/.next/server/app/index.html +1 -1
  95. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  96. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  97. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  98. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  99. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  100. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  101. package/dist/web/standalone/.next/server/app-paths-manifest.json +12 -12
  102. package/dist/web/standalone/.next/server/chunks/6336.js +1 -0
  103. package/dist/web/standalone/.next/server/chunks/6897.js +1 -1
  104. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  105. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  106. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  107. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  108. package/package.json +1 -1
  109. package/packages/mcp-server/dist/workflow-tools.d.ts +6 -0
  110. package/packages/mcp-server/dist/workflow-tools.d.ts.map +1 -1
  111. package/packages/mcp-server/dist/workflow-tools.js +56 -2
  112. package/packages/mcp-server/dist/workflow-tools.js.map +1 -1
  113. package/packages/mcp-server/src/parse-workflow-args.test.ts +80 -0
  114. package/packages/mcp-server/src/workflow-tools.ts +61 -2
  115. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -1
  116. package/src/resources/extensions/browser-tools/tools/intent.ts +13 -2
  117. package/src/resources/extensions/gsd/auto-dispatch.ts +4 -60
  118. package/src/resources/extensions/gsd/auto-post-unit.ts +7 -26
  119. package/src/resources/extensions/gsd/auto-start.ts +1 -8
  120. package/src/resources/extensions/gsd/auto-worktree.ts +61 -204
  121. package/src/resources/extensions/gsd/auto.ts +23 -6
  122. package/src/resources/extensions/gsd/bootstrap/dynamic-tools.ts +9 -84
  123. package/src/resources/extensions/gsd/commands-codebase.ts +2 -2
  124. package/src/resources/extensions/gsd/commands-handlers.ts +5 -5
  125. package/src/resources/extensions/gsd/commands-logs.ts +2 -2
  126. package/src/resources/extensions/gsd/commands-scan.ts +2 -2
  127. package/src/resources/extensions/gsd/commands-ship.ts +2 -2
  128. package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -5
  129. package/src/resources/extensions/gsd/db-writer.ts +16 -83
  130. package/src/resources/extensions/gsd/dispatch-guard.ts +6 -11
  131. package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -2
  132. package/src/resources/extensions/gsd/gsd-db.ts +85 -8
  133. package/src/resources/extensions/gsd/guided-flow.ts +35 -8
  134. package/src/resources/extensions/gsd/markdown-renderer.ts +13 -64
  135. package/src/resources/extensions/gsd/parallel-merge.ts +14 -13
  136. package/src/resources/extensions/gsd/parallel-monitor-overlay.ts +5 -2
  137. package/src/resources/extensions/gsd/paths.ts +55 -1
  138. package/src/resources/extensions/gsd/prompts/plan-milestone.md +6 -0
  139. package/src/resources/extensions/gsd/queue-order.ts +6 -1
  140. package/src/resources/extensions/gsd/rethink.ts +2 -2
  141. package/src/resources/extensions/gsd/state.ts +91 -389
  142. package/src/resources/extensions/gsd/tests/artifact-corruption-2630.test.ts +1 -0
  143. package/src/resources/extensions/gsd/tests/auto-paused-session-validation.test.ts +6 -0
  144. package/src/resources/extensions/gsd/tests/auto-remediate-slice-status.test.ts +21 -34
  145. package/src/resources/extensions/gsd/tests/complete-task-rollback-evidence.test.ts +6 -7
  146. package/src/resources/extensions/gsd/tests/complete-task.test.ts +8 -6
  147. package/src/resources/extensions/gsd/tests/completed-at-reconcile.test.ts +12 -27
  148. package/src/resources/extensions/gsd/tests/completed-units-metrics-sync.test.ts +18 -5
  149. package/src/resources/extensions/gsd/tests/db-path-worktree-symlink.test.ts +4 -4
  150. package/src/resources/extensions/gsd/tests/db-writer.test.ts +14 -16
  151. package/src/resources/extensions/gsd/tests/derive-state-crossval.test.ts +6 -5
  152. package/src/resources/extensions/gsd/tests/derive-state-db-disk-reconcile.test.ts +10 -38
  153. package/src/resources/extensions/gsd/tests/derive-state-db.test.ts +136 -56
  154. package/src/resources/extensions/gsd/tests/derive-state-draft.test.ts +3 -0
  155. package/src/resources/extensions/gsd/tests/derive-state-helpers.test.ts +119 -61
  156. package/src/resources/extensions/gsd/tests/derive-state.test.ts +4 -0
  157. package/src/resources/extensions/gsd/tests/dispatch-complete-milestone-guard.test.ts +6 -20
  158. package/src/resources/extensions/gsd/tests/dispatch-guard.test.ts +4 -5
  159. package/src/resources/extensions/gsd/tests/dispatcher-stuck-planning.test.ts +14 -15
  160. package/src/resources/extensions/gsd/tests/ensure-db-open.test.ts +11 -16
  161. package/src/resources/extensions/gsd/tests/escalation.test.ts +2 -1
  162. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +2 -1
  163. package/src/resources/extensions/gsd/tests/gsdroot-worktree-detection.test.ts +15 -36
  164. package/src/resources/extensions/gsd/tests/handler-worktree-write-isolation.test.ts +57 -0
  165. package/src/resources/extensions/gsd/tests/integration/parallel-merge.test.ts +15 -15
  166. package/src/resources/extensions/gsd/tests/integration/state-machine-edge-cases.test.ts +15 -5
  167. package/src/resources/extensions/gsd/tests/markdown-renderer.test.ts +14 -8
  168. package/src/resources/extensions/gsd/tests/md-importer.test.ts +2 -1
  169. package/src/resources/extensions/gsd/tests/memory-store.test.ts +3 -2
  170. package/src/resources/extensions/gsd/tests/park-milestone.test.ts +2 -0
  171. package/src/resources/extensions/gsd/tests/progressive-planning.test.ts +25 -16
  172. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +1 -0
  173. package/src/resources/extensions/gsd/tests/ready-phrase-no-files-4573.test.ts +184 -0
  174. package/src/resources/extensions/gsd/tests/register-hooks-compaction-checkpoint.test.ts +6 -1
  175. package/src/resources/extensions/gsd/tests/replan-slice.test.ts +3 -0
  176. package/src/resources/extensions/gsd/tests/resolve-ts.mjs +4 -0
  177. package/src/resources/extensions/gsd/tests/rogue-file-detection.test.ts +3 -4
  178. package/src/resources/extensions/gsd/tests/slice-disk-reconcile.test.ts +10 -56
  179. package/src/resources/extensions/gsd/tests/stale-slice-rows.test.ts +15 -16
  180. package/src/resources/extensions/gsd/tests/state-corruption-2945.test.ts +1 -0
  181. package/src/resources/extensions/gsd/tests/state-machine-full-walkthrough.test.ts +23 -27
  182. package/src/resources/extensions/gsd/tests/steer-worktree-path.test.ts +13 -14
  183. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +4 -3
  184. package/src/resources/extensions/gsd/tests/sync-worktree-skip-current.test.ts +10 -33
  185. package/src/resources/extensions/gsd/tests/validate-milestone-write-order.test.ts +7 -8
  186. package/src/resources/extensions/gsd/tests/validate-milestone.test.ts +9 -15
  187. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +12 -7
  188. package/src/resources/extensions/gsd/tests/workflow-mcp-auto-prep.test.ts +4 -4
  189. package/src/resources/extensions/gsd/tests/workflow-mcp.test.ts +24 -1
  190. package/src/resources/extensions/gsd/tests/worktree-db-same-file.test.ts +13 -0
  191. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +65 -71
  192. package/src/resources/extensions/gsd/tests/worktree-sync-tasks.test.ts +26 -151
  193. package/src/resources/extensions/gsd/tools/complete-milestone.ts +7 -5
  194. package/src/resources/extensions/gsd/tools/complete-slice.ts +7 -14
  195. package/src/resources/extensions/gsd/tools/complete-task.ts +19 -34
  196. package/src/resources/extensions/gsd/tools/validate-milestone.ts +7 -5
  197. package/src/resources/extensions/gsd/workflow-manifest.ts +4 -1
  198. package/src/resources/extensions/gsd/workflow-mcp-auto-prep.ts +2 -18
  199. package/src/resources/extensions/gsd/workflow-reconcile.ts +3 -3
  200. package/src/resources/extensions/gsd/worktree-command.ts +4 -3
  201. package/dist/web/standalone/.next/server/chunks/8527.js +0 -1
  202. /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_buildManifest.js +0 -0
  203. /package/dist/web/standalone/.next/static/{rk1EN3FQTE6Z1yalkW_GE → oZGTPvJBQX_IDKKnuV8Bt}/_ssgManifest.js +0 -0
@@ -145,16 +145,16 @@ export async function handleCompleteMilestone(params, basePath) {
145
145
  // This handles re-dispatch scenarios (DB/disk state divergence) where a prior
146
146
  // completion already wrote the file. Overwriting would silently destroy the
147
147
  // richer content the agent produced during the original completion run.
148
+ let projectionStale = false;
148
149
  if (!existsSync(summaryPath)) {
149
150
  try {
150
151
  await saveFile(summaryPath, summaryMd);
151
152
  }
152
153
  catch (renderErr) {
153
- // Disk render failed — roll back DB status so state stays consistent
154
- logWarning("tool", `complete_milestone disk render failed, rolling back DB status: ${renderErr.message}`);
155
- updateMilestoneStatus(params.milestoneId, 'active', null);
156
- invalidateStateCache();
157
- return { error: `disk render failed: ${renderErr.message}` };
154
+ projectionStale = true;
155
+ logWarning("projection", `complete_milestone projection write failed for ${params.milestoneId}; DB completion remains committed`, {
156
+ error: renderErr.message,
157
+ });
158
158
  }
159
159
  }
160
160
  // Invalidate all caches
@@ -192,5 +192,6 @@ export async function handleCompleteMilestone(params, basePath) {
192
192
  return {
193
193
  milestoneId: params.milestoneId,
194
194
  summaryPath,
195
+ ...(projectionStale ? { stale: true } : {}),
195
196
  };
196
197
  }
@@ -4,7 +4,8 @@
4
4
  * Validates inputs, checks all tasks are complete, writes slice row to DB in
5
5
  * a transaction, then (outside the transaction) renders SUMMARY.md + UAT.md
6
6
  * to disk, toggles the roadmap checkbox, stores rendered markdown in DB for
7
- * D004 recovery, and invalidates caches.
7
+ * D004 recovery, and invalidates caches. Projection write failures are stale
8
+ * projection diagnostics and do not roll back committed DB state.
8
9
  */
9
10
  import { join } from "node:path";
10
11
  import { mkdirSync } from "node:fs";
@@ -216,7 +217,6 @@ export async function handleCompleteSlice(params, basePath) {
216
217
  }
217
218
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
218
219
  const completedAt = new Date().toISOString();
219
- const originalSliceStatus = getSlice(params.milestoneId, params.sliceId)?.status ?? "pending";
220
220
  let guardError = null;
221
221
  transaction(() => {
222
222
  // State machine preconditions (inside txn for atomicity).
@@ -272,9 +272,6 @@ export async function handleCompleteSlice(params, basePath) {
272
272
  if (guardError) {
273
273
  return { error: guardError };
274
274
  }
275
- // ── Filesystem operations (outside transaction) ─────────────────────────
276
- // If disk render fails, roll back the DB status so deriveState() and
277
- // verifyExpectedArtifact() stay consistent (both say "not done").
278
275
  // Render summary markdown
279
276
  const summaryMd = renderSliceSummaryMarkdown(params);
280
277
  // Resolve and write summary to disk
@@ -292,6 +289,8 @@ export async function handleCompleteSlice(params, basePath) {
292
289
  }
293
290
  const uatMd = renderUatMarkdown(params);
294
291
  const uatPath = summaryPath.replace(/-SUMMARY\.md$/, "-UAT.md");
292
+ setSliceSummaryMd(params.milestoneId, params.sliceId, summaryMd, uatMd);
293
+ let projectionStale = false;
295
294
  try {
296
295
  await saveFile(summaryPath, summaryMd);
297
296
  await saveFile(uatPath, uatMd);
@@ -302,14 +301,9 @@ export async function handleCompleteSlice(params, basePath) {
302
301
  }
303
302
  }
304
303
  catch (renderErr) {
305
- // Disk render failed — roll back DB status so state stays consistent
306
- logWarning("tool", `complete_slice disk render failed for ${params.milestoneId}/${params.sliceId}, rolling back DB status`, { error: renderErr.message });
307
- updateSliceStatus(params.milestoneId, params.sliceId, originalSliceStatus);
308
- invalidateStateCache();
309
- return { error: `disk render failed: ${renderErr.message}` };
304
+ projectionStale = true;
305
+ logWarning("projection", `complete_slice projection write failed for ${params.milestoneId}/${params.sliceId}; DB completion remains committed`, { error: renderErr.message });
310
306
  }
311
- // Store rendered markdown in DB for D004 recovery
312
- setSliceSummaryMd(params.milestoneId, params.sliceId, summaryMd, uatMd);
313
307
  // ── Close gates owned by complete-slice (Q8) ───────────────────────────
314
308
  // Each owned gate maps to a specific summary section via the registry.
315
309
  // If the caller populated the corresponding field, record `pass`; if the
@@ -400,5 +394,6 @@ export async function handleCompleteSlice(params, basePath) {
400
394
  milestoneId: params.milestoneId,
401
395
  summaryPath,
402
396
  uatPath,
397
+ ...(projectionStale ? { stale: true } : {}),
403
398
  };
404
399
  }
@@ -1,17 +1,17 @@
1
1
  /**
2
2
  * complete-task handler — the core operation behind gsd_complete_task.
3
3
  *
4
- * Validates inputs, writes task row to DB in a transaction, then (outside
5
- * the transaction) renders SUMMARY.md to disk, toggles the plan checkbox,
6
- * stores the rendered markdown in the DB for D004 recovery, and invalidates
7
- * caches.
4
+ * Validates inputs, writes task row and rendered SUMMARY.md to DB in a
5
+ * transaction, then renders projections to disk and invalidates caches.
6
+ * Projection write failures are reported as stale projections and do not roll
7
+ * back committed DB state.
8
8
  */
9
9
  import { join } from "node:path";
10
10
  import { mkdirSync } from "node:fs";
11
11
  import { isClosedStatus } from "../status-guards.js";
12
- import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, setTaskSummaryMd, deleteVerificationEvidence, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
12
+ import { transaction, insertMilestone, insertSlice, insertTask, insertVerificationEvidence, getMilestone, getSlice, getTask, updateTaskStatus, deleteVerificationEvidence, saveGateResult, getPendingGatesForTurn, } from "../gsd-db.js";
13
13
  import { getGatesForTurn } from "../gate-registry.js";
14
- import { resolveSliceFile, resolveTasksDir, clearPathCache } from "../paths.js";
14
+ import { resolveTasksDir, clearPathCache } from "../paths.js";
15
15
  import { checkOwnership, taskUnitKey } from "../unit-ownership.js";
16
16
  import { saveFile, clearParseCache } from "../files.js";
17
17
  import { invalidateStateCache } from "../state.js";
@@ -119,6 +119,7 @@ export async function handleCompleteTask(params, basePath) {
119
119
  // ── Guards + DB writes inside a single transaction (prevents TOCTOU) ───
120
120
  const completedAt = new Date().toISOString();
121
121
  let guardError = null;
122
+ let summaryMd = "";
122
123
  // ── ADR-011 Phase 2: validate escalation payload BEFORE any side effects ─
123
124
  // Building the artifact runs the full shape validation (2-4 options, unique
124
125
  // ids, recommendation references a real id). If the payload is malformed
@@ -184,6 +185,8 @@ export async function handleCompleteTask(params, basePath) {
184
185
  return;
185
186
  }
186
187
  // All guards passed — perform writes
188
+ const taskRow = paramsToTaskRow(params, completedAt);
189
+ summaryMd = renderSummaryContent(taskRow, params.sliceId, params.milestoneId, params.verificationEvidence ?? []);
187
190
  insertMilestone({ id: params.milestoneId, title: params.milestoneId });
188
191
  insertSlice({ id: params.sliceId, milestoneId: params.milestoneId, title: params.sliceId });
189
192
  insertTask({
@@ -201,6 +204,7 @@ export async function handleCompleteTask(params, basePath) {
201
204
  knownIssues: params.knownIssues ?? "None.",
202
205
  keyFiles: params.keyFiles ?? [],
203
206
  keyDecisions: params.keyDecisions ?? [],
207
+ fullSummaryMd: summaryMd,
204
208
  });
205
209
  for (const evidence of (params.verificationEvidence ?? [])) {
206
210
  insertVerificationEvidence({
@@ -235,12 +239,7 @@ export async function handleCompleteTask(params, basePath) {
235
239
  if (guardError) {
236
240
  return { error: guardError };
237
241
  }
238
- // ── Filesystem operations (outside transaction) ─────────────────────────
239
- // If disk render fails, roll back the DB status so deriveState() and
240
- // verifyExpectedArtifact() stay consistent (both say "not done").
241
- // Render summary markdown via the single source of truth (#2720)
242
- const taskRow = paramsToTaskRow(params, completedAt);
243
- const summaryMd = renderSummaryContent(taskRow, params.sliceId, params.milestoneId, params.verificationEvidence ?? []);
242
+ let projectionStale = false;
244
243
  // Resolve and write summary to disk
245
244
  let summaryPath;
246
245
  const tasksDir = resolveTasksDir(basePath, params.milestoneId, params.sliceId);
@@ -256,28 +255,16 @@ export async function handleCompleteTask(params, basePath) {
256
255
  }
257
256
  try {
258
257
  await saveFile(summaryPath, summaryMd);
259
- // Toggle plan checkbox via renderer module
260
- const planPath = resolveSliceFile(basePath, params.milestoneId, params.sliceId, "PLAN");
261
- if (planPath) {
262
- await renderPlanCheckboxes(basePath, params.milestoneId, params.sliceId);
263
- }
264
- else {
265
- process.stderr.write(`gsd-db: complete_task — could not find plan file for ${params.sliceId}/${params.milestoneId}, skipping checkbox toggle\n`);
266
- }
258
+ // Toggle or regenerate the plan projection from DB. Missing projection
259
+ // files are rebuilt by the renderer instead of being skipped.
260
+ await renderPlanCheckboxes(basePath, params.milestoneId, params.sliceId);
267
261
  }
268
262
  catch (renderErr) {
269
- // Disk render failed — roll back DB status so state stays consistent
270
- logWarning("tool", `complete_task disk render failed, rolling back DB status: ${renderErr.message}`);
271
- // Delete orphaned verification_evidence rows first (FK constraint
272
- // references tasks, so evidence must go before status change).
273
- // Without this, retries accumulate duplicate evidence rows (#2724).
274
- deleteVerificationEvidence(params.milestoneId, params.sliceId, params.taskId);
275
- updateTaskStatus(params.milestoneId, params.sliceId, params.taskId, 'pending');
276
- invalidateStateCache();
277
- return { error: `disk render failed: ${renderErr.message}` };
263
+ projectionStale = true;
264
+ logWarning("projection", `complete_task projection write failed for ${params.milestoneId}/${params.sliceId}/${params.taskId}; DB completion remains committed`, {
265
+ error: renderErr.message,
266
+ });
278
267
  }
279
- // Store rendered markdown in DB for D004 recovery
280
- setTaskSummaryMd(params.milestoneId, params.sliceId, params.taskId, summaryMd);
281
268
  // ── Close gates owned by execute-task (Q5/Q6/Q7) for this task ────────
282
269
  // Each gate id maps to a specific params field via taskGateFieldForId.
283
270
  // When the model populates the field, record `pass`; when it's empty,
@@ -387,5 +374,6 @@ export async function handleCompleteTask(params, basePath) {
387
374
  sliceId: params.sliceId,
388
375
  milestoneId: params.milestoneId,
389
376
  summaryPath,
377
+ ...(projectionStale ? { stale: true } : {}),
390
378
  };
391
379
  }
@@ -9,7 +9,7 @@
9
9
  * despite passing validation.
10
10
  */
11
11
  import { join } from "node:path";
12
- import { transaction, insertAssessment, deleteAssessmentByScope, getMilestoneSlices, } from "../gsd-db.js";
12
+ import { transaction, insertAssessment, getMilestoneSlices, } from "../gsd-db.js";
13
13
  import { resolveMilestonePath, clearPathCache } from "../paths.js";
14
14
  import { resolveCanonicalMilestoneRoot } from "../worktree-manager.js";
15
15
  import { saveFile, clearParseCache } from "../files.js";
@@ -99,14 +99,15 @@ export async function handleValidateMilestone(params, basePath, opts) {
99
99
  insertMilestoneValidationGates(params.milestoneId, gateSliceId, params.verdict, validatedAt);
100
100
  });
101
101
  // ── Filesystem render (outside transaction) ────────────────────────────
102
- // If disk render fails, roll back the DB row so state stays consistent.
102
+ let projectionStale = false;
103
103
  try {
104
104
  await saveFile(validationPath, validationMd);
105
105
  }
106
106
  catch (renderErr) {
107
- logWarning("tool", `validate_milestone — disk render failed, rolling back DB row: ${renderErr.message}`);
108
- deleteAssessmentByScope(params.milestoneId, 'milestone-validation');
109
- return { error: `disk render failed: ${renderErr.message}` };
107
+ projectionStale = true;
108
+ logWarning("projection", `validate_milestone projection write failed for ${params.milestoneId}; DB validation remains committed`, {
109
+ error: renderErr.message,
110
+ });
110
111
  }
111
112
  invalidateStateCache();
112
113
  clearPathCache();
@@ -147,5 +148,6 @@ export async function handleValidateMilestone(params, basePath, opts) {
147
148
  milestoneId: params.milestoneId,
148
149
  verdict: params.verdict,
149
150
  validationPath,
151
+ ...(projectionStale ? { stale: true } : {}),
150
152
  };
151
153
  }
@@ -42,7 +42,7 @@ export function snapshotState() {
42
42
  // Wrap all reads in a deferred transaction so the snapshot is consistent
43
43
  // (all SELECTs see the same DB state even if a concurrent write lands between them).
44
44
  return readTransaction(() => {
45
- const rawMilestones = db.prepare("SELECT * FROM milestones ORDER BY id").all();
45
+ const rawMilestones = db.prepare("SELECT * FROM milestones ORDER BY CASE WHEN sequence > 0 THEN 0 ELSE 1 END, sequence, id").all();
46
46
  const milestones = rawMilestones.map((r) => ({
47
47
  id: r["id"],
48
48
  title: r["title"],
@@ -61,6 +61,7 @@ export function snapshotState() {
61
61
  definition_of_done: JSON.parse(r["definition_of_done"] || "[]"),
62
62
  requirement_coverage: r["requirement_coverage"] ?? "",
63
63
  boundary_map_markdown: r["boundary_map_markdown"] ?? "",
64
+ sequence: Number(r["sequence"] ?? 0),
64
65
  }));
65
66
  const rawSlices = db.prepare("SELECT * FROM slices ORDER BY milestone_id, sequence, id").all();
66
67
  const slices = rawSlices.map((r) => ({
@@ -13,31 +13,13 @@ function getAuthModeSafe(ctx, provider) {
13
13
  return undefined;
14
14
  }
15
15
  }
16
- function hasClaudeCodeProvider(ctx) {
17
- return getAuthModeSafe(ctx, "claude-code") === "externalCli";
18
- }
19
- function isClaudeCodeProviderReady(ctx) {
20
- const readyCheck = ctx.modelRegistry?.isProviderRequestReady;
21
- if (typeof readyCheck !== "function")
22
- return false;
23
- try {
24
- return readyCheck("claude-code");
25
- }
26
- catch {
27
- return false;
28
- }
29
- }
30
16
  export function shouldAutoPrepareWorkflowMcp(ctx) {
31
17
  const provider = ctx.model?.provider;
32
18
  const baseUrl = ctx.model?.baseUrl;
33
19
  const authMode = getAuthModeSafe(ctx, provider);
34
- if (usesWorkflowMcpTransport(authMode, baseUrl))
35
- return true;
36
- if (provider === "claude-code")
37
- return true;
38
- if (hasClaudeCodeProvider(ctx))
39
- return true;
40
- return isClaudeCodeProviderReady(ctx);
20
+ if (provider !== "claude-code")
21
+ return false;
22
+ return usesWorkflowMcpTransport(authMode, baseUrl) || authMode === "externalCli";
41
23
  }
42
24
  export function prepareWorkflowMcpForProject(ctx, projectRoot) {
43
25
  if (!shouldAutoPrepareWorkflowMcp(ctx))
@@ -5,7 +5,7 @@ import { readEvents, findForkPoint, getSessionId } from "./workflow-events.js";
5
5
  import { transaction, updateTaskStatus, updateSliceStatus, updateMilestoneStatus, getSliceTasks, insertMilestone, getMilestoneSlices, insertVerificationEvidence, upsertDecision, openDatabase, setTaskBlockerDiscovered, insertOrIgnoreSlice, insertOrIgnoreTask, } from "./gsd-db.js";
6
6
  import { isClosedStatus } from "./status-guards.js";
7
7
  import { invalidateStateCache } from "./state.js";
8
- import { clearPathCache } from "./paths.js";
8
+ import { clearPathCache, resolveGsdPathContract } from "./paths.js";
9
9
  import { clearParseCache } from "./files.js";
10
10
  import { writeManifest } from "./workflow-manifest.js";
11
11
  import { atomicWriteSync } from "./atomic-write.js";
@@ -407,7 +407,7 @@ function _reconcileWorktreeLogsInner(mainBasePath, worktreeBasePath) {
407
407
  mkdirSync(join(mainBasePath, ".gsd"), { recursive: true });
408
408
  atomicWriteSync(join(mainBasePath, ".gsd", "event-log.jsonl"), logContent);
409
409
  // Step 8: Replay into DB (wrapped in a transaction by replayEvents)
410
- openDatabase(join(mainBasePath, ".gsd", "gsd.db"));
410
+ openDatabase(resolveGsdPathContract(mainBasePath).projectDb);
411
411
  replayEvents(merged);
412
412
  // Step 9: Write manifest
413
413
  try {
@@ -540,7 +540,7 @@ pick) {
540
540
  const targetBaseEvents = pick === "main" ? wtBaseEvents : mainBaseEvents;
541
541
  writeEventLog(targetBasePath, targetBaseEvents.concat(rewrittenTargetEvents));
542
542
  // Replay resolved events through the DB (updates DB state)
543
- openDatabase(join(basePath, ".gsd", "gsd.db"));
543
+ openDatabase(resolveGsdPathContract(basePath).projectDb);
544
544
  replayEvents(eventsToReplay);
545
545
  invalidateStateCache();
546
546
  clearPathCache();
@@ -13,7 +13,7 @@ import { loadPrompt } from "./prompt-loader.js";
13
13
  import { autoCommitCurrentBranch, getMainBranch, nudgeGitBranchCache } from "./worktree.js";
14
14
  import { runWorktreePostCreateHook } from "./auto-worktree.js";
15
15
  import { showConfirm } from "../shared/tui.js";
16
- import { gsdRoot, milestonesDir } from "./paths.js";
16
+ import { gsdRoot, milestonesDir, resolveGsdPathContract } from "./paths.js";
17
17
  import { createWorktree, listWorktrees, removeWorktree, mergeWorktreeToMain, diffWorktreeAll, diffWorktreeNumstat, getWorktreeGSDDiff, getWorktreeCodeDiff, getWorktreeLog, worktreeBranchName, worktreePath, } from "./worktree-manager.js";
18
18
  import { inferCommitType } from "./git-service.js";
19
19
  import { existsSync, realpathSync, readdirSync, rmSync, unlinkSync } from "node:fs";
@@ -530,8 +530,9 @@ async function handleMerge(basePath, name, ctx, pi, targetBranch) {
530
530
  const commitType = inferCommitType(name);
531
531
  const commitMessage = `${commitType}: merge worktree ${name}\n\nGSD-Worktree: ${name}`;
532
532
  // Reconcile worktree DB into main DB before squash merge
533
- const wtDbPath = join(worktreePath(basePath, name), ".gsd", "gsd.db");
534
- const mainDbPath = join(basePath, ".gsd", "gsd.db");
533
+ const contract = resolveGsdPathContract(worktreePath(basePath, name), basePath);
534
+ const wtDbPath = join(contract.worktreeGsd ?? join(contract.workRoot, ".gsd"), "gsd.db");
535
+ const mainDbPath = contract.projectDb;
535
536
  if (existsSync(wtDbPath) && existsSync(mainDbPath)) {
536
537
  try {
537
538
  const { reconcileWorktreeDb } = await import("./gsd-db.js");