@opengsd/gsd-pi 1.9.0-dev.97ef11e3 → 1.9.0-dev.e10b60d4

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 (113) hide show
  1. package/dist/resources/.managed-resources-content-hash +1 -1
  2. package/dist/resources/extensions/gsd/auto/loop.js +4 -24
  3. package/dist/resources/extensions/gsd/auto/run-unit.js +1 -19
  4. package/dist/resources/extensions/gsd/auto-start.js +9 -29
  5. package/dist/resources/extensions/gsd/auto-worktree-sync.js +3 -7
  6. package/dist/resources/extensions/gsd/auto.js +1 -12
  7. package/dist/resources/extensions/gsd/doctor-engine-checks.js +2 -21
  8. package/dist/resources/extensions/gsd/flat-phase-migration.js +0 -12
  9. package/dist/resources/extensions/gsd/gsd-db.js +31 -75
  10. package/dist/resources/extensions/gsd/worktree-state-projection.js +18 -20
  11. package/dist/tsconfig.extensions.tsbuildinfo +1 -1
  12. package/dist/web/standalone/.next/BUILD_ID +1 -1
  13. package/dist/web/standalone/.next/app-path-routes-manifest.json +10 -10
  14. package/dist/web/standalone/.next/build-manifest.json +2 -2
  15. package/dist/web/standalone/.next/prerender-manifest.json +3 -3
  16. package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
  17. package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
  18. package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
  19. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
  20. package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
  21. package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
  22. package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
  23. package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
  24. package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
  25. package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
  26. package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
  27. package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
  28. package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
  29. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
  30. package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
  31. package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
  32. package/dist/web/standalone/.next/server/app/index.html +1 -1
  33. package/dist/web/standalone/.next/server/app/index.rsc +1 -1
  34. package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
  35. package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
  36. package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
  37. package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
  38. package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
  39. package/dist/web/standalone/.next/server/app-paths-manifest.json +10 -10
  40. package/dist/web/standalone/.next/server/chunks/1991.js +2 -2
  41. package/dist/web/standalone/.next/server/chunks/5124.js +1 -1
  42. package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
  43. package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
  44. package/dist/web/standalone/.next/server/pages/404.html +1 -1
  45. package/dist/web/standalone/.next/server/pages/500.html +1 -1
  46. package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
  47. package/package.json +1 -1
  48. package/packages/cloud-mcp-gateway/package.json +2 -2
  49. package/packages/contracts/package.json +1 -1
  50. package/packages/daemon/package.json +4 -4
  51. package/packages/gsd-agent-core/package.json +5 -5
  52. package/packages/gsd-agent-modes/package.json +7 -7
  53. package/packages/gsd-cloud/README.md +3 -9
  54. package/packages/gsd-cloud/dist/cli.d.ts.map +1 -1
  55. package/packages/gsd-cloud/dist/cli.js +15 -121
  56. package/packages/gsd-cloud/dist/cli.js.map +1 -1
  57. package/packages/gsd-cloud/dist/cloud-config.d.ts +1 -1
  58. package/packages/gsd-cloud/dist/cloud-config.d.ts.map +1 -1
  59. package/packages/gsd-cloud/dist/cloud-config.js +1 -7
  60. package/packages/gsd-cloud/dist/cloud-config.js.map +1 -1
  61. package/packages/gsd-cloud/dist/cloud-runtime.d.ts +3 -1
  62. package/packages/gsd-cloud/dist/cloud-runtime.d.ts.map +1 -1
  63. package/packages/gsd-cloud/dist/cloud-runtime.js +7 -9
  64. package/packages/gsd-cloud/dist/cloud-runtime.js.map +1 -1
  65. package/packages/gsd-cloud/package.json +2 -2
  66. package/packages/mcp-server/dist/cli-runner.d.ts +0 -3
  67. package/packages/mcp-server/dist/cli-runner.d.ts.map +1 -1
  68. package/packages/mcp-server/dist/cli-runner.js +33 -119
  69. package/packages/mcp-server/dist/cli-runner.js.map +1 -1
  70. package/packages/mcp-server/package.json +4 -4
  71. package/packages/native/package.json +1 -1
  72. package/packages/pi-agent-core/package.json +1 -1
  73. package/packages/pi-ai/dist/models.generated.d.ts +0 -17
  74. package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
  75. package/packages/pi-ai/dist/models.generated.js +0 -17
  76. package/packages/pi-ai/dist/models.generated.js.map +1 -1
  77. package/packages/pi-ai/package.json +1 -1
  78. package/packages/pi-coding-agent/package.json +7 -7
  79. package/packages/pi-tui/package.json +2 -2
  80. package/packages/rpc-client/package.json +2 -2
  81. package/pkg/package.json +1 -1
  82. package/src/resources/extensions/gsd/auto/loop.ts +4 -48
  83. package/src/resources/extensions/gsd/auto/run-unit.ts +1 -20
  84. package/src/resources/extensions/gsd/auto-start.ts +8 -31
  85. package/src/resources/extensions/gsd/auto-worktree-sync.ts +3 -10
  86. package/src/resources/extensions/gsd/auto.ts +1 -13
  87. package/src/resources/extensions/gsd/doctor-engine-checks.ts +2 -27
  88. package/src/resources/extensions/gsd/flat-phase-migration.ts +0 -14
  89. package/src/resources/extensions/gsd/gsd-db.ts +33 -85
  90. package/src/resources/extensions/gsd/tests/auto-loop.test.ts +0 -156
  91. package/src/resources/extensions/gsd/tests/auto-start-index-lock.test.ts +1 -21
  92. package/src/resources/extensions/gsd/tests/auto-wrapup-inflight-guard.test.ts +1 -74
  93. package/src/resources/extensions/gsd/tests/doctor-scope-db-unavailable.test.ts +0 -83
  94. package/src/resources/extensions/gsd/tests/flat-phase-migration.test.ts +9 -8
  95. package/src/resources/extensions/gsd/tests/gsd-db.test.ts +0 -32
  96. package/src/resources/extensions/gsd/tests/integration/git-service.test.ts +0 -13
  97. package/src/resources/extensions/gsd/tests/worktree-state-projection.test.ts +2 -2
  98. package/src/resources/extensions/gsd/tests/worktree-sync-milestones.test.ts +0 -60
  99. package/src/resources/extensions/gsd/worktree-state-projection.ts +24 -21
  100. package/packages/gsd-cloud/dist/background-cli.test.d.ts +0 -2
  101. package/packages/gsd-cloud/dist/background-cli.test.d.ts.map +0 -1
  102. package/packages/gsd-cloud/dist/background-cli.test.js +0 -248
  103. package/packages/gsd-cloud/dist/background-cli.test.js.map +0 -1
  104. package/packages/gsd-cloud/dist/runtime-process.d.ts +0 -27
  105. package/packages/gsd-cloud/dist/runtime-process.d.ts.map +0 -1
  106. package/packages/gsd-cloud/dist/runtime-process.js +0 -272
  107. package/packages/gsd-cloud/dist/runtime-process.js.map +0 -1
  108. package/packages/gsd-cloud/dist/runtime-process.test.d.ts +0 -2
  109. package/packages/gsd-cloud/dist/runtime-process.test.d.ts.map +0 -1
  110. package/packages/gsd-cloud/dist/runtime-process.test.js +0 -97
  111. package/packages/gsd-cloud/dist/runtime-process.test.js.map +0 -1
  112. /package/dist/web/standalone/.next/static/{DXD9fZPnqtjZSlT5xdC3v → V9MAVyYVPboQUB9KTTAMi}/_buildManifest.js +0 -0
  113. /package/dist/web/standalone/.next/static/{DXD9fZPnqtjZSlT5xdC3v → V9MAVyYVPboQUB9KTTAMi}/_ssgManifest.js +0 -0
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-ai",
3
- "version": "1.9.0-dev.97ef11e3",
3
+ "version": "1.9.0-dev.e10b60d4",
4
4
  "description": "Unified LLM API with automatic model discovery and provider configuration",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-coding-agent",
3
- "version": "1.9.0-dev.97ef11e3",
3
+ "version": "1.9.0-dev.e10b60d4",
4
4
  "description": "Coding agent CLI (vendored from earendil-works/pi)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -33,7 +33,7 @@
33
33
  "copy-assets": "node scripts/copy-assets.cjs"
34
34
  },
35
35
  "dependencies": {
36
- "@opengsd/contracts": "^1.9.0-dev.97ef11e3",
36
+ "@opengsd/contracts": "^1.9.0-dev.e10b60d4",
37
37
  "@mariozechner/jiti": "^2.6.2",
38
38
  "@silvia-odwyer/photon-node": "0.3.4",
39
39
  "chalk": "5.6.2",
@@ -53,11 +53,11 @@
53
53
  "typebox": "1.1.38",
54
54
  "undici": "7.28.0",
55
55
  "yaml": "2.9.0",
56
- "@gsd/agent-core": "^1.9.0-dev.97ef11e3",
57
- "@gsd/native": "^1.9.0-dev.97ef11e3",
58
- "@gsd/pi-agent-core": "^1.9.0-dev.97ef11e3",
59
- "@gsd/pi-ai": "^1.9.0-dev.97ef11e3",
60
- "@gsd/pi-tui": "^1.9.0-dev.97ef11e3",
56
+ "@gsd/agent-core": "^1.9.0-dev.e10b60d4",
57
+ "@gsd/native": "^1.9.0-dev.e10b60d4",
58
+ "@gsd/pi-agent-core": "^1.9.0-dev.e10b60d4",
59
+ "@gsd/pi-ai": "^1.9.0-dev.e10b60d4",
60
+ "@gsd/pi-tui": "^1.9.0-dev.e10b60d4",
61
61
  "@sinclair/typebox": "^0.34.41"
62
62
  },
63
63
  "devDependencies": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gsd/pi-tui",
3
- "version": "1.9.0-dev.97ef11e3",
3
+ "version": "1.9.0-dev.e10b60d4",
4
4
  "description": "Terminal UI library (vendored from earendil-works/pi)",
5
5
  "type": "module",
6
6
  "gsd": {
@@ -21,7 +21,7 @@
21
21
  "build": "node ../../scripts/clean-package-dist.cjs && tsc -p tsconfig.json --incremental false"
22
22
  },
23
23
  "dependencies": {
24
- "@gsd/native": "^1.9.0-dev.97ef11e3",
24
+ "@gsd/native": "^1.9.0-dev.e10b60d4",
25
25
  "get-east-asian-width": "1.6.0",
26
26
  "marked": "15.0.12",
27
27
  "@sinclair/typebox": "^0.34.41"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengsd/rpc-client",
3
- "version": "1.9.0-dev.97ef11e3",
3
+ "version": "1.9.0-dev.e10b60d4",
4
4
  "description": "Standalone RPC client SDK for GSD — zero internal dependencies",
5
5
  "license": "MIT",
6
6
  "gsd": {
@@ -34,7 +34,7 @@
34
34
  "test": "node --test dist/rpc-client.test.js"
35
35
  },
36
36
  "dependencies": {
37
- "@opengsd/contracts": "^1.9.0-dev.97ef11e3"
37
+ "@opengsd/contracts": "^1.9.0-dev.e10b60d4"
38
38
  },
39
39
  "engines": {
40
40
  "node": ">=22.0.0"
package/pkg/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@glittercowboy/gsd",
3
- "version": "1.9.0-dev.97ef11e3",
3
+ "version": "1.9.0-dev.e10b60d4",
4
4
  "piConfig": {
5
5
  "name": "gsd",
6
6
  "configDir": ".gsd"
@@ -355,41 +355,7 @@ async function enforceMinRequestInterval(s: AutoSession, prefs: IterationContext
355
355
  }
356
356
  }
357
357
 
358
- async function snapshotCrashedUnitWork(
359
- ctx: ExtensionContext,
360
- s: AutoSession,
361
- deps: LoopDeps,
362
- iterData: IterationData,
363
- ): Promise<void> {
364
- try {
365
- const commitMsg = await deps.autoCommitUnit?.(
366
- s.basePath,
367
- iterData.unitType,
368
- iterData.unitId,
369
- ctx,
370
- );
371
- if (commitMsg) {
372
- debugLog("autoLoop", {
373
- phase: "crash-snapshot-commit",
374
- unitType: iterData.unitType,
375
- unitId: iterData.unitId,
376
- });
377
- }
378
- } catch (snapshotErr) {
379
- logWarning(
380
- "dispatch",
381
- `unit crash snapshot failed: ${snapshotErr instanceof Error ? snapshotErr.message : String(snapshotErr)}`,
382
- );
383
- }
384
- }
385
-
386
- async function closeOutCrashedUnit(
387
- ctx: ExtensionContext,
388
- s: AutoSession,
389
- deps: LoopDeps,
390
- iterData: IterationData,
391
- err: unknown,
392
- ): Promise<void> {
358
+ function closeOutCrashedUnit(s: AutoSession, iterData: IterationData, err: unknown): void {
393
359
  const summary = formatDispatchExceptionSummary({ error: err });
394
360
  try {
395
361
  emitOpenUnitEndForUnit(
@@ -417,7 +383,6 @@ async function closeOutCrashedUnit(
417
383
  } catch (closeoutErr) {
418
384
  logWarning("dispatch", `unit crash closeout failed: ${closeoutErr instanceof Error ? closeoutErr.message : String(closeoutErr)}`);
419
385
  }
420
- await snapshotCrashedUnitWork(ctx, s, deps, iterData);
421
386
  }
422
387
 
423
388
  /**
@@ -561,20 +526,11 @@ export async function autoLoop(
561
526
  active: s.active,
562
527
  iteration,
563
528
  maxIterations: MAX_LOOP_ITERATIONS,
564
- hasCommandContext: typeof s.cmdCtx?.newSession === "function",
529
+ hasCommandContext: Boolean(s.cmdCtx),
565
530
  sessionLockValid: true,
566
531
  });
567
532
  if (commandContextDecision.action === "stop" && commandContextDecision.reason === "missing-command-context") {
568
533
  debugLog("autoLoop", { phase: "exit", reason: "no-cmdCtx" });
569
- if (s.currentUnit) {
570
- await deps.autoCommitUnit?.(
571
- s.basePath,
572
- s.currentUnit.type,
573
- s.currentUnit.id,
574
- ctx,
575
- );
576
- }
577
- await deps.stopAuto(ctx, pi, commandContextDecision.message, { preserveWorktree: true });
578
534
  finishTurn("stopped", "manual-attention", commandContextDecision.reason);
579
535
  break;
580
536
  }
@@ -774,7 +730,7 @@ export async function autoLoop(
774
730
  if (err instanceof ModelPolicyDispatchBlockedError) {
775
731
  throw err;
776
732
  }
777
- await closeOutCrashedUnit(ctx, s, deps, iterData, err);
733
+ closeOutCrashedUnit(s, iterData, err);
778
734
  throw err;
779
735
  }
780
736
  if (unitPhaseResult.action === "next") {
@@ -1329,7 +1285,7 @@ export async function autoLoop(
1329
1285
  if (err instanceof ModelPolicyDispatchBlockedError) {
1330
1286
  throw err;
1331
1287
  }
1332
- await closeOutCrashedUnit(ctx, s, deps, iterData, err);
1288
+ closeOutCrashedUnit(s, iterData, err);
1333
1289
  dispatchSettled = settleDispatchFailed(
1334
1290
  dispatchId,
1335
1291
  formatDispatchExceptionSummary({ error: err }),
@@ -91,25 +91,6 @@ export async function runUnit(
91
91
  // ── Session creation with timeout ──
92
92
  debugLog("runUnit", { phase: "session-create", unitType, unitId });
93
93
 
94
- const cmdCtx = s.cmdCtx;
95
- if (typeof cmdCtx?.newSession !== "function") {
96
- const msg = "command context is missing newSession";
97
- debugLog("runUnit", {
98
- phase: "session-error",
99
- unitType,
100
- unitId,
101
- error: msg,
102
- });
103
- return {
104
- status: "cancelled",
105
- errorContext: {
106
- message: `Session creation failed: ${msg}`,
107
- category: "session-failed",
108
- isTransient: false,
109
- },
110
- };
111
- }
112
-
113
94
  let sessionResult: { cancelled: boolean };
114
95
  let sessionTimeoutHandle: ReturnType<typeof setTimeout> | undefined;
115
96
  const mySessionSwitchGeneration = ++sessionSwitchGeneration;
@@ -119,7 +100,7 @@ export async function runUnit(
119
100
  const sessionAbortController = new AbortController();
120
101
  _setSessionSwitchInFlight(true);
121
102
  try {
122
- const sessionPromise = cmdCtx.newSession({
103
+ const sessionPromise = s.cmdCtx!.newSession({
123
104
  abortSignal: sessionAbortController.signal,
124
105
  workspaceRoot: s.basePath,
125
106
  }).finally(() => {
@@ -88,7 +88,6 @@ import { classifyMilestoneSummaryContent } from "./milestone-summary-classifier.
88
88
  import { extractVerdict } from "./verdict-parser.js";
89
89
  import { auditOrphanedPreflightStashes } from "./orphan-stash-audit.js";
90
90
  import { parseProject } from "./schemas/parsers.js";
91
- import { LAYOUT_SEGMENTS } from "./layout-policy.js";
92
91
 
93
92
  import {
94
93
  debugLog,
@@ -153,33 +152,6 @@ export function hasGitIndexLockForTest(basePath: string): boolean {
153
152
  return existsSync(join(basePath, ".git", "index.lock"));
154
153
  }
155
154
 
156
- function isEmptyDirectory(dirPath: string): boolean {
157
- try {
158
- return existsSync(dirPath) && readdirSync(dirPath).length === 0;
159
- } catch {
160
- return false;
161
- }
162
- }
163
-
164
- export function reconcileFlatPhaseBootstrapLayout(basePath: string): boolean {
165
- const gsdDir = join(basePath, ".gsd");
166
- const phasesPath = join(gsdDir, LAYOUT_SEGMENTS.level1);
167
- const milestonesPath = join(gsdDir, "milestones");
168
-
169
- if (isEmptyDirectory(milestonesPath)) {
170
- if (!existsSync(phasesPath)) mkdirSync(phasesPath, { recursive: true });
171
- rmSync(milestonesPath, { recursive: true, force: true });
172
- return true;
173
- }
174
-
175
- if (!existsSync(phasesPath) && !existsSync(milestonesPath)) {
176
- mkdirSync(phasesPath, { recursive: true });
177
- return true;
178
- }
179
-
180
- return false;
181
- }
182
-
183
155
  export function _shouldAbortBootstrapForUnavailableDbForTest(
184
156
  gsdDbPath: string,
185
157
  dbAvailable: boolean,
@@ -1180,14 +1152,19 @@ export async function bootstrapAutoSession(
1180
1152
  ensureGitignore(base, { manageGitignore });
1181
1153
  if (manageGitignore !== false) untrackRuntimeFiles(base);
1182
1154
 
1183
- // Bootstrap the flat-phase projection root and clean empty legacy scaffolds.
1184
- if (reconcileFlatPhaseBootstrapLayout(base)) {
1155
+ // Bootstrap milestones/ if it doesn't exist.
1156
+ // Check milestones/ directly — ensureGsdSymlink above already created .gsd/,
1157
+ // so checking .gsd/ existence would be dead code (#2942).
1158
+ const gsdDir = join(base, ".gsd");
1159
+ const milestonesPath = join(gsdDir, "milestones");
1160
+ if (!existsSync(milestonesPath)) {
1161
+ mkdirSync(milestonesPath, { recursive: true });
1185
1162
  try {
1186
1163
  nativeAddAll(base);
1187
1164
  nativeCommit(base, "chore: init gsd");
1188
1165
  } catch (err) {
1189
1166
  /* nothing to commit */
1190
- logWarning("engine", `layout bootstrap commit failed: ${err instanceof Error ? err.message : String(err)}`);
1167
+ logWarning("engine", `mkdir failed: ${err instanceof Error ? err.message : String(err)}`);
1191
1168
  }
1192
1169
  }
1193
1170
 
@@ -16,7 +16,7 @@ import {
16
16
  _isSamePath as isSamePath,
17
17
  _shouldReconcileWorktreeDb,
18
18
  } from "./auto-worktree-cleanup.js";
19
- import { dirIsContentBearingLegacyMilestone, resolveGsdPathContract } from "./paths.js";
19
+ import { resolveGsdPathContract } from "./paths.js";
20
20
  import type { MilestoneScope } from "./workspace.js";
21
21
  import { WorktreeStateProjection } from "./worktree-state-projection.js";
22
22
  import { logWarning } from "./workflow-logger.js";
@@ -211,17 +211,10 @@ function syncMilestoneLayout(
211
211
  if (!existsSync(mainMilestonesDir)) return;
212
212
 
213
213
  try {
214
+ mkdirSync(wtMilestonesDir, { recursive: true });
214
215
  const mainMilestones = readdirSync(mainMilestonesDir, { withFileTypes: true })
215
216
  .filter((entry) => entry.isDirectory())
216
- .map((entry) => entry.name)
217
- .filter((name) =>
218
- layoutSegment !== "milestones" ||
219
- dirIsContentBearingLegacyMilestone(join(mainMilestonesDir, name)),
220
- );
221
-
222
- if (mainMilestones.length === 0) return;
223
-
224
- mkdirSync(wtMilestonesDir, { recursive: true });
217
+ .map((entry) => entry.name);
225
218
 
226
219
  for (const milestoneId of mainMilestones) {
227
220
  syncMilestoneDirectory(
@@ -3163,19 +3163,7 @@ export async function dispatchHookUnit(
3163
3163
  workspaceRoot: s.basePath,
3164
3164
  });
3165
3165
 
3166
- const cmdCtx = s.cmdCtx;
3167
- if (typeof cmdCtx?.newSession !== "function") {
3168
- const message = "Auto-mode has no command context for dispatch.";
3169
- ctx.ui.notify(message, "error");
3170
- const unitToCommit = previousCurrentUnit ?? s.currentUnit;
3171
- if (unitToCommit) {
3172
- await autoCommitUnit(s.basePath, unitToCommit.type, unitToCommit.id, ctx);
3173
- }
3174
- await stopAuto(ctx, pi, message, { preserveWorktree: true });
3175
- return false;
3176
- }
3177
-
3178
- const result = await cmdCtx.newSession({ workspaceRoot: s.basePath });
3166
+ const result = await s.cmdCtx!.newSession({ workspaceRoot: s.basePath });
3179
3167
  if (result.cancelled) {
3180
3168
  await stopAuto(ctx, pi);
3181
3169
  return false;
@@ -95,25 +95,6 @@ function reportCheckboxDbStatusDivergence(
95
95
  });
96
96
  }
97
97
 
98
- function bareDuplicateMilestoneId(milestoneId: string): string | null {
99
- const match = milestoneId.match(/^(M\d{3})-[a-z0-9]{6}$/i);
100
- return match?.[1]?.toUpperCase() ?? null;
101
- }
102
-
103
- function checkboxDbStatusMilestoneIds(basePath: string, milestoneIds: string[]): string[] {
104
- if (!hasFlatPhaseLayout(basePath)) return milestoneIds;
105
-
106
- // Flat-phase directories are keyed by phase number. A restored
107
- // M003-xxxxxx row alongside M003 is a stale duplicate for the same
108
- // phases/03-* projection, so checking both can compare DB status against two
109
- // competing PLAN files and report a persistent false divergence.
110
- const allIds = new Set(milestoneIds.map((id) => id.toUpperCase()));
111
- return milestoneIds.filter((milestoneId) => {
112
- const bareId = bareDuplicateMilestoneId(milestoneId);
113
- return !bareId || !allIds.has(bareId);
114
- });
115
- }
116
-
117
98
  function checkProjectionCheckboxDbStatus(basePath: string, milestoneIds: string[], issues: DoctorIssue[]): void {
118
99
  for (const milestoneId of milestoneIds) {
119
100
  const roadmapPath = resolveMilestoneFile(basePath, milestoneId, "ROADMAP");
@@ -453,9 +434,7 @@ function staleMilestonesArtifactRowFixable(basePath: string, row: ArtifactRow, a
453
434
 
454
435
  function stalePhasesArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
455
436
  const issuePath = artifactPathRelativeToGsd(row.path);
456
- return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) &&
457
- (hasPresentFlatPhaseReplacement(basePath, row, artifactRows) ||
458
- hasPresentMilestonesReplacement(basePath, row, artifactRows));
437
+ return issuePath.startsWith(`${LAYOUT_SEGMENTS.level1}/`) && hasPresentMilestonesReplacement(basePath, row, artifactRows);
459
438
  }
460
439
 
461
440
  function staleArtifactRowFixable(basePath: string, row: ArtifactRow, artifactRows: ArtifactRow[]): boolean {
@@ -828,11 +807,7 @@ export async function checkEngineHealth(
828
807
  // block doctor.
829
808
  try {
830
809
  if (isDbAvailable()) {
831
- checkProjectionCheckboxDbStatus(
832
- basePath,
833
- checkboxDbStatusMilestoneIds(basePath, getAllMilestones().map((milestone) => milestone.id)),
834
- issues,
835
- );
810
+ checkProjectionCheckboxDbStatus(basePath, getAllMilestones().map((milestone) => milestone.id), issues);
836
811
  }
837
812
  } catch {
838
813
  // Non-fatal: checkbox-vs-DB divergence check must never block doctor
@@ -155,19 +155,6 @@ function isInsideFlatProjectionBackup(backupDir: string, src: string): boolean {
155
155
  return src === phaseBackupDir || src.startsWith(`${phaseBackupDir}/`) || src.startsWith(`${phaseBackupDir}\\`);
156
156
  }
157
157
 
158
- function removeFlatProjectionBackup(backupDir: string): void {
159
- const phaseBackupDir = join(backupDir, "__phases");
160
- if (!existsSync(phaseBackupDir)) return;
161
- try {
162
- removePathWithRetries(phaseBackupDir);
163
- } catch (err) {
164
- logWarning(
165
- "migration",
166
- `flat-phase migration succeeded but could not remove temporary ${LAYOUT_SEGMENTS.level1}/ backup: ${(err as Error).message}`,
167
- );
168
- }
169
- }
170
-
171
158
  function restoreFlatProjectionFromBackup(basePath: string, backupDir: string): void {
172
159
  const phaseBackupDir = join(backupDir, "__phases");
173
160
  if (!existsSync(phaseBackupDir)) return;
@@ -507,5 +494,4 @@ export async function migrateToFlatPhase(basePath: string): Promise<void> {
507
494
  `flat-phase migration succeeded but could not remove ${LEGACY_MIGRATING_SEGMENT}: ${(err as Error).message}`,
508
495
  );
509
496
  }
510
- removeFlatProjectionBackup(backupDir);
511
497
  }
@@ -67,13 +67,6 @@ export type { SliceRow, TaskRow } from "./db-task-slice-rows.js";
67
67
 
68
68
  import { TERMINAL_STATUS_SQL } from "./db/sql-constants.js";
69
69
  import { applyStatusTransition } from "./db/writers/status.js";
70
- import {
71
- LAYOUT_SEGMENTS,
72
- derivePhaseSlug,
73
- milestoneIdToPhaseNum,
74
- milestoneIdUniqueSuffix,
75
- phaseDirName,
76
- } from "./layout-policy.js";
77
70
 
78
71
  export function insertDecision(d: Omit<Decision, "seq">): void {
79
72
  if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
@@ -213,47 +206,6 @@ export function insertArtifact(a: {
213
206
  });
214
207
  }
215
208
 
216
- function canonicalPhaseDirNameForDb(milestoneId: string, title: string): string {
217
- const phaseNum = milestoneIdToPhaseNum(milestoneId);
218
- const slug = derivePhaseSlug(title || milestoneId);
219
- const suffix = milestoneIdUniqueSuffix(milestoneId);
220
- return phaseDirName(phaseNum, suffix ? `${suffix}-${slug}` : slug);
221
- }
222
-
223
- function reconcileMilestonePhaseArtifactPaths(milestoneId: string, title: string): void {
224
- const db = getDbOrNull()!;
225
- const phaseNumPrefix = String(milestoneIdToPhaseNum(milestoneId)).padStart(2, "0");
226
- const canonicalDir = canonicalPhaseDirNameForDb(milestoneId, title);
227
- const staleRows = db.prepare(
228
- `SELECT path
229
- FROM artifacts
230
- WHERE milestone_id = :milestone_id
231
- AND path LIKE :phase_prefix`,
232
- ).all({
233
- ":milestone_id": milestoneId,
234
- ":phase_prefix": `${LAYOUT_SEGMENTS.level1}/${phaseNumPrefix}-%/%`,
235
- }) as Array<{ path: string }>;
236
-
237
- const updatePath = db.prepare("UPDATE artifacts SET path = :new_path WHERE path = :old_path");
238
- const deletePath = db.prepare("DELETE FROM artifacts WHERE path = :path");
239
- const existingPath = db.prepare("SELECT 1 AS present FROM artifacts WHERE path = :path");
240
-
241
- for (const row of staleRows) {
242
- const parts = row.path.split("/");
243
- if (parts.length < 3) continue;
244
- if (parts[0] !== LAYOUT_SEGMENTS.level1) continue;
245
- if (parts[1] === canonicalDir) continue;
246
- if (!parts[1]?.startsWith(`${phaseNumPrefix}-`)) continue;
247
-
248
- const newPath = [LAYOUT_SEGMENTS.level1, canonicalDir, ...parts.slice(2)].join("/");
249
- if (existingPath.get({ ":path": newPath })) {
250
- deletePath.run({ ":path": row.path });
251
- continue;
252
- }
253
- updatePath.run({ ":new_path": newPath, ":old_path": row.path });
254
- }
255
- }
256
-
257
209
  export interface MilestonePlanningRecord {
258
210
  vision: string;
259
211
  successCriteria: string[];
@@ -335,43 +287,39 @@ export function insertMilestone(m: {
335
287
 
336
288
  export function upsertMilestonePlanning(milestoneId: string, planning: Partial<MilestonePlanningRecord> & { title?: string; status?: string; depends_on?: string[] }): void {
337
289
  if (!getDbOrNull()!) throw new GSDError(GSD_STALE_STATE, "gsd-db: No database open");
338
- transaction(() => {
339
- getDbOrNull()!.prepare(
340
- `UPDATE milestones SET
341
- title = COALESCE(NULLIF(:title, ''), title),
342
- status = COALESCE(NULLIF(:status, ''), status),
343
- depends_on = COALESCE(:depends_on, depends_on),
344
- vision = COALESCE(:vision, vision),
345
- success_criteria = COALESCE(:success_criteria, success_criteria),
346
- key_risks = COALESCE(:key_risks, key_risks),
347
- proof_strategy = COALESCE(:proof_strategy, proof_strategy),
348
- verification_contract = COALESCE(:verification_contract, verification_contract),
349
- verification_integration = COALESCE(:verification_integration, verification_integration),
350
- verification_operational = COALESCE(:verification_operational, verification_operational),
351
- verification_uat = COALESCE(:verification_uat, verification_uat),
352
- definition_of_done = COALESCE(:definition_of_done, definition_of_done),
353
- requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
354
- boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
355
- WHERE id = :id`,
356
- ).run({
357
- ":id": milestoneId,
358
- ":title": planning.title ?? "",
359
- ":status": planning.status ?? "",
360
- ":depends_on": planning.depends_on ? JSON.stringify(planning.depends_on) : null,
361
- ":vision": planning.vision ?? null,
362
- ":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
363
- ":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
364
- ":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
365
- ":verification_contract": planning.verificationContract ?? null,
366
- ":verification_integration": planning.verificationIntegration ?? null,
367
- ":verification_operational": planning.verificationOperational ?? null,
368
- ":verification_uat": planning.verificationUat ?? null,
369
- ":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
370
- ":requirement_coverage": planning.requirementCoverage ?? null,
371
- ":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
372
- });
373
- const finalTitle = planning.title?.trim();
374
- if (finalTitle) reconcileMilestonePhaseArtifactPaths(milestoneId, finalTitle);
290
+ getDbOrNull()!.prepare(
291
+ `UPDATE milestones SET
292
+ title = COALESCE(NULLIF(:title, ''), title),
293
+ status = COALESCE(NULLIF(:status, ''), status),
294
+ depends_on = COALESCE(:depends_on, depends_on),
295
+ vision = COALESCE(:vision, vision),
296
+ success_criteria = COALESCE(:success_criteria, success_criteria),
297
+ key_risks = COALESCE(:key_risks, key_risks),
298
+ proof_strategy = COALESCE(:proof_strategy, proof_strategy),
299
+ verification_contract = COALESCE(:verification_contract, verification_contract),
300
+ verification_integration = COALESCE(:verification_integration, verification_integration),
301
+ verification_operational = COALESCE(:verification_operational, verification_operational),
302
+ verification_uat = COALESCE(:verification_uat, verification_uat),
303
+ definition_of_done = COALESCE(:definition_of_done, definition_of_done),
304
+ requirement_coverage = COALESCE(:requirement_coverage, requirement_coverage),
305
+ boundary_map_markdown = COALESCE(:boundary_map_markdown, boundary_map_markdown)
306
+ WHERE id = :id`,
307
+ ).run({
308
+ ":id": milestoneId,
309
+ ":title": planning.title ?? "",
310
+ ":status": planning.status ?? "",
311
+ ":depends_on": planning.depends_on ? JSON.stringify(planning.depends_on) : null,
312
+ ":vision": planning.vision ?? null,
313
+ ":success_criteria": planning.successCriteria ? JSON.stringify(planning.successCriteria) : null,
314
+ ":key_risks": planning.keyRisks ? JSON.stringify(planning.keyRisks) : null,
315
+ ":proof_strategy": planning.proofStrategy ? JSON.stringify(planning.proofStrategy) : null,
316
+ ":verification_contract": planning.verificationContract ?? null,
317
+ ":verification_integration": planning.verificationIntegration ?? null,
318
+ ":verification_operational": planning.verificationOperational ?? null,
319
+ ":verification_uat": planning.verificationUat ?? null,
320
+ ":definition_of_done": planning.definitionOfDone ? JSON.stringify(planning.definitionOfDone) : null,
321
+ ":requirement_coverage": planning.requirementCoverage ?? null,
322
+ ":boundary_map_markdown": planning.boundaryMapMarkdown ?? null,
375
323
  });
376
324
  }
377
325