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
@@ -5,7 +5,7 @@ import {
5
5
  getMilestone,
6
6
  getSliceStatusSummary,
7
7
  getSliceTaskCounts,
8
- _getAdapter,
8
+ readTransaction,
9
9
  saveGateResult,
10
10
  } from "../gsd-db.js";
11
11
  import { GATE_REGISTRY } from "../gate-registry.js";
@@ -616,12 +616,9 @@ export async function executeMilestoneStatus(
616
616
  };
617
617
  }
618
618
 
619
- const adapter = _getAdapter()!;
620
- adapter.exec("BEGIN");
621
- try {
619
+ return readTransaction(() => {
622
620
  const milestone = getMilestone(params.milestoneId);
623
621
  if (!milestone) {
624
- adapter.exec("COMMIT");
625
622
  return {
626
623
  content: [{ type: "text", text: `Milestone ${params.milestoneId} not found in database.` }],
627
624
  details: { operation: "milestone_status", milestoneId: params.milestoneId, found: false },
@@ -635,8 +632,6 @@ export async function executeMilestoneStatus(
635
632
  taskCounts: getSliceTaskCounts(params.milestoneId, s.id),
636
633
  }));
637
634
 
638
- adapter.exec("COMMIT");
639
-
640
635
  const result = {
641
636
  milestoneId: milestone.id,
642
637
  title: milestone.title,
@@ -651,10 +646,7 @@ export async function executeMilestoneStatus(
651
646
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
652
647
  details: { operation: "milestone_status", milestoneId: milestone.id, sliceCount: slices.length },
653
648
  };
654
- } catch (txErr) {
655
- try { adapter.exec("ROLLBACK"); } catch { /* swallow */ }
656
- throw txErr;
657
- }
649
+ });
658
650
  } catch (err) {
659
651
  const msg = err instanceof Error ? err.message : String(err);
660
652
  logWarning("tool", `gsd_milestone_status tool failed: ${msg}`);
@@ -111,14 +111,9 @@ export function executeReplan(
111
111
  // Also write replan_triggered_at column for DB-backed detection
112
112
  try {
113
113
  const req = createRequire(import.meta.url);
114
- const { isDbAvailable, _getAdapter } = req("./gsd-db.js");
114
+ const { isDbAvailable, setSliceReplanTriggeredAt } = req("./gsd-db.js");
115
115
  if (isDbAvailable()) {
116
- const adapter = _getAdapter();
117
- if (adapter) {
118
- adapter.prepare(
119
- "UPDATE slices SET replan_triggered_at = :ts WHERE milestone_id = :mid AND id = :sid",
120
- ).run({ ":ts": ts, ":mid": mid, ":sid": sid });
121
- }
116
+ setSliceReplanTriggeredAt(mid, sid, ts);
122
117
  }
123
118
  } catch {
124
119
  // DB write is best-effort — disk file is the primary trigger for fallback path
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  _getAdapter,
3
- transaction,
3
+ readTransaction,
4
+ restoreManifest,
4
5
  type MilestoneRow,
5
6
  type SliceRow,
6
7
  type TaskRow,
@@ -74,9 +75,7 @@ export function snapshotState(): StateManifest {
74
75
 
75
76
  // Wrap all reads in a deferred transaction so the snapshot is consistent
76
77
  // (all SELECTs see the same DB state even if a concurrent write lands between them).
77
- db.exec("BEGIN DEFERRED");
78
-
79
- try {
78
+ return readTransaction(() => {
80
79
  const rawMilestones = db.prepare("SELECT * FROM milestones ORDER BY id").all() as Record<string, unknown>[];
81
80
  const milestones: MilestoneRow[] = rawMilestones.map((r) => ({
82
81
  id: r["id"] as string,
@@ -186,109 +185,15 @@ export function snapshotState(): StateManifest {
186
185
  verification_evidence,
187
186
  };
188
187
 
189
- db.exec("COMMIT");
190
188
  return result;
191
- } catch (err) {
192
- try { db.exec("ROLLBACK"); } catch { /* ignore rollback failure */ }
193
- throw err;
194
- }
189
+ });
195
190
  }
196
191
 
197
192
  // ─── restore ─────────────────────────────────────────────────────────────
198
-
199
- /**
200
- * Atomically replace all workflow state from a manifest.
201
- * Runs inside a transaction — if any insert fails, no tables are modified.
202
- * Only touches engine tables + decisions. Does NOT modify artifacts or memories.
203
- */
204
- function restore(manifest: StateManifest): void {
205
- const db = requireDb();
206
-
207
- transaction(() => {
208
- // Clear engine tables (order matters for foreign-key-like consistency)
209
- db.exec("DELETE FROM verification_evidence");
210
- db.exec("DELETE FROM tasks");
211
- db.exec("DELETE FROM slices");
212
- db.exec("DELETE FROM milestones");
213
- db.exec("DELETE FROM decisions WHERE 1=1");
214
-
215
- // Restore milestones
216
- const msStmt = db.prepare(
217
- `INSERT INTO milestones (id, title, status, depends_on, created_at, completed_at,
218
- vision, success_criteria, key_risks, proof_strategy,
219
- verification_contract, verification_integration, verification_operational, verification_uat,
220
- definition_of_done, requirement_coverage, boundary_map_markdown)
221
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
222
- );
223
- for (const m of manifest.milestones) {
224
- msStmt.run(
225
- m.id, m.title, m.status,
226
- JSON.stringify(m.depends_on), m.created_at, m.completed_at,
227
- m.vision, JSON.stringify(m.success_criteria), JSON.stringify(m.key_risks),
228
- JSON.stringify(m.proof_strategy),
229
- m.verification_contract, m.verification_integration, m.verification_operational, m.verification_uat,
230
- JSON.stringify(m.definition_of_done), m.requirement_coverage, m.boundary_map_markdown,
231
- );
232
- }
233
-
234
- // Restore slices
235
- const slStmt = db.prepare(
236
- `INSERT INTO slices (milestone_id, id, title, status, risk, depends, demo,
237
- created_at, completed_at, full_summary_md, full_uat_md,
238
- goal, success_criteria, proof_level, integration_closure, observability_impact,
239
- sequence, replan_triggered_at)
240
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
241
- );
242
- for (const s of manifest.slices) {
243
- slStmt.run(
244
- s.milestone_id, s.id, s.title, s.status, s.risk,
245
- JSON.stringify(s.depends), s.demo,
246
- s.created_at, s.completed_at, s.full_summary_md, s.full_uat_md,
247
- s.goal, s.success_criteria, s.proof_level, s.integration_closure, s.observability_impact,
248
- s.sequence, s.replan_triggered_at,
249
- );
250
- }
251
-
252
- // Restore tasks
253
- const tkStmt = db.prepare(
254
- `INSERT INTO tasks (milestone_id, slice_id, id, title, status,
255
- one_liner, narrative, verification_result, duration, completed_at,
256
- blocker_discovered, deviations, known_issues, key_files, key_decisions,
257
- full_summary_md, description, estimate, files, verify,
258
- inputs, expected_output, observability_impact, sequence)
259
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
260
- );
261
- for (const t of manifest.tasks) {
262
- tkStmt.run(
263
- t.milestone_id, t.slice_id, t.id, t.title, t.status,
264
- t.one_liner, t.narrative, t.verification_result, t.duration, t.completed_at,
265
- t.blocker_discovered ? 1 : 0, t.deviations, t.known_issues,
266
- JSON.stringify(t.key_files), JSON.stringify(t.key_decisions),
267
- t.full_summary_md, t.description, t.estimate, JSON.stringify(t.files), t.verify,
268
- JSON.stringify(t.inputs), JSON.stringify(t.expected_output),
269
- t.observability_impact, t.sequence,
270
- );
271
- }
272
-
273
- // Restore decisions
274
- const dcStmt = db.prepare(
275
- `INSERT INTO decisions (seq, id, when_context, scope, decision, choice, rationale, revisable, made_by, superseded_by)
276
- VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
277
- );
278
- for (const d of manifest.decisions) {
279
- 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);
280
- }
281
-
282
- // Restore verification evidence
283
- const evStmt = db.prepare(
284
- `INSERT INTO verification_evidence (task_id, slice_id, milestone_id, command, exit_code, verdict, duration_ms, created_at)
285
- VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
286
- );
287
- for (const e of manifest.verification_evidence) {
288
- evStmt.run(e.task_id, e.slice_id, e.milestone_id, e.command, e.exit_code, e.verdict, e.duration_ms, e.created_at);
289
- }
290
- });
291
- }
193
+ //
194
+ // The actual restore() implementation lives in gsd-db.ts (single-writer
195
+ // invariant). This module only orchestrates reading the manifest file
196
+ // and handing it to the writer.
292
197
 
293
198
  // ─── writeManifest ───────────────────────────────────────────────────────
294
199
 
@@ -346,6 +251,6 @@ export function bootstrapFromManifest(basePath: string): boolean {
346
251
  return false;
347
252
  }
348
253
 
349
- restore(manifest);
254
+ restoreManifest(manifest);
350
255
  return true;
351
256
  }
@@ -5,7 +5,7 @@
5
5
 
6
6
  import { existsSync, readdirSync, readFileSync } from "node:fs";
7
7
  import { join } from "node:path";
8
- import { _getAdapter, transaction } from "./gsd-db.js";
8
+ import { _getAdapter, bulkInsertLegacyHierarchy } from "./gsd-db.js";
9
9
  import { parseRoadmap, parsePlan } from "./parsers-legacy.js";
10
10
  import { logWarning } from "./workflow-logger.js";
11
11
 
@@ -219,34 +219,26 @@ export function migrateFromMarkdown(basePath: string): void {
219
219
  return;
220
220
  }
221
221
 
222
- const placeholders = migratedMilestoneIds.map(() => "?").join(",");
223
- transaction(() => {
224
- // Clear existing data to handle stale DB shape (DELETE ... IN (...))
225
- db.prepare(`DELETE FROM tasks WHERE milestone_id IN (${placeholders})`).run(...migratedMilestoneIds);
226
- db.prepare(`DELETE FROM slices WHERE milestone_id IN (${placeholders})`).run(...migratedMilestoneIds);
227
- db.prepare(`DELETE FROM milestones WHERE id IN (${placeholders})`).run(...migratedMilestoneIds);
228
-
229
- // Insert milestones
230
- const insertMilestone = db.prepare("INSERT INTO milestones (id, title, status, created_at) VALUES (?, ?, ?, ?)");
231
- for (const m of milestoneInserts) {
232
- insertMilestone.run(m.id, m.title, m.status, now);
233
- }
234
-
235
- // Insert slices (using v10 column names: depends, sequence)
236
- const insertSlice = db.prepare(
237
- "INSERT INTO slices (id, milestone_id, title, status, risk, depends, sequence, created_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?)"
238
- );
239
- for (const s of sliceInserts) {
240
- insertSlice.run(s.id, s.milestoneId, s.title, s.status, s.risk, "[]", s.sequence, now);
241
- }
242
-
243
- // Insert tasks (using v10 column names: sequence, blocker_discovered, full_summary_md)
244
- const insertTask = db.prepare(
245
- "INSERT INTO tasks (id, slice_id, milestone_id, title, description, status, estimate, files, sequence) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)"
246
- );
247
- for (const t of taskInserts) {
248
- insertTask.run(t.id, t.sliceId, t.milestoneId, t.title, "", t.status, "", "[]", t.sequence);
249
- }
222
+ bulkInsertLegacyHierarchy({
223
+ milestones: milestoneInserts,
224
+ slices: sliceInserts.map(s => ({
225
+ id: s.id,
226
+ milestoneId: s.milestoneId,
227
+ title: s.title,
228
+ status: s.status,
229
+ risk: s.risk,
230
+ sequence: s.sequence,
231
+ })),
232
+ tasks: taskInserts.map(t => ({
233
+ id: t.id,
234
+ sliceId: t.sliceId,
235
+ milestoneId: t.milestoneId,
236
+ title: t.title,
237
+ status: t.status,
238
+ sequence: t.sequence,
239
+ })),
240
+ clearMilestoneIds: migratedMilestoneIds,
241
+ createdAt: now,
250
242
  });
251
243
  }
252
244
 
@@ -350,7 +350,14 @@ export async function renderStateProjection(basePath: string): Promise<void> {
350
350
  // Probe DB handle — adapter may be set but underlying handle closed
351
351
  const adapter = _getAdapter();
352
352
  if (!adapter) return;
353
- try { adapter.prepare("SELECT 1").get(); } catch { return; }
353
+ try {
354
+ adapter.prepare("SELECT 1").get();
355
+ } catch (err) {
356
+ logWarning("projection", "renderStateProjection: DB handle probe failed, skipping render", {
357
+ error: (err as Error).message,
358
+ });
359
+ return;
360
+ }
354
361
  const state = await deriveState(basePath);
355
362
  const content = renderStateContent(state);
356
363
  const dir = join(basePath, ".gsd");
@@ -10,12 +10,13 @@ import {
10
10
  updateMilestoneStatus,
11
11
  getSliceTasks,
12
12
  insertMilestone,
13
- _getAdapter,
14
13
  getMilestoneSlices,
15
14
  insertVerificationEvidence,
16
15
  upsertDecision,
17
16
  openDatabase,
18
17
  setTaskBlockerDiscovered,
18
+ insertOrIgnoreSlice,
19
+ insertOrIgnoreTask,
19
20
  } from "./gsd-db.js";
20
21
  import { isClosedStatus } from "./status-guards.js";
21
22
  import { invalidateStateCache } from "./state.js";
@@ -164,13 +165,12 @@ function replayEvents(events: WorkflowEvent[]): void {
164
165
  const milestoneId = p["milestoneId"] as string;
165
166
  const sliceId = p["sliceId"] as string;
166
167
  if (milestoneId && sliceId) {
167
- const adapter = _getAdapter();
168
- if (adapter) {
169
- adapter.prepare(
170
- `INSERT OR IGNORE INTO slices (milestone_id, id, title, status, created_at)
171
- VALUES (:mid, :sid, :title, 'pending', :ts)`,
172
- ).run({ ":mid": milestoneId, ":sid": sliceId, ":title": (p["title"] as string) ?? sliceId, ":ts": event.ts });
173
- }
168
+ insertOrIgnoreSlice({
169
+ milestoneId,
170
+ sliceId,
171
+ title: (p["title"] as string) ?? sliceId,
172
+ createdAt: event.ts,
173
+ });
174
174
  }
175
175
  break;
176
176
  }
@@ -182,13 +182,13 @@ function replayEvents(events: WorkflowEvent[]): void {
182
182
  const sliceId = p["sliceId"] as string;
183
183
  const taskId = p["taskId"] as string;
184
184
  if (milestoneId && sliceId && taskId) {
185
- const adapter = _getAdapter();
186
- if (adapter) {
187
- adapter.prepare(
188
- `INSERT OR IGNORE INTO tasks (milestone_id, slice_id, id, title, status, created_at)
189
- VALUES (:mid, :sid, :tid, :title, 'pending', :ts)`,
190
- ).run({ ":mid": milestoneId, ":sid": sliceId, ":tid": taskId, ":title": (p["title"] as string) ?? taskId, ":ts": event.ts });
191
- }
185
+ insertOrIgnoreTask({
186
+ milestoneId,
187
+ sliceId,
188
+ taskId,
189
+ title: (p["title"] as string) ?? taskId,
190
+ createdAt: event.ts,
191
+ });
192
192
  }
193
193
  break;
194
194
  }