gsd-pi 2.45.0-dev.fdcf73c → 2.46.0-dev.cc9d310

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 (180) hide show
  1. package/dist/resources/extensions/gsd/auto/phases.js +14 -35
  2. package/dist/resources/extensions/gsd/auto/session.js +0 -11
  3. package/dist/resources/extensions/gsd/auto-artifact-paths.js +112 -0
  4. package/dist/resources/extensions/gsd/auto-post-unit.js +25 -96
  5. package/dist/resources/extensions/gsd/auto-start.js +2 -3
  6. package/dist/resources/extensions/gsd/auto.js +8 -52
  7. package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +18 -0
  8. package/dist/resources/extensions/gsd/commands/context.js +0 -4
  9. package/dist/resources/extensions/gsd/commands/handlers/parallel.js +1 -1
  10. package/dist/resources/extensions/gsd/crash-recovery.js +2 -4
  11. package/dist/resources/extensions/gsd/dashboard-overlay.js +0 -44
  12. package/dist/resources/extensions/gsd/doctor-checks.js +166 -1
  13. package/dist/resources/extensions/gsd/doctor.js +3 -1
  14. package/dist/resources/extensions/gsd/gsd-db.js +11 -2
  15. package/dist/resources/extensions/gsd/guided-flow.js +1 -2
  16. package/dist/resources/extensions/gsd/parallel-merge.js +1 -1
  17. package/dist/resources/extensions/gsd/parallel-orchestrator.js +5 -18
  18. package/dist/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  19. package/dist/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  20. package/dist/resources/extensions/gsd/prompts/discuss.md +2 -2
  21. package/dist/resources/extensions/gsd/prompts/execute-task.md +5 -15
  22. package/dist/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  23. package/dist/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  24. package/dist/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  25. package/dist/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  26. package/dist/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  27. package/dist/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  28. package/dist/resources/extensions/gsd/prompts/queue.md +2 -2
  29. package/dist/resources/extensions/gsd/prompts/quick-task.md +2 -0
  30. package/dist/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  31. package/dist/resources/extensions/gsd/prompts/research-slice.md +3 -3
  32. package/dist/resources/extensions/gsd/prompts/rethink.md +7 -2
  33. package/dist/resources/extensions/gsd/prompts/system.md +1 -1
  34. package/dist/resources/extensions/gsd/session-lock.js +1 -3
  35. package/dist/resources/extensions/gsd/state.js +7 -0
  36. package/dist/resources/extensions/gsd/sync-lock.js +89 -0
  37. package/dist/resources/extensions/gsd/tools/complete-milestone.js +58 -12
  38. package/dist/resources/extensions/gsd/tools/complete-slice.js +56 -11
  39. package/dist/resources/extensions/gsd/tools/complete-task.js +50 -2
  40. package/dist/resources/extensions/gsd/tools/plan-milestone.js +37 -1
  41. package/dist/resources/extensions/gsd/tools/plan-slice.js +30 -1
  42. package/dist/resources/extensions/gsd/tools/plan-task.js +27 -1
  43. package/dist/resources/extensions/gsd/tools/reassess-roadmap.js +32 -2
  44. package/dist/resources/extensions/gsd/tools/reopen-slice.js +86 -0
  45. package/dist/resources/extensions/gsd/tools/reopen-task.js +90 -0
  46. package/dist/resources/extensions/gsd/tools/replan-slice.js +32 -2
  47. package/dist/resources/extensions/gsd/unit-ownership.js +85 -0
  48. package/dist/resources/extensions/gsd/workflow-events.js +102 -0
  49. package/dist/resources/extensions/gsd/workflow-logger.js +56 -1
  50. package/dist/resources/extensions/gsd/workflow-manifest.js +244 -0
  51. package/dist/resources/extensions/gsd/workflow-migration.js +280 -0
  52. package/dist/resources/extensions/gsd/workflow-projections.js +373 -0
  53. package/dist/resources/extensions/gsd/workflow-reconcile.js +411 -0
  54. package/dist/resources/extensions/gsd/write-intercept.js +84 -0
  55. package/dist/web/standalone/.next/BUILD_ID +1 -1
  56. package/dist/web/standalone/.next/app-path-routes-manifest.json +17 -17
  57. package/dist/web/standalone/.next/build-manifest.json +2 -2
  58. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  59. package/dist/web/standalone/.next/server/app/_global-error.html +2 -2
  60. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  66. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  67. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  68. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  69. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  70. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  71. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  72. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  73. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  74. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  75. package/dist/web/standalone/.next/server/app/index.html +1 -1
  76. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  77. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  78. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  79. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  80. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  81. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  82. package/dist/web/standalone/.next/server/app-paths-manifest.json +17 -17
  83. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  84. package/dist/web/standalone/.next/server/pages/500.html +2 -2
  85. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  86. package/package.json +1 -1
  87. package/packages/pi-coding-agent/package.json +1 -1
  88. package/pkg/package.json +1 -1
  89. package/src/resources/extensions/gsd/auto/loop-deps.ts +0 -19
  90. package/src/resources/extensions/gsd/auto/phases.ts +11 -35
  91. package/src/resources/extensions/gsd/auto/session.ts +0 -18
  92. package/src/resources/extensions/gsd/auto-artifact-paths.ts +131 -0
  93. package/src/resources/extensions/gsd/auto-dashboard.ts +0 -1
  94. package/src/resources/extensions/gsd/auto-post-unit.ts +25 -106
  95. package/src/resources/extensions/gsd/auto-start.ts +1 -3
  96. package/src/resources/extensions/gsd/auto.ts +4 -80
  97. package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +22 -0
  98. package/src/resources/extensions/gsd/commands/context.ts +0 -5
  99. package/src/resources/extensions/gsd/commands/handlers/parallel.ts +1 -1
  100. package/src/resources/extensions/gsd/crash-recovery.ts +1 -5
  101. package/src/resources/extensions/gsd/dashboard-overlay.ts +0 -50
  102. package/src/resources/extensions/gsd/doctor-checks.ts +179 -1
  103. package/src/resources/extensions/gsd/doctor-types.ts +7 -1
  104. package/src/resources/extensions/gsd/doctor.ts +4 -1
  105. package/src/resources/extensions/gsd/gsd-db.ts +11 -2
  106. package/src/resources/extensions/gsd/guided-flow.ts +1 -2
  107. package/src/resources/extensions/gsd/parallel-merge.ts +1 -1
  108. package/src/resources/extensions/gsd/parallel-orchestrator.ts +5 -21
  109. package/src/resources/extensions/gsd/prompts/complete-milestone.md +1 -1
  110. package/src/resources/extensions/gsd/prompts/complete-slice.md +10 -23
  111. package/src/resources/extensions/gsd/prompts/discuss.md +2 -2
  112. package/src/resources/extensions/gsd/prompts/execute-task.md +5 -15
  113. package/src/resources/extensions/gsd/prompts/guided-discuss-milestone.md +1 -1
  114. package/src/resources/extensions/gsd/prompts/guided-discuss-slice.md +1 -1
  115. package/src/resources/extensions/gsd/prompts/guided-plan-slice.md +1 -1
  116. package/src/resources/extensions/gsd/prompts/guided-research-slice.md +1 -1
  117. package/src/resources/extensions/gsd/prompts/plan-milestone.md +1 -1
  118. package/src/resources/extensions/gsd/prompts/plan-slice.md +4 -2
  119. package/src/resources/extensions/gsd/prompts/queue.md +2 -2
  120. package/src/resources/extensions/gsd/prompts/quick-task.md +2 -0
  121. package/src/resources/extensions/gsd/prompts/reactive-execute.md +1 -1
  122. package/src/resources/extensions/gsd/prompts/research-slice.md +3 -3
  123. package/src/resources/extensions/gsd/prompts/rethink.md +7 -2
  124. package/src/resources/extensions/gsd/prompts/system.md +1 -1
  125. package/src/resources/extensions/gsd/session-lock.ts +0 -4
  126. package/src/resources/extensions/gsd/state.ts +8 -0
  127. package/src/resources/extensions/gsd/sync-lock.ts +94 -0
  128. package/src/resources/extensions/gsd/tests/auto-lock-creation.test.ts +5 -13
  129. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +6 -10
  130. package/src/resources/extensions/gsd/tests/complete-slice.test.ts +264 -228
  131. package/src/resources/extensions/gsd/tests/complete-task.test.ts +317 -250
  132. package/src/resources/extensions/gsd/tests/crash-recovery.test.ts +2 -8
  133. package/src/resources/extensions/gsd/tests/custom-engine-loop-integration.test.ts +0 -3
  134. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +1 -1
  135. package/src/resources/extensions/gsd/tests/idle-recovery.test.ts +1 -1
  136. package/src/resources/extensions/gsd/tests/integration-proof.test.ts +15 -24
  137. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +0 -3
  138. package/src/resources/extensions/gsd/tests/md-importer.test.ts +1 -1
  139. package/src/resources/extensions/gsd/tests/memory-store.test.ts +2 -2
  140. package/src/resources/extensions/gsd/tests/milestone-transition-state-rebuild.test.ts +8 -9
  141. package/src/resources/extensions/gsd/tests/parallel-budget-atomicity.test.ts +0 -1
  142. package/src/resources/extensions/gsd/tests/parallel-crash-recovery.test.ts +0 -7
  143. package/src/resources/extensions/gsd/tests/parallel-merge.test.ts +7 -8
  144. package/src/resources/extensions/gsd/tests/parallel-orchestration.test.ts +20 -24
  145. package/src/resources/extensions/gsd/tests/parallel-worker-monitoring.test.ts +0 -2
  146. package/src/resources/extensions/gsd/tests/plan-milestone.test.ts +9 -6
  147. package/src/resources/extensions/gsd/tests/post-mutation-hook.test.ts +171 -0
  148. package/src/resources/extensions/gsd/tests/projection-regression.test.ts +174 -0
  149. package/src/resources/extensions/gsd/tests/prompt-contracts.test.ts +15 -14
  150. package/src/resources/extensions/gsd/tests/reopen-slice.test.ts +155 -0
  151. package/src/resources/extensions/gsd/tests/reopen-task.test.ts +165 -0
  152. package/src/resources/extensions/gsd/tests/session-lock-regression.test.ts +1 -4
  153. package/src/resources/extensions/gsd/tests/stop-auto-remote.test.ts +2 -3
  154. package/src/resources/extensions/gsd/tests/sync-lock.test.ts +122 -0
  155. package/src/resources/extensions/gsd/tests/unit-ownership.test.ts +175 -0
  156. package/src/resources/extensions/gsd/tests/workflow-events.test.ts +205 -0
  157. package/src/resources/extensions/gsd/tests/workflow-manifest.test.ts +186 -0
  158. package/src/resources/extensions/gsd/tests/workflow-projections.test.ts +171 -0
  159. package/src/resources/extensions/gsd/tests/write-intercept.test.ts +76 -0
  160. package/src/resources/extensions/gsd/tools/complete-milestone.ts +70 -13
  161. package/src/resources/extensions/gsd/tools/complete-slice.ts +68 -11
  162. package/src/resources/extensions/gsd/tools/complete-task.ts +63 -1
  163. package/src/resources/extensions/gsd/tools/plan-milestone.ts +45 -0
  164. package/src/resources/extensions/gsd/tools/plan-slice.ts +38 -0
  165. package/src/resources/extensions/gsd/tools/plan-task.ts +35 -1
  166. package/src/resources/extensions/gsd/tools/reassess-roadmap.ts +39 -1
  167. package/src/resources/extensions/gsd/tools/reopen-slice.ts +125 -0
  168. package/src/resources/extensions/gsd/tools/reopen-task.ts +129 -0
  169. package/src/resources/extensions/gsd/tools/replan-slice.ts +38 -1
  170. package/src/resources/extensions/gsd/types.ts +8 -0
  171. package/src/resources/extensions/gsd/unit-ownership.ts +104 -0
  172. package/src/resources/extensions/gsd/workflow-events.ts +154 -0
  173. package/src/resources/extensions/gsd/workflow-logger.ts +51 -1
  174. package/src/resources/extensions/gsd/workflow-manifest.ts +334 -0
  175. package/src/resources/extensions/gsd/workflow-migration.ts +345 -0
  176. package/src/resources/extensions/gsd/workflow-projections.ts +425 -0
  177. package/src/resources/extensions/gsd/workflow-reconcile.ts +503 -0
  178. package/src/resources/extensions/gsd/write-intercept.ts +90 -0
  179. /package/dist/web/standalone/.next/static/{zWYDSwB-terOjfhmWzqk1 → ZIDqryyYDroh_8AnaAOSG}/_buildManifest.js +0 -0
  180. /package/dist/web/standalone/.next/static/{zWYDSwB-terOjfhmWzqk1 → ZIDqryyYDroh_8AnaAOSG}/_ssgManifest.js +0 -0
@@ -30,14 +30,10 @@ export async function guardRemoteSession(ctx, pi) {
30
30
  `Stop it first with /gsd stop, or use /gsd steer to redirect it.`, "warning");
31
31
  return false;
32
32
  }
33
- const unitsMsg = remote.completedUnits != null
34
- ? `${remote.completedUnits} units completed`
35
- : "";
36
33
  const choice = await showNextAction(ctx, {
37
34
  title: `Auto-mode is running in another terminal (PID ${remote.pid})`,
38
35
  summary: [
39
36
  `Currently executing: ${unitLabel}`,
40
- ...(unitsMsg ? [unitsMsg] : []),
41
37
  ...(remote.startedAt ? [`Started: ${remote.startedAt}`] : []),
42
38
  ],
43
39
  actions: [
@@ -44,7 +44,7 @@ export async function handleParallelCommand(trimmed, _ctx, pi) {
44
44
  }
45
45
  const lines = ["# Parallel Workers\n"];
46
46
  for (const worker of workers) {
47
- lines.push(`- **${worker.milestoneId}** (${worker.title}) — ${worker.state} — ${worker.completedUnits} units — $${worker.cost.toFixed(2)}`);
47
+ lines.push(`- **${worker.milestoneId}** (${worker.title}) — ${worker.state} — $${worker.cost.toFixed(2)}`);
48
48
  }
49
49
  const state = getOrchestratorState();
50
50
  if (state) {
@@ -18,7 +18,7 @@ function lockPath(basePath) {
18
18
  return join(gsdRoot(basePath), LOCK_FILE);
19
19
  }
20
20
  /** Write or update the lock file with current auto-mode state. */
21
- export function writeLock(basePath, unitType, unitId, completedUnits, sessionFile) {
21
+ export function writeLock(basePath, unitType, unitId, sessionFile) {
22
22
  try {
23
23
  const data = {
24
24
  pid: process.pid,
@@ -26,7 +26,6 @@ export function writeLock(basePath, unitType, unitId, completedUnits, sessionFil
26
26
  unitType,
27
27
  unitId,
28
28
  unitStartedAt: new Date().toISOString(),
29
- completedUnits,
30
29
  sessionFile,
31
30
  };
32
31
  const lp = lockPath(basePath);
@@ -90,11 +89,10 @@ export function formatCrashInfo(lock) {
90
89
  `Previous auto-mode session was interrupted.`,
91
90
  ` Was executing: ${lock.unitType} (${lock.unitId})`,
92
91
  ` Started at: ${lock.unitStartedAt}`,
93
- ` Units completed before crash: ${lock.completedUnits}`,
94
92
  ` PID: ${lock.pid}`,
95
93
  ];
96
94
  // Add recovery guidance based on what was happening when it crashed
97
- if (lock.unitType === "starting" && lock.unitId === "bootstrap" && lock.completedUnits === 0) {
95
+ if (lock.unitType === "starting" && lock.unitId === "bootstrap") {
98
96
  lines.push(`No work was lost. Run /gsd auto to restart.`);
99
97
  }
100
98
  else if (lock.unitType.includes("research") || lock.unitType.includes("plan")) {
@@ -81,18 +81,11 @@ export class GSDDashboardOverlay {
81
81
  const currentUnit = dashData.currentUnit
82
82
  ? `${dashData.currentUnit.type}:${dashData.currentUnit.id}:${dashData.currentUnit.startedAt}`
83
83
  : "-";
84
- const lastCompleted = dashData.completedUnits.length > 0
85
- ? dashData.completedUnits[dashData.completedUnits.length - 1]
86
- : null;
87
- const completedKey = lastCompleted
88
- ? `${dashData.completedUnits.length}:${lastCompleted.type}:${lastCompleted.id}:${lastCompleted.finishedAt}`
89
- : "0";
90
84
  return [
91
85
  base,
92
86
  dashData.active ? "1" : "0",
93
87
  dashData.paused ? "1" : "0",
94
88
  currentUnit,
95
- completedKey,
96
89
  ].join("|");
97
90
  }
98
91
  async refreshDashboard(initial = false) {
@@ -393,43 +386,6 @@ export class GSDDashboardOverlay {
393
386
  else {
394
387
  lines.push(centered(th.fg("dim", "No active milestone.")));
395
388
  }
396
- if (this.dashData.completedUnits.length > 0) {
397
- lines.push(blank());
398
- lines.push(hr());
399
- lines.push(row(th.fg("text", th.bold("Completed"))));
400
- lines.push(blank());
401
- // Build ledger lookup for budget indicators (last entry wins for retries)
402
- const ledgerLookup = new Map();
403
- const currentLedger = getLedger();
404
- if (currentLedger) {
405
- for (const lu of currentLedger.units) {
406
- ledgerLookup.set(`${lu.type}:${lu.id}`, lu);
407
- }
408
- }
409
- const recent = [...this.dashData.completedUnits].reverse().slice(0, 10);
410
- for (const u of recent) {
411
- // Budget indicators from ledger — use warning glyph for pressured units
412
- const ledgerEntry = ledgerLookup.get(`${u.type}:${u.id}`);
413
- const hadPressure = ledgerEntry?.continueHereFired === true;
414
- const hadTruncation = (ledgerEntry?.truncationSections ?? 0) > 0;
415
- const unitGlyph = hadPressure
416
- ? th.fg(STATUS_COLOR.warning, STATUS_GLYPH.warning)
417
- : th.fg(STATUS_COLOR.done, STATUS_GLYPH.done);
418
- const left = ` ${unitGlyph} ${th.fg("muted", unitLabel(u.type))} ${th.fg("muted", u.id)}`;
419
- let budgetMarkers = "";
420
- if (hadTruncation) {
421
- budgetMarkers += th.fg("warning", ` ▼${ledgerEntry.truncationSections}`);
422
- }
423
- if (hadPressure) {
424
- budgetMarkers += th.fg("error", " → wrap-up");
425
- }
426
- const right = th.fg("dim", formatDuration(u.finishedAt - u.startedAt));
427
- lines.push(row(joinColumns(`${left}${budgetMarkers}`, right, contentWidth)));
428
- }
429
- if (this.dashData.completedUnits.length > 10) {
430
- lines.push(row(th.fg("dim", ` ...and ${this.dashData.completedUnits.length - 10} more`)));
431
- }
432
- }
433
389
  const ledger = getLedger();
434
390
  if (ledger && ledger.units.length > 0) {
435
391
  const totals = getProjectTotals(ledger.units);
@@ -3,7 +3,7 @@ import { basename, dirname, join, sep } from "node:path";
3
3
  import { readRepoMeta, externalProjectsRoot, cleanNumberedGsdVariants } from "./repo-identity.js";
4
4
  import { loadFile } from "./files.js";
5
5
  import { parseRoadmap as parseLegacyRoadmap } from "./parsers-legacy.js";
6
- import { isDbAvailable, getMilestoneSlices } from "./gsd-db.js";
6
+ import { isDbAvailable, _getAdapter, getMilestoneSlices } from "./gsd-db.js";
7
7
  import { resolveMilestoneFile, milestonesDir, gsdRoot, resolveGsdRootFile } from "./paths.js";
8
8
  import { deriveState, isMilestoneComplete } from "./state.js";
9
9
  import { saveFile } from "./files.js";
@@ -17,6 +17,8 @@ import { getAllWorktreeHealth } from "./worktree-health.js";
17
17
  import { readAllSessionStatuses, isSessionStale, removeSessionStatus } from "./session-status-io.js";
18
18
  import { recoverFailedMigration } from "./migrate-external.js";
19
19
  import { loadEffectiveGSDPreferences } from "./preferences.js";
20
+ import { readEvents } from "./workflow-events.js";
21
+ import { renderAllProjections } from "./workflow-projections.js";
20
22
  export async function checkGitHealth(basePath, issues, fixesApplied, shouldFix, isolationMode = "none") {
21
23
  // Degrade gracefully if not a git repo
22
24
  if (!nativeIsRepo(basePath)) {
@@ -1067,3 +1069,166 @@ export async function checkGlobalHealth(issues, fixesApplied, shouldFix) {
1067
1069
  // Non-fatal — global health check must not block per-project doctor
1068
1070
  }
1069
1071
  }
1072
+ // ── Engine Health Checks ────────────────────────────────────────────────────
1073
+ // DB constraint violation detection and projection drift checks.
1074
+ export async function checkEngineHealth(basePath, issues, fixesApplied) {
1075
+ // ── DB constraint violation detection (full doctor only, not pre-dispatch per D-10) ──
1076
+ try {
1077
+ if (isDbAvailable()) {
1078
+ const adapter = _getAdapter();
1079
+ // a. Orphaned tasks (task.slice_id points to non-existent slice)
1080
+ try {
1081
+ const orphanedTasks = adapter
1082
+ .prepare(`SELECT t.id, t.slice_id, t.milestone_id
1083
+ FROM tasks t
1084
+ LEFT JOIN slices s ON t.milestone_id = s.milestone_id AND t.slice_id = s.id
1085
+ WHERE s.id IS NULL`)
1086
+ .all();
1087
+ for (const row of orphanedTasks) {
1088
+ issues.push({
1089
+ severity: "error",
1090
+ code: "db_orphaned_task",
1091
+ scope: "task",
1092
+ unitId: `${row.milestone_id}/${row.slice_id}/${row.id}`,
1093
+ message: `Task ${row.id} references slice ${row.slice_id} in milestone ${row.milestone_id} but no such slice exists in the database`,
1094
+ fixable: false,
1095
+ });
1096
+ }
1097
+ }
1098
+ catch {
1099
+ // Non-fatal — orphaned task check failed
1100
+ }
1101
+ // b. Orphaned slices (slice.milestone_id points to non-existent milestone)
1102
+ try {
1103
+ const orphanedSlices = adapter
1104
+ .prepare(`SELECT s.id, s.milestone_id
1105
+ FROM slices s
1106
+ LEFT JOIN milestones m ON s.milestone_id = m.id
1107
+ WHERE m.id IS NULL`)
1108
+ .all();
1109
+ for (const row of orphanedSlices) {
1110
+ issues.push({
1111
+ severity: "error",
1112
+ code: "db_orphaned_slice",
1113
+ scope: "slice",
1114
+ unitId: `${row.milestone_id}/${row.id}`,
1115
+ message: `Slice ${row.id} references milestone ${row.milestone_id} but no such milestone exists in the database`,
1116
+ fixable: false,
1117
+ });
1118
+ }
1119
+ }
1120
+ catch {
1121
+ // Non-fatal — orphaned slice check failed
1122
+ }
1123
+ // c. Tasks marked complete without summaries
1124
+ try {
1125
+ const doneTasks = adapter
1126
+ .prepare(`SELECT id, slice_id, milestone_id FROM tasks
1127
+ WHERE status = 'done' AND (summary IS NULL OR summary = '')`)
1128
+ .all();
1129
+ for (const row of doneTasks) {
1130
+ issues.push({
1131
+ severity: "warning",
1132
+ code: "db_done_task_no_summary",
1133
+ scope: "task",
1134
+ unitId: `${row.milestone_id}/${row.slice_id}/${row.id}`,
1135
+ message: `Task ${row.id} is marked done but has no summary in the database`,
1136
+ fixable: false,
1137
+ });
1138
+ }
1139
+ }
1140
+ catch {
1141
+ // Non-fatal — done-task-no-summary check failed
1142
+ }
1143
+ // d. Duplicate entity IDs (safety check)
1144
+ try {
1145
+ const dupMilestones = adapter
1146
+ .prepare("SELECT id, COUNT(*) as cnt FROM milestones GROUP BY id HAVING cnt > 1")
1147
+ .all();
1148
+ for (const row of dupMilestones) {
1149
+ issues.push({
1150
+ severity: "error",
1151
+ code: "db_duplicate_id",
1152
+ scope: "milestone",
1153
+ unitId: row.id,
1154
+ message: `Duplicate milestone ID "${row.id}" appears ${row.cnt} times in the database`,
1155
+ fixable: false,
1156
+ });
1157
+ }
1158
+ const dupSlices = adapter
1159
+ .prepare("SELECT id, milestone_id, COUNT(*) as cnt FROM slices GROUP BY id, milestone_id HAVING cnt > 1")
1160
+ .all();
1161
+ for (const row of dupSlices) {
1162
+ issues.push({
1163
+ severity: "error",
1164
+ code: "db_duplicate_id",
1165
+ scope: "slice",
1166
+ unitId: `${row.milestone_id}/${row.id}`,
1167
+ message: `Duplicate slice ID "${row.id}" in milestone ${row.milestone_id} appears ${row.cnt} times`,
1168
+ fixable: false,
1169
+ });
1170
+ }
1171
+ const dupTasks = adapter
1172
+ .prepare("SELECT id, slice_id, milestone_id, COUNT(*) as cnt FROM tasks GROUP BY id, slice_id, milestone_id HAVING cnt > 1")
1173
+ .all();
1174
+ for (const row of dupTasks) {
1175
+ issues.push({
1176
+ severity: "error",
1177
+ code: "db_duplicate_id",
1178
+ scope: "task",
1179
+ unitId: `${row.milestone_id}/${row.slice_id}/${row.id}`,
1180
+ message: `Duplicate task ID "${row.id}" in slice ${row.slice_id} appears ${row.cnt} times`,
1181
+ fixable: false,
1182
+ });
1183
+ }
1184
+ }
1185
+ catch {
1186
+ // Non-fatal — duplicate ID check failed
1187
+ }
1188
+ }
1189
+ }
1190
+ catch {
1191
+ // Non-fatal — DB constraint checks failed entirely
1192
+ }
1193
+ // ── Projection drift detection ──────────────────────────────────────────
1194
+ // If the DB is available, check whether markdown projections are stale
1195
+ // relative to the event log and re-render them.
1196
+ try {
1197
+ if (isDbAvailable()) {
1198
+ const eventLogPath = join(basePath, ".gsd", "event-log.jsonl");
1199
+ const events = readEvents(eventLogPath);
1200
+ if (events.length > 0) {
1201
+ const lastEventTs = new Date(events[events.length - 1].ts).getTime();
1202
+ const state = await deriveState(basePath);
1203
+ for (const milestone of state.registry) {
1204
+ if (milestone.status === "complete")
1205
+ continue;
1206
+ const roadmapPath = resolveMilestoneFile(basePath, milestone.id, "ROADMAP");
1207
+ if (!roadmapPath || !existsSync(roadmapPath)) {
1208
+ try {
1209
+ await renderAllProjections(basePath, milestone.id);
1210
+ fixesApplied.push(`re-rendered missing projections for ${milestone.id}`);
1211
+ }
1212
+ catch {
1213
+ // Non-fatal — projection re-render failed
1214
+ }
1215
+ continue;
1216
+ }
1217
+ const projectionMtime = statSync(roadmapPath).mtimeMs;
1218
+ if (lastEventTs > projectionMtime) {
1219
+ try {
1220
+ await renderAllProjections(basePath, milestone.id);
1221
+ fixesApplied.push(`re-rendered stale projections for ${milestone.id}`);
1222
+ }
1223
+ catch {
1224
+ // Non-fatal — projection re-render failed
1225
+ }
1226
+ }
1227
+ }
1228
+ }
1229
+ }
1230
+ }
1231
+ catch {
1232
+ // Non-fatal — projection drift check must never block doctor
1233
+ }
1234
+ }
@@ -8,7 +8,7 @@ import { deriveState, isMilestoneComplete } from "./state.js";
8
8
  import { invalidateAllCaches } from "./cache.js";
9
9
  import { loadEffectiveGSDPreferences } from "./preferences.js";
10
10
  import { GLOBAL_STATE_CODES } from "./doctor-types.js";
11
- import { checkGitHealth, checkRuntimeHealth, checkGlobalHealth } from "./doctor-checks.js";
11
+ import { checkGitHealth, checkRuntimeHealth, checkGlobalHealth, checkEngineHealth } from "./doctor-checks.js";
12
12
  import { checkEnvironmentHealth } from "./doctor-environment.js";
13
13
  import { runProviderChecks } from "./doctor-providers.js";
14
14
  export { summarizeDoctorIssues, filterDoctorIssues, formatDoctorReport, formatDoctorIssuesForPrompt, formatDoctorReportJson } from "./doctor-format.js";
@@ -350,6 +350,8 @@ export async function runGSDDoctor(basePath, options) {
350
350
  includeTests: options?.includeTests,
351
351
  });
352
352
  const envMs = Date.now() - t0env;
353
+ // Engine health checks — DB constraints and projection drift
354
+ await checkEngineHealth(basePath, issues, fixesApplied);
353
355
  const milestonesPath = milestonesDir(basePath);
354
356
  if (!existsSync(milestonesPath)) {
355
357
  const report = { ok: issues.every(i => i.severity !== "error"), basePath, issues, fixesApplied, timing: { git: gitMs, runtime: runtimeMs, environment: envMs, gsdState: 0 } };
@@ -109,7 +109,7 @@ function openRawDb(path) {
109
109
  const Database = providerModule;
110
110
  return new Database(path);
111
111
  }
112
- const SCHEMA_VERSION = 10;
112
+ const SCHEMA_VERSION = 11;
113
113
  function initSchema(db, fileBacked) {
114
114
  if (fileBacked)
115
115
  db.exec("PRAGMA journal_mode=WAL");
@@ -549,6 +549,13 @@ function migrateSchema(db) {
549
549
  }
550
550
  if (currentVersion < 11) {
551
551
  ensureColumn(db, "tasks", "full_plan_md", `ALTER TABLE tasks ADD COLUMN full_plan_md TEXT NOT NULL DEFAULT ''`);
552
+ // Add unique constraint to replan_history for idempotency:
553
+ // one replan record per blocker task per slice per milestone.
554
+ db.exec(`
555
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_replan_history_unique
556
+ ON replan_history(milestone_id, slice_id, task_id)
557
+ WHERE slice_id IS NOT NULL AND task_id IS NOT NULL
558
+ `);
552
559
  db.prepare("INSERT INTO schema_version (version, applied_at) VALUES (:version, :applied_at)").run({
553
560
  ":version": 11,
554
561
  ":applied_at": new Date().toISOString(),
@@ -1315,7 +1322,9 @@ export function reconcileWorktreeDb(mainDbPath, worktreeDbPath) {
1315
1322
  export function insertReplanHistory(entry) {
1316
1323
  if (!currentDb)
1317
1324
  throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
1318
- currentDb.prepare(`INSERT INTO replan_history (milestone_id, slice_id, task_id, summary, previous_artifact_path, replacement_artifact_path, created_at)
1325
+ // INSERT OR REPLACE: idempotent on (milestone_id, slice_id, task_id) via schema v11 unique index.
1326
+ // Retrying the same replan silently updates summary instead of accumulating duplicate rows.
1327
+ currentDb.prepare(`INSERT OR REPLACE INTO replan_history (milestone_id, slice_id, task_id, summary, previous_artifact_path, replacement_artifact_path, created_at)
1319
1328
  VALUES (:milestone_id, :slice_id, :task_id, :summary, :previous_artifact_path, :replacement_artifact_path, :created_at)`).run({
1320
1329
  ":milestone_id": entry.milestoneId,
1321
1330
  ":slice_id": entry.sliceId ?? null,
@@ -755,8 +755,7 @@ export async function showSmartEntry(ctx, pi, basePath, options) {
755
755
  // when the user exits during init wizard or discuss phase before any
756
756
  // real auto-mode work begins.
757
757
  const isBootstrapCrash = crashLock.unitType === "starting"
758
- && crashLock.unitId === "bootstrap"
759
- && crashLock.completedUnits === 0;
758
+ && crashLock.unitId === "bootstrap";
760
759
  if (!isBootstrapCrash) {
761
760
  const resume = await showNextAction(ctx, {
762
761
  title: "GSD — Interrupted Session Detected",
@@ -17,7 +17,7 @@ import { getErrorMessage } from "./error-utils.js";
17
17
  * By-completion: merge in the order milestones finished.
18
18
  */
19
19
  export function determineMergeOrder(workers, order = "sequential") {
20
- const completed = workers.filter(w => w.state === "stopped" && w.completedUnits > 0);
20
+ const completed = workers.filter(w => w.state === "stopped");
21
21
  if (order === "by-completion") {
22
22
  return completed
23
23
  .sort((a, b) => a.startedAt - b.startedAt) // earliest first
@@ -47,7 +47,6 @@ export function persistState(basePath) {
47
47
  worktreePath: w.worktreePath,
48
48
  startedAt: w.startedAt,
49
49
  state: w.state,
50
- completedUnits: w.completedUnits,
51
50
  cost: w.cost,
52
51
  })),
53
52
  totalCost: state.totalCost,
@@ -158,7 +157,6 @@ function restoreRuntimeState(basePath) {
158
157
  worktreePath: diskStatus?.worktreePath ?? w.worktreePath,
159
158
  startedAt: w.startedAt,
160
159
  state: diskStatus?.state ?? w.state,
161
- completedUnits: diskStatus?.completedUnits ?? w.completedUnits,
162
160
  cost: diskStatus?.cost ?? w.cost,
163
161
  });
164
162
  }
@@ -189,7 +187,6 @@ function restoreRuntimeState(basePath) {
189
187
  worktreePath: status.worktreePath,
190
188
  startedAt: status.startedAt,
191
189
  state: status.state,
192
- completedUnits: status.completedUnits,
193
190
  cost: status.cost,
194
191
  });
195
192
  state.totalCost += status.cost;
@@ -296,7 +293,6 @@ export async function startParallel(basePath, milestoneIds, prefs) {
296
293
  worktreePath: w.worktreePath,
297
294
  startedAt: w.startedAt,
298
295
  state: "running",
299
- completedUnits: w.completedUnits,
300
296
  cost: w.cost,
301
297
  });
302
298
  adopted.push(w.milestoneId);
@@ -341,7 +337,6 @@ export async function startParallel(basePath, milestoneIds, prefs) {
341
337
  worktreePath: wtPath,
342
338
  startedAt: now,
343
339
  state: "running",
344
- completedUnits: 0,
345
340
  cost: 0,
346
341
  };
347
342
  state.workers.set(mid, worker);
@@ -486,7 +481,7 @@ export function spawnWorker(basePath, milestoneId) {
486
481
  pid: worker.pid,
487
482
  state: "running",
488
483
  currentUnit: null,
489
- completedUnits: worker.completedUnits,
484
+ completedUnits: 0,
490
485
  cost: worker.cost,
491
486
  lastHeartbeat: Date.now(),
492
487
  startedAt: worker.startedAt,
@@ -527,7 +522,7 @@ export function spawnWorker(basePath, milestoneId) {
527
522
  pid: w.pid,
528
523
  state: w.state,
529
524
  currentUnit: null,
530
- completedUnits: w.completedUnits,
525
+ completedUnits: 0,
531
526
  cost: w.cost,
532
527
  lastHeartbeat: Date.now(),
533
528
  startedAt: w.startedAt,
@@ -602,13 +597,6 @@ function processWorkerLine(basePath, milestoneId, line) {
602
597
  }
603
598
  }
604
599
  }
605
- // Track completed units (each message_end from assistant = progress)
606
- if (msg.role === "assistant") {
607
- const worker = state.workers.get(milestoneId);
608
- if (worker) {
609
- worker.completedUnits++;
610
- }
611
- }
612
600
  // Update session status file so dashboard sees live cost
613
601
  const worker = state.workers.get(milestoneId);
614
602
  if (worker) {
@@ -617,7 +605,7 @@ function processWorkerLine(basePath, milestoneId, line) {
617
605
  pid: worker.pid,
618
606
  state: worker.state,
619
607
  currentUnit: null,
620
- completedUnits: worker.completedUnits,
608
+ completedUnits: 0,
621
609
  cost: worker.cost,
622
610
  lastHeartbeat: Date.now(),
623
611
  startedAt: worker.startedAt,
@@ -635,7 +623,7 @@ function processWorkerLine(basePath, milestoneId, line) {
635
623
  pid: worker.pid,
636
624
  state: worker.state,
637
625
  currentUnit: null,
638
- completedUnits: worker.completedUnits,
626
+ completedUnits: 0,
639
627
  cost: worker.cost,
640
628
  lastHeartbeat: Date.now(),
641
629
  startedAt: worker.startedAt,
@@ -776,13 +764,12 @@ export function refreshWorkerStatuses(basePath, options = {}) {
776
764
  if (!isPidAlive(worker.pid)) {
777
765
  worker.cleanup?.();
778
766
  worker.cleanup = undefined;
779
- worker.state = worker.completedUnits > 0 ? "stopped" : "error";
767
+ worker.state = "error";
780
768
  worker.process = null;
781
769
  }
782
770
  continue;
783
771
  }
784
772
  worker.state = diskStatus.state;
785
- worker.completedUnits = diskStatus.completedUnits;
786
773
  worker.cost = diskStatus.cost;
787
774
  worker.pid = diskStatus.pid;
788
775
  }
@@ -36,7 +36,7 @@ Then:
36
36
  **Success path** (all verifications passed — continue with steps 7–11):
37
37
 
38
38
  7. **Persist completion through `gsd_complete_milestone`.** Call it with: `milestoneId`, `title`, `oneLiner`, `narrative`, `successCriteriaResults`, `definitionOfDoneResults`, `requirementOutcomes`, `keyDecisions`, `keyFiles`, `lessonsLearned`, `followUps`, `deviations`, `verificationPassed: true`. The tool updates the milestone status in the DB, renders `{{milestoneSummaryPath}}`, and validates all slices are complete before proceeding.
39
- 8. Update `.gsd/REQUIREMENTS.md` if any requirement status transitions were validated in step 6.
39
+ 8. For each requirement whose status changed in step 6, call `gsd_requirement_update` with the requirement ID and updated `status` and `validation` fields the tool regenerates `.gsd/REQUIREMENTS.md` automatically.
40
40
  9. Update `.gsd/PROJECT.md` to reflect milestone completion and current project state.
41
41
  10. Review all slice summaries for cross-cutting lessons, patterns, or gotchas that emerged during this milestone. Append any non-obvious, reusable insights to `.gsd/KNOWLEDGE.md`.
42
42
  11. Do not commit manually — the system auto-commits your changes after this unit completes.
@@ -23,28 +23,15 @@ Then:
23
23
  2. {{skillActivation}}
24
24
  3. Run all slice-level verification checks defined in the slice plan. All must pass before marking the slice done. If any fail, fix them first.
25
25
  4. If the slice plan includes observability/diagnostic surfaces, confirm they work. Skip this for simple slices that don't have observability sections.
26
- 5. If `.gsd/REQUIREMENTS.md` exists, update it based on what this slice actually proved. Move requirements between Active, Validated, Deferred, Blocked, or Out of Scope only when the evidence from execution supports that change.
27
- 6. Call the `gsd_slice_complete` tool (alias: `gsd_complete_slice`) to record the slice as complete. The tool validates all tasks are complete, updates the slice status in the DB, renders the summary to `{{sliceSummaryPath}}`, UAT to `{{sliceUatPath}}`, and re-renders `{{roadmapPath}}` — all atomically. Read the summary and UAT templates at `~/.gsd/agent/extensions/gsd/templates/` to understand the expected structure, then pass the following parameters:
28
-
29
- **Identity:** `sliceId`, `milestoneId`, `sliceTitle`
30
-
31
- **Narrative:** `oneLiner` (one-line summary of what the slice accomplished), `narrative` (detailed account of what happened across all tasks), `verification` (what was verified and how), `deviations` (deviations from plan, or "None."), `knownLimitations` (gaps or limitations, or "None."), `followUps` (follow-up work discovered, or "None.")
32
-
33
- **Files:** `keyFiles` (array of key file paths), `filesModified` (array of `{path, description}` objects for all files changed)
34
-
35
- **Requirements:** `requirementsAdvanced` (array of `{id, how}`), `requirementsValidated` (array of `{id, proof}`), `requirementsInvalidated` (array of `{id, what}`), `requirementsSurfaced` (array of new requirement strings)
36
-
37
- **Patterns & decisions:** `keyDecisions` (array of decision strings), `patternsEstablished` (array), `observabilitySurfaces` (array)
38
-
39
- **Dependencies:** `provides` (what this slice provides downstream), `affects` (downstream slice IDs affected), `requires` (array of `{slice, provides}` for upstream dependencies consumed), `drillDownPaths` (paths to task summaries)
40
-
41
- **UAT content:** `uatContent` — the UAT markdown body. This must be a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built. The tool writes it to `{{sliceUatPath}}`.
42
-
43
- 7. Review task summaries for `key_decisions`. Append any significant decisions to `.gsd/DECISIONS.md` if missing.
44
- 8. Review task summaries for patterns, gotchas, or non-obvious lessons learned. If any would save future agents from repeating investigation or hitting the same issues, append them to `.gsd/KNOWLEDGE.md`. Only add entries that are genuinely useful — don't pad with obvious observations.
45
- 9. Do not run git commands — the system commits your changes and handles any merge after this unit succeeds.
46
- 10. Update `.gsd/PROJECT.md` if it exists — refresh current state if needed.
47
-
48
- **You MUST call `gsd_slice_complete` before finishing.** The tool handles writing `{{sliceSummaryPath}}`, `{{sliceUatPath}}`, and updating `{{roadmapPath}}` atomically. You must still review decisions and knowledge manually (steps 7-8).
26
+ 5. If this slice produced evidence that a requirement changed status (Active Validated, Active Deferred, etc.), call `gsd_save_decision` with scope="requirement", decision="{requirement-id}", choice="{new-status}", rationale="{evidence}". Do NOT write `.gsd/REQUIREMENTS.md` directly — the engine renders it from the database.
27
+ 6. Write `{{sliceSummaryPath}}` (compress all task summaries).
28
+ 7. Write `{{sliceUatPath}}` — a concrete UAT script with real test cases derived from the slice plan and task summaries. Include preconditions, numbered steps with expected outcomes, and edge cases. This must NOT be a placeholder or generic template — tailor every test case to what this slice actually built.
29
+ 8. Review task summaries for `key_decisions`. Append any significant decisions to `.gsd/DECISIONS.md` if missing.
30
+ 9. Review task summaries for patterns, gotchas, or non-obvious lessons learned. If any would save future agents from repeating investigation or hitting the same issues, append them to `.gsd/KNOWLEDGE.md`. Only add entries that are genuinely useful — don't pad with obvious observations.
31
+ 10. Call `gsd_complete_slice` with milestone_id, slice_id, the slice summary, and the UAT result. Do NOT manually mark the roadmap checkbox the tool writes to the DB and renders the ROADMAP.md projection automatically.
32
+ 11. Do not run git commands — the system commits your changes and handles any merge after this unit succeeds.
33
+ 12. Update `.gsd/PROJECT.md` if it exists refresh current state if needed.
34
+
35
+ **You MUST do ALL THREE before finishing: (1) write `{{sliceSummaryPath}}`, (2) write `{{sliceUatPath}}`, (3) call `gsd_complete_slice`. The unit will not be marked complete if any of these are missing.**
49
36
 
50
37
  When done, say: "Slice {{sliceId}} complete."
@@ -203,7 +203,7 @@ When writing context.md, preserve the user's exact terminology, emphasis, and sp
203
203
 
204
204
  4. Write `{{contextPath}}` — use the **Context** output template below. Preserve key risks, unknowns, existing codebase constraints, integration points, and relevant requirements surfaced during discussion.
205
205
  5. Call `gsd_plan_milestone` to create the roadmap. Decompose into demoable vertical slices with risk, depends, demo sentences, proof strategy, verification classes, milestone definition of done, requirement coverage, and a boundary map. If the milestone crosses multiple runtime boundaries, include an explicit final integration slice that proves the assembled system works end-to-end in a real environment. Use the **Roadmap** output template below to structure the tool call parameters.
206
- 6. Seed `.gsd/DECISIONS.md` use the **Decisions** output template below. Append rows for any architectural or pattern decisions made during discussion.
206
+ 6. For each architectural or pattern decision made during discussion, call `gsd_decision_save` the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
207
207
  7. {{commitInstruction}}
208
208
 
209
209
  After writing the files, say exactly: "Milestone {{milestoneId}} ready." — nothing else. Auto-mode will start automatically.
@@ -217,7 +217,7 @@ Once the user confirms the milestone split:
217
217
  1. For each milestone, call `gsd_milestone_generate_id` to get its ID — never invent milestone IDs manually. Then `mkdir -p .gsd/milestones/<ID>/slices`.
218
218
  2. Write `.gsd/PROJECT.md` — use the **Project** output template below.
219
219
  3. Write `.gsd/REQUIREMENTS.md` — use the **Requirements** output template below. Capture Active, Deferred, Out of Scope, and any already Validated requirements. Later milestones may have provisional ownership where slice plans do not exist yet.
220
- 4. Seed `.gsd/DECISIONS.md` — use the **Decisions** output template below.
220
+ 4. For any architectural or pattern decisions made during discussion, call `gsd_decision_save` — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically.
221
221
 
222
222
  #### Phase 2: Primary milestone
223
223
 
@@ -63,23 +63,13 @@ Then:
63
63
  11. **Blocker discovery:** If execution reveals that the remaining slice plan is fundamentally invalid — not just a bug or minor deviation, but a plan-invalidating finding like a wrong API, missing capability, or architectural mismatch — set `blocker_discovered: true` in the task summary frontmatter and describe the blocker clearly in the summary narrative. Do NOT set `blocker_discovered: true` for ordinary debugging, minor deviations, or issues that can be fixed within the current task or the remaining plan. This flag triggers an automatic replan of the slice.
64
64
  12. If you made an architectural, pattern, library, or observability decision during this task that downstream work should know about, append it to `.gsd/DECISIONS.md` (read the template at `~/.gsd/agent/extensions/gsd/templates/decisions.md` if the file doesn't exist yet). Not every task produces decisions — only append when a meaningful choice was made.
65
65
  13. If you discover a non-obvious rule, recurring gotcha, or useful pattern during execution, append it to `.gsd/KNOWLEDGE.md`. Only add entries that would save future agents from repeating your investigation. Don't add obvious things.
66
- 14. Call the `gsd_task_complete` tool (alias: `gsd_complete_task`) to record the task completion. This single tool call atomically updates the task status in the DB, renders the summary file to `{{taskSummaryPath}}`, and re-renders the plan file at `{{planPath}}`. Read the summary template at `~/.gsd/agent/extensions/gsd/templates/task-summary.md` to understand the expected structure — but pass the content as tool parameters, not as a file write. The tool parameters are:
67
- - `taskId`: "{{taskId}}"
68
- - `sliceId`: "{{sliceId}}"
69
- - `milestoneId`: "{{milestoneId}}"
70
- - `oneLiner`: One-line summary of what was accomplished (becomes the commit message)
71
- - `narrative`: Detailed narrative of what happened during the task
72
- - `verification`: What was verified and how — commands run, tests passed, behavior confirmed
73
- - `deviations`: Deviations from the task plan, or "None."
74
- - `knownIssues`: Known issues discovered but not fixed, or "None."
75
- - `keyFiles`: Array of key files created or modified
76
- - `keyDecisions`: Array of key decisions made during this task
77
- - `blockerDiscovered`: Whether a plan-invalidating blocker was discovered (boolean)
78
- - `verificationEvidence`: Array of `{ command, exitCode, verdict, durationMs }` objects from the verification gate
79
- 15. Do not run git commands — the system reads your task summary after completion and creates a meaningful commit from it (type inferred from title, message from your one-liner, key files from frontmatter). Write a clear, specific one-liner in the summary — it becomes the commit message.
66
+ 14. Read the template at `~/.gsd/agent/extensions/gsd/templates/task-summary.md`
67
+ 15. Write `{{taskSummaryPath}}`
68
+ 16. Call `gsd_complete_task` with milestone_id, slice_id, task_id, and a summary of what was accomplished. This is your final required step — do NOT manually edit PLAN.md checkboxes. The tool marks the task complete, updates the DB, and renders PLAN.md automatically.
69
+ 17. Do not run git commands — the system reads your task summary after completion and creates a meaningful commit from it (type inferred from title, message from your one-liner, key files from frontmatter). Write a clear, specific one-liner in the summary — it becomes the commit message.
80
70
 
81
71
  All work stays in your working directory: `{{workingDirectory}}`.
82
72
 
83
- **You MUST call `gsd_task_complete` before finishing.** The tool handles writing `{{taskSummaryPath}}` and updating the plan file at `{{planPath}}` — do not write the summary file or modify the plan file manually.
73
+ **You MUST call `gsd_complete_task` AND write `{{taskSummaryPath}}` before finishing.**
84
74
 
85
75
  When done, say: "Task {{taskId}} complete."
@@ -105,6 +105,6 @@ Once the user confirms depth:
105
105
 
106
106
  1. Use the **Context** output template below
107
107
  2. `mkdir -p` the milestone directory if needed
108
- 3. Write `{{milestoneId}}-CONTEXT.md` — preserve the user's exact terminology, emphasis, and framing. Do not paraphrase nuance into generic summaries. The context file is downstream agents' only window into this conversation.
108
+ 3. Call `gsd_summary_save` with `milestone_id: {{milestoneId}}`, `artifact_type: "CONTEXT"`, and the full context markdown as `content`the tool writes the file to disk and persists to DB. Preserve the user's exact terminology, emphasis, and framing in the content. Do not paraphrase nuance into generic summaries. The context file is downstream agents' only window into this conversation.
109
109
  4. {{commitInstruction}}
110
110
  5. Say exactly: `"{{milestoneId}} context written."` — nothing else.
@@ -48,7 +48,7 @@ Once the user is ready to wrap up:
48
48
 
49
49
  1. Use the **Slice Context** output template below
50
50
  2. `mkdir -p {{sliceDirPath}}`
51
- 3. Write `{{contextPath}}` — use the template structure, filling in:
51
+ 3. Call `gsd_summary_save` with `milestone_id: {{milestoneId}}`, `slice_id: {{sliceId}}`, `artifact_type: "CONTEXT"`, and the context as `content` the tool writes the file to disk and persists to DB. Use the template structure, filling in:
52
52
  - **Goal** — one sentence: what this slice delivers
53
53
  - **Why this Slice** — why now, what it unblocks
54
54
  - **Scope / In Scope** — what was confirmed in scope during the interview
@@ -1,3 +1,3 @@
1
- Plan slice {{sliceId}} ("{{sliceTitle}}") of milestone {{milestoneId}}. Read `.gsd/DECISIONS.md` if it exists — respect existing decisions. Read `.gsd/REQUIREMENTS.md` if it exists — identify which Active requirements the roadmap says this slice owns or supports, and ensure the plan delivers them. Read the roadmap boundary map, any existing context/research files, and dependency summaries. Use the **Slice Plan** and **Task Plan** output templates below. Decompose into tasks with must-haves. Fill the `Proof Level` and `Integration Closure` sections truthfully so the plan says what class of proof this slice really delivers and what end-to-end wiring still remains. Write `{{sliceId}}-PLAN.md` and individual `T##-PLAN.md` files in the `tasks/` subdirectory. If planning produces structural decisions, append them to `.gsd/DECISIONS.md`. {{skillActivation}} Before committing, self-audit the plan: every must-have maps to at least one task, every task has complete sections (steps, must-haves, verification, observability impact, inputs, and expected output), task ordering is consistent with no circular references, every pair of artifacts that must connect has an explicit wiring step, task scope targets 2–5 steps and 3–8 files (6–8 steps or 8–10 files — consider splitting; 10+ steps or 12+ files — must split), the plan honors locked decisions from context/research/decisions artifacts, the proof-level wording does not overclaim live integration if only fixture/contract proof is planned, every Active requirement this slice owns has at least one task with verification that proves it is met, and every task produces real user-facing progress — if the slice has a UI surface at least one task builds the real UI, if it has an API at least one task connects it to a real data source, and showing the completed result to a non-technical stakeholder would demonstrate real product progress rather than developer artifacts.
1
+ Plan slice {{sliceId}} ("{{sliceTitle}}") of milestone {{milestoneId}}. Read `.gsd/DECISIONS.md` if it exists — respect existing decisions. Read `.gsd/REQUIREMENTS.md` if it exists — identify which Active requirements the roadmap says this slice owns or supports, and ensure the plan delivers them. Read the roadmap boundary map, any existing context/research files, and dependency summaries. Use the **Slice Plan** and **Task Plan** output templates below. Decompose into tasks with must-haves. Fill the `Proof Level` and `Integration Closure` sections truthfully so the plan says what class of proof this slice really delivers and what end-to-end wiring still remains. Call `gsd_plan_slice` to persist the slice plan — the tool writes `{{sliceId}}-PLAN.md` and individual `T##-PLAN.md` files to disk and persists to DB. Do **not** write plan files manually — use the DB-backed tool so state stays consistent. If planning produces structural decisions, call `gsd_decision_save` for each — the tool auto-assigns IDs and regenerates `.gsd/DECISIONS.md` automatically. {{skillActivation}} Before finishing, self-audit the plan: every must-have maps to at least one task, every task has complete sections (steps, must-haves, verification, observability impact, inputs, and expected output), task ordering is consistent with no circular references, every pair of artifacts that must connect has an explicit wiring step, task scope targets 2–5 steps and 3–8 files (6–8 steps or 8–10 files — consider splitting; 10+ steps or 12+ files — must split), the plan honors locked decisions from context/research/decisions artifacts, the proof-level wording does not overclaim live integration if only fixture/contract proof is planned, every Active requirement this slice owns has at least one task with verification that proves it is met, and every task produces real user-facing progress — if the slice has a UI surface at least one task builds the real UI, if it has an API at least one task connects it to a real data source, and showing the completed result to a non-technical stakeholder would demonstrate real product progress rather than developer artifacts.
2
2
 
3
3
  {{inlinedTemplates}}