gsd-pi 2.74.0-dev.2b524c3 → 2.74.0-dev.b741afb

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 (159) hide show
  1. package/dist/cli.js +85 -0
  2. package/dist/headless-query.js +4 -1
  3. package/dist/help-text.js +23 -0
  4. package/dist/resources/extensions/gsd/auto/detect-stuck.js +11 -4
  5. package/dist/resources/extensions/gsd/auto/phases.js +45 -1
  6. package/dist/resources/extensions/gsd/auto-post-unit.js +52 -56
  7. package/dist/resources/extensions/gsd/auto-prompts.js +12 -0
  8. package/dist/resources/extensions/gsd/auto.js +8 -2
  9. package/dist/resources/extensions/gsd/bootstrap/register-extension.js +21 -8
  10. package/dist/resources/extensions/gsd/commands/catalog.js +26 -1
  11. package/dist/resources/extensions/gsd/commands/handlers/ops.js +20 -0
  12. package/dist/resources/extensions/gsd/commands/handlers/workflow.js +68 -9
  13. package/dist/resources/extensions/gsd/commands-add-tests.js +111 -0
  14. package/dist/resources/extensions/gsd/commands-backlog.js +140 -0
  15. package/dist/resources/extensions/gsd/commands-do.js +79 -0
  16. package/dist/resources/extensions/gsd/commands-maintenance.js +6 -6
  17. package/dist/resources/extensions/gsd/commands-pr-branch.js +180 -0
  18. package/dist/resources/extensions/gsd/commands-session-report.js +82 -0
  19. package/dist/resources/extensions/gsd/commands-ship.js +187 -0
  20. package/dist/resources/extensions/gsd/db-writer.js +3 -5
  21. package/dist/resources/extensions/gsd/graph-context.js +66 -0
  22. package/dist/resources/extensions/gsd/gsd-db.js +321 -0
  23. package/dist/resources/extensions/gsd/index.js +15 -2
  24. package/dist/resources/extensions/gsd/md-importer.js +3 -4
  25. package/dist/resources/extensions/gsd/memory-store.js +19 -51
  26. package/dist/resources/extensions/gsd/milestone-validation-gates.js +13 -12
  27. package/dist/resources/extensions/gsd/native-git-bridge.js +7 -4
  28. package/dist/resources/extensions/gsd/prompts/add-tests.md +35 -0
  29. package/dist/resources/extensions/gsd/state.js +5 -1
  30. package/dist/resources/extensions/gsd/tools/complete-slice.js +15 -0
  31. package/dist/resources/extensions/gsd/tools/workflow-tool-executors.js +3 -14
  32. package/dist/resources/extensions/gsd/triage-resolution.js +2 -5
  33. package/dist/resources/extensions/gsd/workflow-manifest.js +8 -69
  34. package/dist/resources/extensions/gsd/workflow-migration.js +21 -22
  35. package/dist/resources/extensions/gsd/workflow-projections.js +4 -1
  36. package/dist/resources/extensions/gsd/workflow-reconcile.js +14 -11
  37. package/dist/tsconfig.extensions.tsbuildinfo +1 -0
  38. package/dist/web/standalone/.next/BUILD_ID +1 -1
  39. package/dist/web/standalone/.next/app-path-routes-manifest.json +7 -7
  40. package/dist/web/standalone/.next/build-manifest.json +2 -2
  41. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  42. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  43. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  44. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  45. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  46. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  47. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  48. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  49. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  50. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  51. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  52. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  53. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  54. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  55. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  56. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  57. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  58. package/dist/web/standalone/.next/server/app/index.html +1 -1
  59. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  60. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  61. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  62. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  63. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  64. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  65. package/dist/web/standalone/.next/server/app-paths-manifest.json +7 -7
  66. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  67. package/dist/web/standalone/.next/server/middleware-manifest.json +5 -5
  68. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  69. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  70. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  71. package/package.json +3 -2
  72. package/packages/daemon/package.json +2 -2
  73. package/packages/mcp-server/dist/index.d.ts +3 -0
  74. package/packages/mcp-server/dist/index.d.ts.map +1 -1
  75. package/packages/mcp-server/dist/index.js +3 -0
  76. package/packages/mcp-server/dist/index.js.map +1 -1
  77. package/packages/mcp-server/dist/readers/graph.d.ts +87 -0
  78. package/packages/mcp-server/dist/readers/graph.d.ts.map +1 -0
  79. package/packages/mcp-server/dist/readers/graph.js +548 -0
  80. package/packages/mcp-server/dist/readers/graph.js.map +1 -0
  81. package/packages/mcp-server/dist/readers/index.d.ts +2 -0
  82. package/packages/mcp-server/dist/readers/index.d.ts.map +1 -1
  83. package/packages/mcp-server/dist/readers/index.js +1 -0
  84. package/packages/mcp-server/dist/readers/index.js.map +1 -1
  85. package/packages/mcp-server/dist/server.d.ts.map +1 -1
  86. package/packages/mcp-server/dist/server.js +65 -0
  87. package/packages/mcp-server/dist/server.js.map +1 -1
  88. package/packages/mcp-server/package.json +2 -2
  89. package/packages/mcp-server/src/index.ts +15 -0
  90. package/packages/mcp-server/src/readers/graph.test.ts +426 -0
  91. package/packages/mcp-server/src/readers/graph.ts +708 -0
  92. package/packages/mcp-server/src/readers/index.ts +12 -0
  93. package/packages/mcp-server/src/server.ts +83 -0
  94. package/packages/mcp-server/tsconfig.json +1 -0
  95. package/packages/mcp-server/tsconfig.tsbuildinfo +1 -0
  96. package/packages/native/package.json +2 -2
  97. package/packages/native/tsconfig.tsbuildinfo +1 -0
  98. package/packages/pi-agent-core/package.json +1 -1
  99. package/packages/pi-agent-core/tsconfig.json +1 -0
  100. package/packages/pi-agent-core/tsconfig.tsbuildinfo +1 -0
  101. package/packages/pi-ai/package.json +1 -1
  102. package/packages/pi-ai/tsconfig.json +1 -0
  103. package/packages/pi-ai/tsconfig.tsbuildinfo +1 -0
  104. package/packages/pi-coding-agent/tsconfig.json +1 -0
  105. package/packages/pi-coding-agent/tsconfig.tsbuildinfo +1 -0
  106. package/packages/pi-tui/package.json +1 -1
  107. package/packages/pi-tui/tsconfig.json +1 -0
  108. package/packages/pi-tui/tsconfig.tsbuildinfo +1 -0
  109. package/packages/rpc-client/package.json +1 -1
  110. package/packages/rpc-client/tsconfig.json +1 -0
  111. package/packages/rpc-client/tsconfig.tsbuildinfo +1 -0
  112. package/src/resources/extensions/gsd/auto/detect-stuck.ts +12 -4
  113. package/src/resources/extensions/gsd/auto/loop-deps.ts +6 -0
  114. package/src/resources/extensions/gsd/auto/phases.ts +68 -1
  115. package/src/resources/extensions/gsd/auto-post-unit.ts +60 -57
  116. package/src/resources/extensions/gsd/auto-prompts.ts +13 -0
  117. package/src/resources/extensions/gsd/auto.ts +7 -0
  118. package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -8
  119. package/src/resources/extensions/gsd/commands/catalog.ts +26 -1
  120. package/src/resources/extensions/gsd/commands/handlers/ops.ts +20 -0
  121. package/src/resources/extensions/gsd/commands/handlers/workflow.ts +74 -9
  122. package/src/resources/extensions/gsd/commands-add-tests.ts +137 -0
  123. package/src/resources/extensions/gsd/commands-backlog.ts +182 -0
  124. package/src/resources/extensions/gsd/commands-do.ts +109 -0
  125. package/src/resources/extensions/gsd/commands-maintenance.ts +6 -6
  126. package/src/resources/extensions/gsd/commands-pr-branch.ts +234 -0
  127. package/src/resources/extensions/gsd/commands-session-report.ts +101 -0
  128. package/src/resources/extensions/gsd/commands-ship.ts +219 -0
  129. package/src/resources/extensions/gsd/db-writer.ts +3 -5
  130. package/src/resources/extensions/gsd/graph-context.ts +85 -0
  131. package/src/resources/extensions/gsd/gsd-db.ts +467 -0
  132. package/src/resources/extensions/gsd/index.ts +18 -2
  133. package/src/resources/extensions/gsd/md-importer.ts +3 -5
  134. package/src/resources/extensions/gsd/memory-store.ts +31 -62
  135. package/src/resources/extensions/gsd/milestone-validation-gates.ts +13 -14
  136. package/src/resources/extensions/gsd/native-git-bridge.ts +11 -12
  137. package/src/resources/extensions/gsd/prompts/add-tests.md +35 -0
  138. package/src/resources/extensions/gsd/state.ts +9 -2
  139. package/src/resources/extensions/gsd/tests/commands-backlog.test.ts +158 -0
  140. package/src/resources/extensions/gsd/tests/commands-do.test.ts +127 -0
  141. package/src/resources/extensions/gsd/tests/commands-pr-branch.test.ts +68 -0
  142. package/src/resources/extensions/gsd/tests/commands-session-report.test.ts +82 -0
  143. package/src/resources/extensions/gsd/tests/commands-ship.test.ts +71 -0
  144. package/src/resources/extensions/gsd/tests/commands-workflow-custom.test.ts +14 -0
  145. package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +154 -0
  146. package/src/resources/extensions/gsd/tests/graph-context.test.ts +337 -0
  147. package/src/resources/extensions/gsd/tests/journal-integration.test.ts +68 -1
  148. package/src/resources/extensions/gsd/tests/native-git-bridge-exec-fallback.test.ts +140 -0
  149. package/src/resources/extensions/gsd/tests/single-writer-invariant.test.ts +180 -0
  150. package/src/resources/extensions/gsd/tests/workflow-logger-wiring.test.ts +223 -0
  151. package/src/resources/extensions/gsd/tools/complete-slice.ts +19 -0
  152. package/src/resources/extensions/gsd/tools/workflow-tool-executors.ts +3 -11
  153. package/src/resources/extensions/gsd/triage-resolution.ts +2 -7
  154. package/src/resources/extensions/gsd/workflow-manifest.ts +9 -104
  155. package/src/resources/extensions/gsd/workflow-migration.ts +21 -29
  156. package/src/resources/extensions/gsd/workflow-projections.ts +8 -1
  157. package/src/resources/extensions/gsd/workflow-reconcile.ts +15 -15
  158. /package/dist/web/standalone/.next/static/{YzIEI9sxJy4t5xgClF08g → XnHY5eXUsTCFmNodWHetD}/_buildManifest.js +0 -0
  159. /package/dist/web/standalone/.next/static/{YzIEI9sxJy4t5xgClF08g → XnHY5eXUsTCFmNodWHetD}/_ssgManifest.js +0 -0
@@ -354,6 +354,21 @@ export async function handleCompleteSlice(params, basePath) {
354
354
  catch (eventErr) {
355
355
  logError("tool", `complete-slice event log FAILED — completion invisible to reconciliation`, { error: eventErr.message });
356
356
  }
357
+ // Fire-and-forget graph rebuild — must NOT await, must NOT crash slice completion.
358
+ // Dynamic import of the package name (not a relative path) so it resolves
359
+ // correctly via package.json#exports in both development and production.
360
+ // eslint-disable-next-line @typescript-eslint/no-floating-promises
361
+ (async () => {
362
+ try {
363
+ const graphMod = await import("@gsd-build/mcp-server");
364
+ const g = await graphMod.buildGraph(basePath);
365
+ await graphMod.writeGraph(graphMod.resolveGsdRoot(basePath), g);
366
+ }
367
+ catch (graphErr) {
368
+ // Graph rebuild is best-effort — log at warning level but never propagate
369
+ logWarning("tool", `complete-slice graph rebuild failed (non-fatal): ${graphErr.message ?? String(graphErr)}`);
370
+ }
371
+ })();
357
372
  return {
358
373
  sliceId: params.sliceId,
359
374
  milestoneId: params.milestoneId,
@@ -1,7 +1,7 @@
1
1
  import { ensureDbOpen } from "../bootstrap/dynamic-tools.js";
2
2
  import { sanitizeCompleteMilestoneParams } from "../bootstrap/sanitize-complete-milestone.js";
3
3
  import { loadWriteGateSnapshot, shouldBlockContextArtifactSaveInSnapshot } from "../bootstrap/write-gate.js";
4
- import { getMilestone, getSliceStatusSummary, getSliceTaskCounts, _getAdapter, saveGateResult, } from "../gsd-db.js";
4
+ import { getMilestone, getSliceStatusSummary, getSliceTaskCounts, readTransaction, saveGateResult, } from "../gsd-db.js";
5
5
  import { GATE_REGISTRY } from "../gate-registry.js";
6
6
  import { saveArtifactToDb } from "../db-writer.js";
7
7
  import { handleCompleteMilestone } from "./complete-milestone.js";
@@ -493,12 +493,9 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
493
493
  isError: true,
494
494
  };
495
495
  }
496
- const adapter = _getAdapter();
497
- adapter.exec("BEGIN");
498
- try {
496
+ return readTransaction(() => {
499
497
  const milestone = getMilestone(params.milestoneId);
500
498
  if (!milestone) {
501
- adapter.exec("COMMIT");
502
499
  return {
503
500
  content: [{ type: "text", text: `Milestone ${params.milestoneId} not found in database.` }],
504
501
  details: { operation: "milestone_status", milestoneId: params.milestoneId, found: false },
@@ -510,7 +507,6 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
510
507
  status: s.status,
511
508
  taskCounts: getSliceTaskCounts(params.milestoneId, s.id),
512
509
  }));
513
- adapter.exec("COMMIT");
514
510
  const result = {
515
511
  milestoneId: milestone.id,
516
512
  title: milestone.title,
@@ -524,14 +520,7 @@ export async function executeMilestoneStatus(params, basePath = process.cwd()) {
524
520
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
525
521
  details: { operation: "milestone_status", milestoneId: milestone.id, sliceCount: slices.length },
526
522
  };
527
- }
528
- catch (txErr) {
529
- try {
530
- adapter.exec("ROLLBACK");
531
- }
532
- catch { /* swallow */ }
533
- throw txErr;
534
- }
523
+ });
535
524
  }
536
525
  catch (err) {
537
526
  const msg = err instanceof Error ? err.message : String(err);
@@ -83,12 +83,9 @@ export function executeReplan(basePath, mid, sid, capture) {
83
83
  // Also write replan_triggered_at column for DB-backed detection
84
84
  try {
85
85
  const req = createRequire(import.meta.url);
86
- const { isDbAvailable, _getAdapter } = req("./gsd-db.js");
86
+ const { isDbAvailable, setSliceReplanTriggeredAt } = req("./gsd-db.js");
87
87
  if (isDbAvailable()) {
88
- const adapter = _getAdapter();
89
- if (adapter) {
90
- adapter.prepare("UPDATE slices SET replan_triggered_at = :ts WHERE milestone_id = :mid AND id = :sid").run({ ":ts": ts, ":mid": mid, ":sid": sid });
91
- }
88
+ setSliceReplanTriggeredAt(mid, sid, ts);
92
89
  }
93
90
  }
94
91
  catch {
@@ -1,4 +1,4 @@
1
- import { _getAdapter, transaction, } from "./gsd-db.js";
1
+ import { _getAdapter, readTransaction, restoreManifest, } from "./gsd-db.js";
2
2
  import { atomicWriteSync } from "./atomic-write.js";
3
3
  import { readFileSync, existsSync, mkdirSync } from "node:fs";
4
4
  import { join } from "node:path";
@@ -41,8 +41,7 @@ export function snapshotState() {
41
41
  const db = requireDb();
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
- db.exec("BEGIN DEFERRED");
45
- try {
44
+ return readTransaction(() => {
46
45
  const rawMilestones = db.prepare("SELECT * FROM milestones ORDER BY id").all();
47
46
  const milestones = rawMilestones.map((r) => ({
48
47
  id: r["id"],
@@ -146,74 +145,14 @@ export function snapshotState() {
146
145
  decisions,
147
146
  verification_evidence,
148
147
  };
149
- db.exec("COMMIT");
150
148
  return result;
151
- }
152
- catch (err) {
153
- try {
154
- db.exec("ROLLBACK");
155
- }
156
- catch { /* ignore rollback failure */ }
157
- throw err;
158
- }
159
- }
160
- // ─── restore ─────────────────────────────────────────────────────────────
161
- /**
162
- * Atomically replace all workflow state from a manifest.
163
- * Runs inside a transaction — if any insert fails, no tables are modified.
164
- * Only touches engine tables + decisions. Does NOT modify artifacts or memories.
165
- */
166
- function restore(manifest) {
167
- const db = requireDb();
168
- transaction(() => {
169
- // Clear engine tables (order matters for foreign-key-like consistency)
170
- db.exec("DELETE FROM verification_evidence");
171
- db.exec("DELETE FROM tasks");
172
- db.exec("DELETE FROM slices");
173
- db.exec("DELETE FROM milestones");
174
- db.exec("DELETE FROM decisions WHERE 1=1");
175
- // Restore milestones
176
- const msStmt = db.prepare(`INSERT INTO milestones (id, title, status, depends_on, created_at, completed_at,
177
- vision, success_criteria, key_risks, proof_strategy,
178
- verification_contract, verification_integration, verification_operational, verification_uat,
179
- definition_of_done, requirement_coverage, boundary_map_markdown)
180
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
181
- for (const m of manifest.milestones) {
182
- msStmt.run(m.id, m.title, m.status, JSON.stringify(m.depends_on), m.created_at, m.completed_at, m.vision, JSON.stringify(m.success_criteria), JSON.stringify(m.key_risks), JSON.stringify(m.proof_strategy), m.verification_contract, m.verification_integration, m.verification_operational, m.verification_uat, JSON.stringify(m.definition_of_done), m.requirement_coverage, m.boundary_map_markdown);
183
- }
184
- // Restore slices
185
- const slStmt = db.prepare(`INSERT INTO slices (milestone_id, id, title, status, risk, depends, demo,
186
- created_at, completed_at, full_summary_md, full_uat_md,
187
- goal, success_criteria, proof_level, integration_closure, observability_impact,
188
- sequence, replan_triggered_at)
189
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
190
- for (const s of manifest.slices) {
191
- slStmt.run(s.milestone_id, s.id, s.title, s.status, s.risk, JSON.stringify(s.depends), s.demo, s.created_at, s.completed_at, s.full_summary_md, s.full_uat_md, s.goal, s.success_criteria, s.proof_level, s.integration_closure, s.observability_impact, s.sequence, s.replan_triggered_at);
192
- }
193
- // Restore tasks
194
- const tkStmt = db.prepare(`INSERT INTO tasks (milestone_id, slice_id, id, title, status,
195
- one_liner, narrative, verification_result, duration, completed_at,
196
- blocker_discovered, deviations, known_issues, key_files, key_decisions,
197
- full_summary_md, description, estimate, files, verify,
198
- inputs, expected_output, observability_impact, sequence)
199
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
200
- for (const t of manifest.tasks) {
201
- tkStmt.run(t.milestone_id, t.slice_id, t.id, t.title, t.status, t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at, t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues, JSON.stringify(t.key_files), JSON.stringify(t.key_decisions), t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify, JSON.stringify(t.inputs), JSON.stringify(t.expected_output), t.observability_impact, t.sequence);
202
- }
203
- // Restore decisions
204
- const dcStmt = db.prepare(`INSERT INTO decisions (seq, id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
205
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`);
206
- for (const d of manifest.decisions) {
207
- dcStmt.run(d.seq, d.id, d.when_context, d.scope, d.decision, d.choice, d.rationale, d.revisable, d.made_by, d.superseded_by);
208
- }
209
- // Restore verification evidence
210
- const evStmt = db.prepare(`INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
211
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`);
212
- for (const e of manifest.verification_evidence) {
213
- evStmt.run(e.task_id, e.slice_id, e.milestone_id, e.command, e.exit_code, e.verdict, e.duration_ms, e.created_at);
214
- }
215
149
  });
216
150
  }
151
+ // ─── restore ─────────────────────────────────────────────────────────────
152
+ //
153
+ // The actual restore() implementation lives in gsd-db.ts (single-writer
154
+ // invariant). This module only orchestrates reading the manifest file
155
+ // and handing it to the writer.
217
156
  // ─── writeManifest ───────────────────────────────────────────────────────
218
157
  /**
219
158
  * Write current DB state to .gsd/state-manifest.json via atomicWriteSync.
@@ -258,6 +197,6 @@ export function bootstrapFromManifest(basePath) {
258
197
  if (!manifest) {
259
198
  return false;
260
199
  }
261
- restore(manifest);
200
+ restoreManifest(manifest);
262
201
  return true;
263
202
  }
@@ -4,7 +4,7 @@
4
4
  // Populates data into the already-existing v10 schema tables.
5
5
  import { existsSync, readdirSync, readFileSync } from "node:fs";
6
6
  import { join } from "node:path";
7
- import { _getAdapter, transaction } from "./gsd-db.js";
7
+ import { _getAdapter, bulkInsertLegacyHierarchy } from "./gsd-db.js";
8
8
  import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
9
9
  import { logWarning } from "./workflow-logger.js";
10
10
  // ─── needsAutoMigration ───────────────────────────────────────────────────
@@ -174,27 +174,26 @@ export function migrateFromMarkdown(basePath) {
174
174
  process.stderr.write("workflow-migration: no milestones collected, nothing to insert\n");
175
175
  return;
176
176
  }
177
- const placeholders = migratedMilestoneIds.map(() => "?").join(",");
178
- transaction(() => {
179
- // Clear existing data to handle stale DB shape (DELETE ... IN (...))
180
- db.prepare(`DELETE FROM tasks WHERE milestone_id IN (${placeholders})`).run(...migratedMilestoneIds);
181
- db.prepare(`DELETE FROM slices WHERE milestone_id IN (${placeholders})`).run(...migratedMilestoneIds);
182
- db.prepare(`DELETE FROM milestones WHERE id IN (${placeholders})`).run(...migratedMilestoneIds);
183
- // Insert milestones
184
- const insertMilestone = db.prepare("INSERT INTO milestones (id, title, status, created_at) VALUES (?, ?, ?, ?)");
185
- for (const m of milestoneInserts) {
186
- insertMilestone.run(m.id, m.title, m.status, now);
187
- }
188
- // Insert slices (using v10 column names: depends, sequence)
189
- const insertSlice = db.prepare("INSERT INTO slices (id, milestone_id, title, status, risk, depends, sequence, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)");
190
- for (const s of sliceInserts) {
191
- insertSlice.run(s.id, s.milestoneId, s.title, s.status, s.risk, "[]", s.sequence, now);
192
- }
193
- // Insert tasks (using v10 column names: sequence, blocker_discovered, full_summary_md)
194
- const insertTask = db.prepare("INSERT INTO tasks (id, slice_id, milestone_id, title, description, status, estimate, files, sequence) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)");
195
- for (const t of taskInserts) {
196
- insertTask.run(t.id, t.sliceId, t.milestoneId, t.title, "", t.status, "", "[]", t.sequence);
197
- }
177
+ bulkInsertLegacyHierarchy({
178
+ milestones: milestoneInserts,
179
+ slices: sliceInserts.map(s => ({
180
+ id: s.id,
181
+ milestoneId: s.milestoneId,
182
+ title: s.title,
183
+ status: s.status,
184
+ risk: s.risk,
185
+ sequence: s.sequence,
186
+ })),
187
+ tasks: taskInserts.map(t => ({
188
+ id: t.id,
189
+ sliceId: t.sliceId,
190
+ milestoneId: t.milestoneId,
191
+ title: t.title,
192
+ status: t.status,
193
+ sequence: t.sequence,
194
+ })),
195
+ clearMilestoneIds: migratedMilestoneIds,
196
+ createdAt: now,
198
197
  });
199
198
  }
200
199
  // ─── validateMigration ────────────────────────────────────────────────────
@@ -303,7 +303,10 @@ export async function renderStateProjection(basePath) {
303
303
  try {
304
304
  adapter.prepare("SELECT 1").get();
305
305
  }
306
- catch {
306
+ catch (err) {
307
+ logWarning("projection", "renderStateProjection: DB handle probe failed, skipping render", {
308
+ error: err.message,
309
+ });
307
310
  return;
308
311
  }
309
312
  const state = await deriveState(basePath);
@@ -2,7 +2,7 @@ import { join } from "node:path";
2
2
  import { mkdirSync, existsSync, readFileSync, unlinkSync } from "node:fs";
3
3
  import { logWarning, logError } from "./workflow-logger.js";
4
4
  import { readEvents, findForkPoint, getSessionId } from "./workflow-events.js";
5
- import { transaction, updateTaskStatus, updateSliceStatus, updateMilestoneStatus, getSliceTasks, insertMilestone, _getAdapter, getMilestoneSlices, insertVerificationEvidence, upsertDecision, openDatabase, setTaskBlockerDiscovered, } from "./gsd-db.js";
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
8
  import { clearPathCache } from "./paths.js";
@@ -132,11 +132,12 @@ function replayEvents(events) {
132
132
  const milestoneId = p["milestoneId"];
133
133
  const sliceId = p["sliceId"];
134
134
  if (milestoneId && sliceId) {
135
- const adapter = _getAdapter();
136
- if (adapter) {
137
- adapter.prepare(`INSERT OR IGNORE INTO slices (milestone_id, id, title, status, created_at)
138
- VALUES (:mid, :sid, :title, 'pending', :ts)`).run({ ":mid": milestoneId, ":sid": sliceId, ":title": p["title"] ?? sliceId, ":ts": event.ts });
139
- }
135
+ insertOrIgnoreSlice({
136
+ milestoneId,
137
+ sliceId,
138
+ title: p["title"] ?? sliceId,
139
+ createdAt: event.ts,
140
+ });
140
141
  }
141
142
  break;
142
143
  }
@@ -148,11 +149,13 @@ function replayEvents(events) {
148
149
  const sliceId = p["sliceId"];
149
150
  const taskId = p["taskId"];
150
151
  if (milestoneId && sliceId && taskId) {
151
- const adapter = _getAdapter();
152
- if (adapter) {
153
- adapter.prepare(`INSERT OR IGNORE INTO tasks (milestone_id, slice_id, id, title, status, created_at)
154
- VALUES (:mid, :sid, :tid, :title, 'pending', :ts)`).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId, ":title": p["title"] ?? taskId, ":ts": event.ts });
155
- }
152
+ insertOrIgnoreTask({
153
+ milestoneId,
154
+ sliceId,
155
+ taskId,
156
+ title: p["title"] ?? taskId,
157
+ createdAt: event.ts,
158
+ });
156
159
  }
157
160
  break;
158
161
  }