gsd-pi 2.32.0-dev.d792ba5 → 2.32.0-dev.d9c9e0c

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 (84) hide show
  1. package/dist/resource-loader.js +13 -3
  2. package/dist/resources/extensions/gsd/auto-dashboard.ts +3 -1
  3. package/dist/resources/extensions/gsd/auto-dispatch.ts +40 -12
  4. package/dist/resources/extensions/gsd/auto-idempotency.ts +3 -2
  5. package/dist/resources/extensions/gsd/auto-observability.ts +2 -4
  6. package/dist/resources/extensions/gsd/auto-post-unit.ts +5 -5
  7. package/dist/resources/extensions/gsd/auto-prompts.ts +46 -44
  8. package/dist/resources/extensions/gsd/auto-recovery.ts +8 -22
  9. package/dist/resources/extensions/gsd/auto-start.ts +8 -6
  10. package/dist/resources/extensions/gsd/auto-stuck-detection.ts +3 -2
  11. package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +2 -1
  12. package/dist/resources/extensions/gsd/auto-timers.ts +3 -2
  13. package/dist/resources/extensions/gsd/auto-verification.ts +6 -6
  14. package/dist/resources/extensions/gsd/auto-worktree.ts +5 -4
  15. package/dist/resources/extensions/gsd/auto.ts +108 -182
  16. package/dist/resources/extensions/gsd/commands-inspect.ts +2 -1
  17. package/dist/resources/extensions/gsd/commands-workflow-templates.ts +2 -1
  18. package/dist/resources/extensions/gsd/complexity-classifier.ts +5 -7
  19. package/dist/resources/extensions/gsd/dispatch-guard.ts +2 -1
  20. package/dist/resources/extensions/gsd/error-utils.ts +6 -0
  21. package/dist/resources/extensions/gsd/export.ts +2 -1
  22. package/dist/resources/extensions/gsd/git-service.ts +3 -2
  23. package/dist/resources/extensions/gsd/guided-flow.ts +3 -2
  24. package/dist/resources/extensions/gsd/index.ts +6 -5
  25. package/dist/resources/extensions/gsd/key-manager.ts +2 -1
  26. package/dist/resources/extensions/gsd/marketplace-discovery.ts +4 -3
  27. package/dist/resources/extensions/gsd/metrics.ts +3 -3
  28. package/dist/resources/extensions/gsd/migrate-external.ts +3 -2
  29. package/dist/resources/extensions/gsd/milestone-ids.ts +2 -1
  30. package/dist/resources/extensions/gsd/native-git-bridge.ts +2 -1
  31. package/dist/resources/extensions/gsd/parallel-merge.ts +2 -1
  32. package/dist/resources/extensions/gsd/parallel-orchestrator.ts +2 -1
  33. package/dist/resources/extensions/gsd/post-unit-hooks.ts +8 -9
  34. package/dist/resources/extensions/gsd/quick.ts +2 -1
  35. package/dist/resources/extensions/gsd/session-lock.ts +12 -1
  36. package/dist/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +14 -11
  37. package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  38. package/dist/resources/extensions/gsd/tests/loop-regression.test.ts +839 -0
  39. package/dist/resources/extensions/gsd/undo.ts +5 -7
  40. package/dist/resources/extensions/gsd/unit-id.ts +14 -0
  41. package/dist/resources/extensions/gsd/unit-runtime.ts +2 -1
  42. package/dist/resources/extensions/gsd/worktree-command.ts +8 -7
  43. package/package.json +1 -1
  44. package/src/resources/extensions/gsd/auto-dashboard.ts +3 -1
  45. package/src/resources/extensions/gsd/auto-dispatch.ts +40 -12
  46. package/src/resources/extensions/gsd/auto-idempotency.ts +3 -2
  47. package/src/resources/extensions/gsd/auto-observability.ts +2 -4
  48. package/src/resources/extensions/gsd/auto-post-unit.ts +5 -5
  49. package/src/resources/extensions/gsd/auto-prompts.ts +46 -44
  50. package/src/resources/extensions/gsd/auto-recovery.ts +8 -22
  51. package/src/resources/extensions/gsd/auto-start.ts +8 -6
  52. package/src/resources/extensions/gsd/auto-stuck-detection.ts +3 -2
  53. package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -1
  54. package/src/resources/extensions/gsd/auto-timers.ts +3 -2
  55. package/src/resources/extensions/gsd/auto-verification.ts +6 -6
  56. package/src/resources/extensions/gsd/auto-worktree.ts +5 -4
  57. package/src/resources/extensions/gsd/auto.ts +108 -182
  58. package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
  59. package/src/resources/extensions/gsd/commands-workflow-templates.ts +2 -1
  60. package/src/resources/extensions/gsd/complexity-classifier.ts +5 -7
  61. package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
  62. package/src/resources/extensions/gsd/error-utils.ts +6 -0
  63. package/src/resources/extensions/gsd/export.ts +2 -1
  64. package/src/resources/extensions/gsd/git-service.ts +3 -2
  65. package/src/resources/extensions/gsd/guided-flow.ts +3 -2
  66. package/src/resources/extensions/gsd/index.ts +6 -5
  67. package/src/resources/extensions/gsd/key-manager.ts +2 -1
  68. package/src/resources/extensions/gsd/marketplace-discovery.ts +4 -3
  69. package/src/resources/extensions/gsd/metrics.ts +3 -3
  70. package/src/resources/extensions/gsd/migrate-external.ts +3 -2
  71. package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
  72. package/src/resources/extensions/gsd/native-git-bridge.ts +2 -1
  73. package/src/resources/extensions/gsd/parallel-merge.ts +2 -1
  74. package/src/resources/extensions/gsd/parallel-orchestrator.ts +2 -1
  75. package/src/resources/extensions/gsd/post-unit-hooks.ts +8 -9
  76. package/src/resources/extensions/gsd/quick.ts +2 -1
  77. package/src/resources/extensions/gsd/session-lock.ts +12 -1
  78. package/src/resources/extensions/gsd/tests/all-milestones-complete-merge.test.ts +14 -11
  79. package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
  80. package/src/resources/extensions/gsd/tests/loop-regression.test.ts +839 -0
  81. package/src/resources/extensions/gsd/undo.ts +5 -7
  82. package/src/resources/extensions/gsd/unit-id.ts +14 -0
  83. package/src/resources/extensions/gsd/unit-runtime.ts +2 -1
  84. package/src/resources/extensions/gsd/worktree-command.ts +8 -7
@@ -64,6 +64,7 @@ import { pauseAutoForProviderError, classifyProviderError } from "./provider-err
64
64
  import { toPosixPath } from "../shared/mod.js";
65
65
  import { isParallelActive, shutdownParallel } from "./parallel-orchestrator.js";
66
66
  import { DEFAULT_BASH_TIMEOUT_SECS } from "./constants.js";
67
+ import { getErrorMessage } from "./error-utils.js";
67
68
 
68
69
  /**
69
70
  * Ensure the GSD database is available, auto-initializing if needed.
@@ -374,7 +375,7 @@ export default function (pi: ExtensionAPI) {
374
375
  details: { operation: "save_decision", id },
375
376
  };
376
377
  } catch (err) {
377
- const msg = err instanceof Error ? err.message : String(err);
378
+ const msg = getErrorMessage(err);
378
379
  process.stderr.write(`gsd-db: gsd_save_decision tool failed: ${msg}\n`);
379
380
  return {
380
381
  content: [{ type: "text" as const, text: `Error saving decision: ${msg}` }],
@@ -445,7 +446,7 @@ export default function (pi: ExtensionAPI) {
445
446
  details: { operation: "update_requirement", id: params.id },
446
447
  };
447
448
  } catch (err) {
448
- const msg = err instanceof Error ? err.message : String(err);
449
+ const msg = getErrorMessage(err);
449
450
  process.stderr.write(`gsd-db: gsd_update_requirement tool failed: ${msg}\n`);
450
451
  return {
451
452
  content: [{ type: "text" as const, text: `Error updating requirement: ${msg}` }],
@@ -525,7 +526,7 @@ export default function (pi: ExtensionAPI) {
525
526
  details: { operation: "save_summary", path: relativePath, artifact_type: params.artifact_type },
526
527
  };
527
528
  } catch (err) {
528
- const msg = err instanceof Error ? err.message : String(err);
529
+ const msg = getErrorMessage(err);
529
530
  process.stderr.write(`gsd-db: gsd_save_summary tool failed: ${msg}\n`);
530
531
  return {
531
532
  content: [{ type: "text" as const, text: `Error saving artifact: ${msg}` }],
@@ -574,7 +575,7 @@ export default function (pi: ExtensionAPI) {
574
575
  details: { operation: "generate_milestone_id", id: newId, existingCount: existingIds.length, reservedCount: reservedMilestoneIds.size, uniqueEnabled },
575
576
  };
576
577
  } catch (err) {
577
- const msg = err instanceof Error ? err.message : String(err);
578
+ const msg = getErrorMessage(err);
578
579
  return {
579
580
  content: [{ type: "text" as const, text: `Error generating milestone ID: ${msg}` }],
580
581
  isError: true,
@@ -993,7 +994,7 @@ export default function (pi: ExtensionAPI) {
993
994
  } catch (err) {
994
995
  // Safety net: if handleAgentEnd throws despite its internal try-catch,
995
996
  // ensure auto-mode stops gracefully instead of silently stalling (#381).
996
- const message = err instanceof Error ? err.message : String(err);
997
+ const message = getErrorMessage(err);
997
998
  ctx.ui.notify(
998
999
  `Auto-mode error in agent_end handler: ${message}. Stopping auto-mode.`,
999
1000
  "error",
@@ -16,6 +16,7 @@ import { getEnvApiKey } from "@gsd/pi-ai";
16
16
  import { existsSync, statSync, chmodSync } from "node:fs";
17
17
  import { join, dirname } from "node:path";
18
18
  import { mkdirSync } from "node:fs";
19
+ import { getErrorMessage } from "./error-utils.js";
19
20
 
20
21
  // ─── Provider Registry ─────────────────────────────────────────────────────────
21
22
 
@@ -552,7 +553,7 @@ export async function testProviderKey(
552
553
  return { provider, status: "error", message: `HTTP ${res.status}`, latencyMs };
553
554
  } catch (err) {
554
555
  const latencyMs = Date.now() - start;
555
- const msg = err instanceof Error ? err.message : String(err);
556
+ const msg = getErrorMessage(err);
556
557
  if (msg.includes("timeout") || msg.includes("AbortError")) {
557
558
  return { provider, status: "error", message: "timeout (15s)", latencyMs };
558
559
  }
@@ -16,6 +16,7 @@
16
16
 
17
17
  import * as fs from 'node:fs';
18
18
  import * as path from 'node:path';
19
+ import { getErrorMessage } from "./error-utils.js";
19
20
 
20
21
  // ============================================================================
21
22
  // Type Definitions
@@ -194,7 +195,7 @@ export function parseMarketplaceJson(repoRoot: string):
194
195
  } catch (err) {
195
196
  return {
196
197
  success: false,
197
- error: `Failed to read marketplace.json: ${err instanceof Error ? err.message : String(err)}`
198
+ error: `Failed to read marketplace.json: ${getErrorMessage(err)}`
198
199
  };
199
200
  }
200
201
 
@@ -204,7 +205,7 @@ export function parseMarketplaceJson(repoRoot: string):
204
205
  } catch (err) {
205
206
  return {
206
207
  success: false,
207
- error: `Failed to parse marketplace.json: ${err instanceof Error ? err.message : String(err)}`
208
+ error: `Failed to parse marketplace.json: ${getErrorMessage(err)}`
208
209
  };
209
210
  }
210
211
 
@@ -293,7 +294,7 @@ export function inspectPlugin(
293
294
  }
294
295
  } catch (err) {
295
296
  // Fall back to marketplace inline or derived
296
- result.error = `Failed to parse plugin.json: ${err instanceof Error ? err.message : String(err)}`;
297
+ result.error = `Failed to parse plugin.json: ${getErrorMessage(err)}`;
297
298
  }
298
299
  }
299
300
 
@@ -18,6 +18,7 @@ import type { ExtensionContext } from "@gsd/pi-coding-agent";
18
18
  import { gsdRoot } from "./paths.js";
19
19
  import { getAndClearSkills } from "./skill-telemetry.js";
20
20
  import { loadJsonFile, loadJsonFileOrNull, saveJsonFile } from "./json-persistence.js";
21
+ import { parseUnitId } from "./unit-id.js";
21
22
 
22
23
  // Re-export from shared — canonical implementation lives in format-utils.
23
24
  export { formatTokenCount } from "../shared/mod.js";
@@ -290,9 +291,8 @@ export function aggregateByPhase(units: UnitMetrics[]): PhaseAggregate[] {
290
291
  export function aggregateBySlice(units: UnitMetrics[]): SliceAggregate[] {
291
292
  const map = new Map<string, SliceAggregate>();
292
293
  for (const u of units) {
293
- const parts = u.id.split("/");
294
- // Slice ID is parts[0]/parts[1] if it exists, else parts[0]
295
- const sliceId = parts.length >= 2 ? `${parts[0]}/${parts[1]}` : parts[0];
294
+ const { milestone, slice } = parseUnitId(u.id);
295
+ const sliceId = slice ? `${milestone}/${slice}` : milestone;
296
296
  let agg = map.get(sliceId);
297
297
  if (!agg) {
298
298
  agg = { sliceId, units: 0, tokens: emptyTokens(), cost: 0, duration: 0 };
@@ -9,6 +9,7 @@
9
9
  import { existsSync, lstatSync, mkdirSync, readdirSync, renameSync, cpSync, rmSync, symlinkSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
  import { externalGsdRoot } from "./repo-identity.js";
12
+ import { getErrorMessage } from "./error-utils.js";
12
13
 
13
14
  export interface MigrationResult {
14
15
  migrated: boolean;
@@ -47,7 +48,7 @@ export function migrateToExternalState(basePath: string): MigrationResult {
47
48
  return { migrated: false, error: ".gsd exists but is not a directory or symlink" };
48
49
  }
49
50
  } catch (err) {
50
- return { migrated: false, error: `Cannot stat .gsd: ${err instanceof Error ? err.message : String(err)}` };
51
+ return { migrated: false, error: `Cannot stat .gsd: ${getErrorMessage(err)}` };
51
52
  }
52
53
 
53
54
  const externalPath = externalGsdRoot(basePath);
@@ -114,7 +115,7 @@ export function migrateToExternalState(basePath: string): MigrationResult {
114
115
 
115
116
  return {
116
117
  migrated: false,
117
- error: `Migration failed: ${err instanceof Error ? err.message : String(err)}`,
118
+ error: `Migration failed: ${getErrorMessage(err)}`,
118
119
  };
119
120
  }
120
121
  }
@@ -9,6 +9,7 @@ import { randomInt } from "node:crypto";
9
9
  import { readdirSync, existsSync } from "node:fs";
10
10
  import { milestonesDir } from "./paths.js";
11
11
  import { loadQueueOrder, sortByQueueOrder } from "./queue-order.js";
12
+ import { getErrorMessage } from "./error-utils.js";
12
13
 
13
14
  // ─── Regex ──────────────────────────────────────────────────────────────────
14
15
 
@@ -88,7 +89,7 @@ export function findMilestoneIds(basePath: string): string[] {
88
89
  } catch (err) {
89
90
  // Log why milestone scanning failed — silent [] here causes infinite loops (#456)
90
91
  if (existsSync(dir)) {
91
- console.error(`[gsd] findMilestoneIds: .gsd/milestones/ exists but readdirSync failed — ${err instanceof Error ? err.message : String(err)}`);
92
+ console.error(`[gsd] findMilestoneIds: .gsd/milestones/ exists but readdirSync failed — ${getErrorMessage(err)}`);
92
93
  }
93
94
  return [];
94
95
  }
@@ -10,6 +10,7 @@ import { existsSync, readFileSync, unlinkSync, rmSync } from "node:fs";
10
10
  import { join } from "node:path";
11
11
  import { GSDError, GSD_GIT_ERROR } from "./errors.js";
12
12
  import { GIT_NO_PROMPT_ENV } from "./git-constants.js";
13
+ import { getErrorMessage } from "./error-utils.js";
13
14
 
14
15
  // Issue #453: keep auto-mode bookkeeping on the stable git CLI path unless a
15
16
  // caller explicitly opts into the native helper.
@@ -716,7 +717,7 @@ export function nativeCommit(
716
717
  try {
717
718
  return native.gitCommit(basePath, message, options?.allowEmpty);
718
719
  } catch (e) {
719
- const msg = e instanceof Error ? e.message : String(e);
720
+ const msg = getErrorMessage(e);
720
721
  if (msg.includes("nothing to commit")) return null;
721
722
  throw e;
722
723
  }
@@ -11,6 +11,7 @@ import { mergeMilestoneToMain } from "./auto-worktree.js";
11
11
  import { MergeConflictError } from "./git-service.js";
12
12
  import { removeSessionStatus } from "./session-status-io.js";
13
13
  import type { WorkerInfo } from "./parallel-orchestrator.js";
14
+ import { getErrorMessage } from "./error-utils.js";
14
15
 
15
16
  // ─── Types ─────────────────────────────────────────────────────────────────
16
17
 
@@ -99,7 +100,7 @@ export async function mergeCompletedMilestone(
99
100
  return {
100
101
  milestoneId,
101
102
  success: false,
102
- error: err instanceof Error ? err.message : String(err),
103
+ error: getErrorMessage(err),
103
104
  };
104
105
  }
105
106
  }
@@ -38,6 +38,7 @@ import {
38
38
  analyzeParallelEligibility,
39
39
  type ParallelCandidates,
40
40
  } from "./parallel-eligibility.js";
41
+ import { getErrorMessage } from "./error-utils.js";
41
42
 
42
43
  // ─── Types ─────────────────────────────────────────────────────────────────
43
44
 
@@ -363,7 +364,7 @@ export async function startParallel(
363
364
 
364
365
  started.push(mid);
365
366
  } catch (err) {
366
- const message = err instanceof Error ? err.message : String(err);
367
+ const message = getErrorMessage(err);
367
368
  errors.push({ mid, error: message });
368
369
  }
369
370
  }
@@ -15,6 +15,7 @@ import { resolvePostUnitHooks, resolvePreDispatchHooks } from "./preferences.js"
15
15
  import { existsSync, readFileSync, writeFileSync, mkdirSync } from "node:fs";
16
16
  import { join } from "node:path";
17
17
  import { gsdRoot } from "./paths.js";
18
+ import { parseUnitId } from "./unit-id.js";
18
19
 
19
20
  // ─── Hook Queue State ──────────────────────────────────────────────────────
20
21
 
@@ -149,7 +150,7 @@ function dequeueNextHook(basePath: string): HookDispatchResult | null {
149
150
  };
150
151
 
151
152
  // Build the prompt with variable substitution
152
- const [mid, sid, tid] = triggerUnitId.split("/");
153
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(triggerUnitId);
153
154
  const prompt = config.prompt
154
155
  .replace(/\{milestoneId\}/g, mid ?? "")
155
156
  .replace(/\{sliceId\}/g, sid ?? "")
@@ -208,16 +209,14 @@ function handleHookCompletion(basePath: string): HookDispatchResult | null {
208
209
  * - Milestone-level (M001): .gsd/M001/{artifact}
209
210
  */
210
211
  export function resolveHookArtifactPath(basePath: string, unitId: string, artifactName: string): string {
211
- const parts = unitId.split("/");
212
- if (parts.length === 3) {
213
- const [mid, sid, tid] = parts;
212
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
213
+ if (mid && sid && tid) {
214
214
  return join(gsdRoot(basePath), mid, "slices", sid, "tasks", `${tid}-${artifactName}`);
215
215
  }
216
- if (parts.length === 2) {
217
- const [mid, sid] = parts;
216
+ if (mid && sid) {
218
217
  return join(gsdRoot(basePath), mid, "slices", sid, artifactName);
219
218
  }
220
- return join(gsdRoot(basePath), parts[0], artifactName);
219
+ return join(gsdRoot(basePath), mid, artifactName);
221
220
  }
222
221
 
223
222
  // ═══════════════════════════════════════════════════════════════════════════
@@ -253,7 +252,7 @@ export function runPreDispatchHooks(
253
252
  return { action: "proceed", prompt, firedHooks: [] };
254
253
  }
255
254
 
256
- const [mid, sid, tid] = unitId.split("/");
255
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
257
256
  const substitute = (text: string): string =>
258
257
  text
259
258
  .replace(/\{milestoneId\}/g, mid ?? "")
@@ -466,7 +465,7 @@ export function triggerHookManually(
466
465
  activeHook.cycle = currentCycle;
467
466
 
468
467
  // Build the prompt with variable substitution
469
- const [mid, sid, tid] = unitId.split("/");
468
+ const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
470
469
  const prompt = hook.prompt
471
470
  .replace(/\{milestoneId\}/g, mid ?? "")
472
471
  .replace(/\{sliceId\}/g, sid ?? "")
@@ -15,6 +15,7 @@ import { join } from "node:path";
15
15
  import { loadPrompt } from "./prompt-loader.js";
16
16
  import { gsdRoot } from "./paths.js";
17
17
  import { createGitService, runGit } from "./git-service.js";
18
+ import { getErrorMessage } from "./error-utils.js";
18
19
 
19
20
  // ─── Quick Task Helpers ───────────────────────────────────────────────────────
20
21
 
@@ -122,7 +123,7 @@ export async function handleQuick(
122
123
  }
123
124
  } catch (err) {
124
125
  // Branch creation failed — continue on current branch
125
- const message = err instanceof Error ? err.message : String(err);
126
+ const message = getErrorMessage(err);
126
127
  ctx.ui.notify(`Could not create branch ${branchName}: ${message}. Working on current branch.`, "warning");
127
128
  }
128
129
  }
@@ -154,12 +154,23 @@ export function acquireSessionLock(basePath: string): SessionLockResult {
154
154
  // Retry acquisition after cleanup
155
155
  const release = lockfile.lockSync(gsdDir, {
156
156
  realpath: false,
157
- stale: 300_000,
157
+ stale: 1_800_000, // 30 minutes — match primary lock settings
158
158
  update: 10_000,
159
+ onCompromised: () => {
160
+ _lockCompromised = true;
161
+ },
159
162
  });
160
163
  _releaseFunction = release;
161
164
  _lockedPath = basePath;
162
165
  _lockPid = process.pid;
166
+
167
+ // Safety net for retry path too
168
+ const retryLockDir = join(gsdDir + ".lock");
169
+ process.once("exit", () => {
170
+ try { if (_releaseFunction) { _releaseFunction(); _releaseFunction = null; } } catch {}
171
+ try { if (existsSync(retryLockDir)) rmSync(retryLockDir, { recursive: true, force: true }); } catch {}
172
+ });
173
+
163
174
  atomicWriteSync(lp, JSON.stringify(lockData, null, 2));
164
175
  return { acquired: true };
165
176
  } catch {
@@ -70,31 +70,34 @@ test("auto.ts 'all milestones complete' path merges before stopping (#962)", ()
70
70
  const incompleteIdx = autoSrc.indexOf("incomplete.length === 0");
71
71
  assert.ok(incompleteIdx > -1, "auto.ts should have 'incomplete.length === 0' check");
72
72
 
73
- // The merge call must appear BETWEEN the incomplete check and the stopAuto call
74
- // in that same block
73
+ // The merge call must appear BETWEEN the incomplete check and the stopAuto call.
74
+ // After the #1308 refactor, the merge is delegated to tryMergeMilestone.
75
75
  const blockAfterIncomplete = autoSrc.slice(incompleteIdx, incompleteIdx + 3000);
76
76
 
77
77
  assert.ok(
78
- blockAfterIncomplete.includes("mergeMilestoneToMain"),
79
- "auto.ts should call mergeMilestoneToMain in the 'all milestones complete' path",
78
+ blockAfterIncomplete.includes("tryMergeMilestone"),
79
+ "auto.ts should call tryMergeMilestone in the 'all milestones complete' path",
80
80
  );
81
81
 
82
82
  // The merge should come before stopAuto in this block
83
- const mergePos = blockAfterIncomplete.indexOf("mergeMilestoneToMain");
83
+ const mergePos = blockAfterIncomplete.indexOf("tryMergeMilestone");
84
84
  const stopPos = blockAfterIncomplete.indexOf("stopAuto");
85
85
  assert.ok(
86
86
  mergePos < stopPos,
87
- "mergeMilestoneToMain should be called before stopAuto in the 'all complete' path",
87
+ "tryMergeMilestone should be called before stopAuto in the 'all complete' path",
88
88
  );
89
89
 
90
- // Should handle both worktree and branch isolation modes
90
+ // Verify tryMergeMilestone handles both worktree and branch isolation
91
+ const helperIdx = autoSrc.indexOf("function tryMergeMilestone");
92
+ assert.ok(helperIdx > -1, "tryMergeMilestone helper should exist");
93
+ const helperBlock = autoSrc.slice(helperIdx, helperIdx + 2000);
91
94
  assert.ok(
92
- blockAfterIncomplete.includes("isInAutoWorktree"),
93
- "should check isInAutoWorktree for worktree mode",
95
+ helperBlock.includes("isInAutoWorktree"),
96
+ "tryMergeMilestone should check isInAutoWorktree for worktree mode",
94
97
  );
95
98
  assert.ok(
96
- blockAfterIncomplete.includes("getIsolationMode"),
97
- "should check getIsolationMode for branch isolation mode",
99
+ helperBlock.includes("getIsolationMode") || helperBlock.includes("isolationMode"),
100
+ "tryMergeMilestone should check isolation mode for branch mode",
98
101
  );
99
102
  });
100
103
 
@@ -91,7 +91,7 @@ test("compression: buildPlanMilestonePrompt minimal drops project/requirements/d
91
91
  // The plan-milestone builder should gate root file inlining on inlineLevel
92
92
  assert.ok(
93
93
  promptsSrc.includes('inlineLevel !== "minimal"') &&
94
- promptsSrc.includes('inlineGsdRootFile(base, "project.md"'),
94
+ promptsSrc.includes("inlineProjectFromDb(base)"),
95
95
  "plan-milestone should conditionally include project.md based on level",
96
96
  );
97
97
  });