gsd-pi 2.31.2 → 2.32.0-dev.3d7932c
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.
- package/README.md +27 -20
- package/dist/cli.js +5 -5
- package/dist/resource-loader.js +13 -3
- package/dist/resources/extensions/gsd/auto-constants.ts +6 -0
- package/dist/resources/extensions/gsd/auto-dashboard.ts +23 -27
- package/dist/resources/extensions/gsd/auto-direct-dispatch.ts +1 -6
- package/dist/resources/extensions/gsd/auto-dispatch.ts +4 -8
- package/dist/resources/extensions/gsd/auto-idempotency.ts +3 -2
- package/dist/resources/extensions/gsd/auto-observability.ts +2 -4
- package/dist/resources/extensions/gsd/auto-post-unit.ts +32 -37
- package/dist/resources/extensions/gsd/auto-prompts.ts +84 -78
- package/dist/resources/extensions/gsd/auto-recovery.ts +8 -22
- package/dist/resources/extensions/gsd/auto-start.ts +16 -12
- package/dist/resources/extensions/gsd/auto-stuck-detection.ts +3 -2
- package/dist/resources/extensions/gsd/auto-timeout-recovery.ts +2 -1
- package/dist/resources/extensions/gsd/auto-timers.ts +3 -2
- package/dist/resources/extensions/gsd/auto-verification.ts +6 -6
- package/dist/resources/extensions/gsd/auto-worktree.ts +5 -4
- package/dist/resources/extensions/gsd/auto.ts +82 -60
- package/dist/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/dist/resources/extensions/gsd/commands-workflow-templates.ts +5 -6
- package/dist/resources/extensions/gsd/commands.ts +19 -0
- package/dist/resources/extensions/gsd/complexity-classifier.ts +5 -7
- package/dist/resources/extensions/gsd/crash-recovery.ts +15 -2
- package/dist/resources/extensions/gsd/dashboard-overlay.ts +28 -0
- package/dist/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/dist/resources/extensions/gsd/doctor-environment.ts +497 -0
- package/dist/resources/extensions/gsd/doctor-providers.ts +343 -0
- package/dist/resources/extensions/gsd/doctor-types.ts +14 -1
- package/dist/resources/extensions/gsd/doctor.ts +6 -0
- package/dist/resources/extensions/gsd/error-utils.ts +6 -0
- package/dist/resources/extensions/gsd/export.ts +2 -1
- package/dist/resources/extensions/gsd/git-service.ts +12 -2
- package/dist/resources/extensions/gsd/guided-flow-queue.ts +1 -8
- package/dist/resources/extensions/gsd/guided-flow.ts +3 -2
- package/dist/resources/extensions/gsd/health-widget.ts +167 -0
- package/dist/resources/extensions/gsd/index.ts +18 -5
- package/dist/resources/extensions/gsd/key-manager.ts +2 -1
- package/dist/resources/extensions/gsd/marketplace-discovery.ts +4 -3
- package/dist/resources/extensions/gsd/metrics.ts +3 -3
- package/dist/resources/extensions/gsd/migrate-external.ts +21 -4
- package/dist/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/dist/resources/extensions/gsd/native-git-bridge.ts +2 -1
- package/dist/resources/extensions/gsd/parallel-merge.ts +2 -1
- package/dist/resources/extensions/gsd/parallel-orchestrator.ts +2 -1
- package/dist/resources/extensions/gsd/post-unit-hooks.ts +8 -9
- package/dist/resources/extensions/gsd/preferences-types.ts +8 -0
- package/dist/resources/extensions/gsd/preferences-validation.ts +3 -10
- package/dist/resources/extensions/gsd/progress-score.ts +273 -0
- package/dist/resources/extensions/gsd/prompts/run-uat.md +1 -42
- package/dist/resources/extensions/gsd/quick.ts +61 -8
- package/dist/resources/extensions/gsd/repo-identity.ts +22 -1
- package/dist/resources/extensions/gsd/session-lock.ts +12 -1
- package/dist/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +127 -0
- package/dist/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/dist/resources/extensions/gsd/tests/doctor-environment.test.ts +314 -0
- package/dist/resources/extensions/gsd/tests/doctor-providers.test.ts +298 -0
- package/dist/resources/extensions/gsd/tests/export-html-enhancements.test.ts +3 -0
- package/dist/resources/extensions/gsd/tests/memory-leak-guards.test.ts +7 -3
- package/dist/resources/extensions/gsd/tests/progress-score.test.ts +206 -0
- package/dist/resources/extensions/gsd/tests/run-uat.test.ts +56 -7
- package/dist/resources/extensions/gsd/tests/visualizer-views.test.ts +12 -0
- package/dist/resources/extensions/gsd/undo.ts +5 -7
- package/dist/resources/extensions/gsd/unit-id.ts +14 -0
- package/dist/resources/extensions/gsd/unit-runtime.ts +2 -1
- package/dist/resources/extensions/gsd/visualizer-data.ts +60 -2
- package/dist/resources/extensions/gsd/visualizer-views.ts +54 -0
- package/dist/resources/extensions/gsd/worktree-command.ts +8 -7
- package/dist/worktree-cli.d.ts +42 -6
- package/dist/worktree-cli.js +88 -48
- package/package.json +1 -1
- package/packages/pi-coding-agent/package.json +1 -1
- package/pkg/package.json +1 -1
- package/src/resources/extensions/gsd/auto-constants.ts +6 -0
- package/src/resources/extensions/gsd/auto-dashboard.ts +23 -27
- package/src/resources/extensions/gsd/auto-direct-dispatch.ts +1 -6
- package/src/resources/extensions/gsd/auto-dispatch.ts +4 -8
- package/src/resources/extensions/gsd/auto-idempotency.ts +3 -2
- package/src/resources/extensions/gsd/auto-observability.ts +2 -4
- package/src/resources/extensions/gsd/auto-post-unit.ts +32 -37
- package/src/resources/extensions/gsd/auto-prompts.ts +84 -78
- package/src/resources/extensions/gsd/auto-recovery.ts +8 -22
- package/src/resources/extensions/gsd/auto-start.ts +16 -12
- package/src/resources/extensions/gsd/auto-stuck-detection.ts +3 -2
- package/src/resources/extensions/gsd/auto-timeout-recovery.ts +2 -1
- package/src/resources/extensions/gsd/auto-timers.ts +3 -2
- package/src/resources/extensions/gsd/auto-verification.ts +6 -6
- package/src/resources/extensions/gsd/auto-worktree.ts +5 -4
- package/src/resources/extensions/gsd/auto.ts +82 -60
- package/src/resources/extensions/gsd/commands-inspect.ts +2 -1
- package/src/resources/extensions/gsd/commands-workflow-templates.ts +5 -6
- package/src/resources/extensions/gsd/commands.ts +19 -0
- package/src/resources/extensions/gsd/complexity-classifier.ts +5 -7
- package/src/resources/extensions/gsd/crash-recovery.ts +15 -2
- package/src/resources/extensions/gsd/dashboard-overlay.ts +28 -0
- package/src/resources/extensions/gsd/dispatch-guard.ts +2 -1
- package/src/resources/extensions/gsd/doctor-environment.ts +497 -0
- package/src/resources/extensions/gsd/doctor-providers.ts +343 -0
- package/src/resources/extensions/gsd/doctor-types.ts +14 -1
- package/src/resources/extensions/gsd/doctor.ts +6 -0
- package/src/resources/extensions/gsd/error-utils.ts +6 -0
- package/src/resources/extensions/gsd/export.ts +2 -1
- package/src/resources/extensions/gsd/git-service.ts +12 -2
- package/src/resources/extensions/gsd/guided-flow-queue.ts +1 -8
- package/src/resources/extensions/gsd/guided-flow.ts +3 -2
- package/src/resources/extensions/gsd/health-widget.ts +167 -0
- package/src/resources/extensions/gsd/index.ts +18 -5
- package/src/resources/extensions/gsd/key-manager.ts +2 -1
- package/src/resources/extensions/gsd/marketplace-discovery.ts +4 -3
- package/src/resources/extensions/gsd/metrics.ts +3 -3
- package/src/resources/extensions/gsd/migrate-external.ts +21 -4
- package/src/resources/extensions/gsd/milestone-ids.ts +2 -1
- package/src/resources/extensions/gsd/native-git-bridge.ts +2 -1
- package/src/resources/extensions/gsd/parallel-merge.ts +2 -1
- package/src/resources/extensions/gsd/parallel-orchestrator.ts +2 -1
- package/src/resources/extensions/gsd/post-unit-hooks.ts +8 -9
- package/src/resources/extensions/gsd/preferences-types.ts +8 -0
- package/src/resources/extensions/gsd/preferences-validation.ts +3 -10
- package/src/resources/extensions/gsd/progress-score.ts +273 -0
- package/src/resources/extensions/gsd/prompts/run-uat.md +1 -42
- package/src/resources/extensions/gsd/quick.ts +61 -8
- package/src/resources/extensions/gsd/repo-identity.ts +22 -1
- package/src/resources/extensions/gsd/session-lock.ts +12 -1
- package/src/resources/extensions/gsd/tests/auto-reentrancy-guard.test.ts +127 -0
- package/src/resources/extensions/gsd/tests/context-compression.test.ts +1 -1
- package/src/resources/extensions/gsd/tests/doctor-environment.test.ts +314 -0
- package/src/resources/extensions/gsd/tests/doctor-providers.test.ts +298 -0
- package/src/resources/extensions/gsd/tests/export-html-enhancements.test.ts +3 -0
- package/src/resources/extensions/gsd/tests/memory-leak-guards.test.ts +7 -3
- package/src/resources/extensions/gsd/tests/progress-score.test.ts +206 -0
- package/src/resources/extensions/gsd/tests/run-uat.test.ts +56 -7
- package/src/resources/extensions/gsd/tests/visualizer-views.test.ts +12 -0
- package/src/resources/extensions/gsd/undo.ts +5 -7
- package/src/resources/extensions/gsd/unit-id.ts +14 -0
- package/src/resources/extensions/gsd/unit-runtime.ts +2 -1
- package/src/resources/extensions/gsd/visualizer-data.ts +60 -2
- package/src/resources/extensions/gsd/visualizer-views.ts +54 -0
- package/src/resources/extensions/gsd/worktree-command.ts +8 -7
|
@@ -189,30 +189,52 @@ export async function inlineGsdRootFile(
|
|
|
189
189
|
// ─── DB-Aware Inline Helpers ──────────────────────────────────────────────
|
|
190
190
|
|
|
191
191
|
/**
|
|
192
|
-
*
|
|
193
|
-
*
|
|
192
|
+
* Shared DB-fallback pattern: attempt a DB query via the context-store, format
|
|
193
|
+
* the result, and fall back to the filesystem file when the DB is unavailable
|
|
194
|
+
* or the query yields no results.
|
|
195
|
+
*
|
|
196
|
+
* @param base Project root for filesystem fallback
|
|
197
|
+
* @param label Section heading (e.g. "Decisions")
|
|
198
|
+
* @param filename Filesystem fallback file (e.g. "decisions.md")
|
|
199
|
+
* @param queryDb Async callback receiving the dynamically-imported
|
|
200
|
+
* context-store module. Returns formatted markdown or null.
|
|
194
201
|
*/
|
|
195
|
-
|
|
196
|
-
base: string,
|
|
202
|
+
async function inlineFromDbOrFile(
|
|
203
|
+
base: string,
|
|
204
|
+
label: string,
|
|
205
|
+
filename: string,
|
|
206
|
+
queryDb: (cs: typeof import("./context-store.js")) => string | null,
|
|
197
207
|
): Promise<string | null> {
|
|
198
|
-
const inlineLevel = level ?? resolveInlineLevel();
|
|
199
208
|
try {
|
|
200
209
|
const { isDbAvailable } = await import("./gsd-db.js");
|
|
201
210
|
if (isDbAvailable()) {
|
|
202
|
-
const
|
|
203
|
-
const
|
|
204
|
-
if (
|
|
205
|
-
|
|
206
|
-
const formatted = inlineLevel !== "full"
|
|
207
|
-
? formatDecisionsCompact(decisions)
|
|
208
|
-
: formatDecisionsForPrompt(decisions);
|
|
209
|
-
return `### Decisions\nSource: \`.gsd/DECISIONS.md\`\n\n${formatted}`;
|
|
211
|
+
const contextStore = await import("./context-store.js");
|
|
212
|
+
const content = queryDb(contextStore);
|
|
213
|
+
if (content) {
|
|
214
|
+
return `### ${label}\nSource: \`.gsd/${filename.toUpperCase().replace(/\.MD$/i, "")}.md\`\n\n${content}`;
|
|
210
215
|
}
|
|
211
216
|
}
|
|
212
217
|
} catch {
|
|
213
218
|
// DB not available — fall through to filesystem
|
|
214
219
|
}
|
|
215
|
-
return inlineGsdRootFile(base,
|
|
220
|
+
return inlineGsdRootFile(base, filename, label);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
/**
|
|
224
|
+
* Inline decisions with optional milestone scoping from the DB.
|
|
225
|
+
* Falls back to filesystem via inlineGsdRootFile when DB unavailable or empty.
|
|
226
|
+
*/
|
|
227
|
+
export async function inlineDecisionsFromDb(
|
|
228
|
+
base: string, milestoneId?: string, scope?: string, level?: InlineLevel,
|
|
229
|
+
): Promise<string | null> {
|
|
230
|
+
const inlineLevel = level ?? resolveInlineLevel();
|
|
231
|
+
return inlineFromDbOrFile(base, "Decisions", "decisions.md", (cs) => {
|
|
232
|
+
const decisions = cs.queryDecisions({ milestoneId, scope });
|
|
233
|
+
if (decisions.length === 0) return null;
|
|
234
|
+
return inlineLevel !== "full"
|
|
235
|
+
? formatDecisionsCompact(decisions)
|
|
236
|
+
: cs.formatDecisionsForPrompt(decisions);
|
|
237
|
+
});
|
|
216
238
|
}
|
|
217
239
|
|
|
218
240
|
/**
|
|
@@ -223,23 +245,13 @@ export async function inlineRequirementsFromDb(
|
|
|
223
245
|
base: string, sliceId?: string, level?: InlineLevel,
|
|
224
246
|
): Promise<string | null> {
|
|
225
247
|
const inlineLevel = level ?? resolveInlineLevel();
|
|
226
|
-
|
|
227
|
-
const {
|
|
228
|
-
if (
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
const formatted = inlineLevel !== "full"
|
|
234
|
-
? formatRequirementsCompact(requirements)
|
|
235
|
-
: formatRequirementsForPrompt(requirements);
|
|
236
|
-
return `### Requirements\nSource: \`.gsd/REQUIREMENTS.md\`\n\n${formatted}`;
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
} catch {
|
|
240
|
-
// DB not available — fall through to filesystem
|
|
241
|
-
}
|
|
242
|
-
return inlineGsdRootFile(base, "requirements.md", "Requirements");
|
|
248
|
+
return inlineFromDbOrFile(base, "Requirements", "requirements.md", (cs) => {
|
|
249
|
+
const requirements = cs.queryRequirements({ sliceId });
|
|
250
|
+
if (requirements.length === 0) return null;
|
|
251
|
+
return inlineLevel !== "full"
|
|
252
|
+
? formatRequirementsCompact(requirements)
|
|
253
|
+
: cs.formatRequirementsForPrompt(requirements);
|
|
254
|
+
});
|
|
243
255
|
}
|
|
244
256
|
|
|
245
257
|
/**
|
|
@@ -249,19 +261,9 @@ export async function inlineRequirementsFromDb(
|
|
|
249
261
|
export async function inlineProjectFromDb(
|
|
250
262
|
base: string,
|
|
251
263
|
): Promise<string | null> {
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
const { queryProject } = await import("./context-store.js");
|
|
256
|
-
const content = queryProject();
|
|
257
|
-
if (content) {
|
|
258
|
-
return `### Project\nSource: \`.gsd/PROJECT.md\`\n\n${content}`;
|
|
259
|
-
}
|
|
260
|
-
}
|
|
261
|
-
} catch {
|
|
262
|
-
// DB not available — fall through to filesystem
|
|
263
|
-
}
|
|
264
|
-
return inlineGsdRootFile(base, "project.md", "Project");
|
|
264
|
+
return inlineFromDbOrFile(base, "Project", "project.md", (cs) => {
|
|
265
|
+
return cs.queryProject();
|
|
266
|
+
});
|
|
265
267
|
}
|
|
266
268
|
|
|
267
269
|
// ─── Skill Discovery ──────────────────────────────────────────────────────
|
|
@@ -324,6 +326,27 @@ function oneLine(text: string): string {
|
|
|
324
326
|
return text.replace(/\s+/g, " ").trim();
|
|
325
327
|
}
|
|
326
328
|
|
|
329
|
+
/** Build the standard inlined-context section used by all prompt builders. */
|
|
330
|
+
function buildInlinedContextSection(inlined: string[]): string {
|
|
331
|
+
return `## Inlined Context (preloaded — do not re-read these files)\n\n${inlined.join("\n\n---\n\n")}`;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
/** Build the formatted list of available GSD source files for planners to read on demand. */
|
|
335
|
+
function buildSourceFileList(base: string, opts?: { includeProject?: boolean }): string {
|
|
336
|
+
const paths: string[] = [];
|
|
337
|
+
if (opts?.includeProject && existsSync(resolveGsdRootFile(base, "PROJECT")))
|
|
338
|
+
paths.push(`- **Project**: \`${relGsdRootFile("PROJECT")}\``);
|
|
339
|
+
if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
|
|
340
|
+
paths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
|
|
341
|
+
if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
|
|
342
|
+
paths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
|
|
343
|
+
if (paths.length === 0) {
|
|
344
|
+
const types = opts?.includeProject ? "project/requirements/decisions" : "requirements/decisions";
|
|
345
|
+
return `_No ${types} files found._`;
|
|
346
|
+
}
|
|
347
|
+
return paths.join("\n");
|
|
348
|
+
}
|
|
349
|
+
|
|
327
350
|
// ─── Section Builders ──────────────────────────────────────────────────────
|
|
328
351
|
|
|
329
352
|
export function buildResumeSection(
|
|
@@ -540,8 +563,11 @@ export async function checkNeedsRunUat(
|
|
|
540
563
|
if (resultContent) return null;
|
|
541
564
|
}
|
|
542
565
|
|
|
543
|
-
// Classify UAT type;
|
|
566
|
+
// Classify UAT type; skip non-artifact-driven types — auto-mode can only
|
|
567
|
+
// execute mechanical checks. Non-artifact UATs are tracked in the dashboard
|
|
568
|
+
// but don't block auto-mode progression.
|
|
544
569
|
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
570
|
+
if (uatType !== "artifact-driven") return null;
|
|
545
571
|
|
|
546
572
|
return { sliceId: sid, uatType };
|
|
547
573
|
}
|
|
@@ -564,7 +590,7 @@ export async function buildResearchMilestonePrompt(mid: string, midTitle: string
|
|
|
564
590
|
if (knowledgeInlineRM) inlined.push(knowledgeInlineRM);
|
|
565
591
|
inlined.push(inlineTemplate("research", "Research"));
|
|
566
592
|
|
|
567
|
-
const inlinedContext =
|
|
593
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
568
594
|
|
|
569
595
|
const outputRelPath = relMilestoneFile(base, mid, "RESEARCH");
|
|
570
596
|
return loadPrompt("research-milestone", {
|
|
@@ -592,17 +618,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
592
618
|
const { inlinePriorMilestoneSummary } = await import("./files.js");
|
|
593
619
|
const priorSummaryInline = await inlinePriorMilestoneSummary(mid, base);
|
|
594
620
|
if (priorSummaryInline) inlined.push(priorSummaryInline);
|
|
595
|
-
|
|
596
|
-
const sourcePaths: string[] = [];
|
|
597
|
-
if (existsSync(resolveGsdRootFile(base, "PROJECT")))
|
|
598
|
-
sourcePaths.push(`- **Project**: \`${relGsdRootFile("PROJECT")}\``);
|
|
599
|
-
if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
|
|
600
|
-
sourcePaths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
|
|
601
|
-
if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
|
|
602
|
-
sourcePaths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
|
|
603
|
-
const sourceFilePaths = sourcePaths.length > 0
|
|
604
|
-
? sourcePaths.join("\n")
|
|
605
|
-
: "_No project/requirements/decisions files found._";
|
|
621
|
+
const sourceFilePaths = buildSourceFileList(base, { includeProject: true });
|
|
606
622
|
|
|
607
623
|
const knowledgeInlinePM = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
608
624
|
if (knowledgeInlinePM) inlined.push(knowledgeInlinePM);
|
|
@@ -618,7 +634,7 @@ export async function buildPlanMilestonePrompt(mid: string, midTitle: string, ba
|
|
|
618
634
|
inlined.push(inlineTemplate("task-plan", "Task Plan"));
|
|
619
635
|
}
|
|
620
636
|
|
|
621
|
-
const inlinedContext =
|
|
637
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
622
638
|
|
|
623
639
|
const outputRelPath = relMilestoneFile(base, mid, "ROADMAP");
|
|
624
640
|
const secretsOutputPath = join(base, relMilestoneFile(base, mid, "SECRETS"));
|
|
@@ -667,7 +683,7 @@ export async function buildResearchSlicePrompt(
|
|
|
667
683
|
const overridesInline = formatOverridesSection(activeOverrides);
|
|
668
684
|
if (overridesInline) inlined.unshift(overridesInline);
|
|
669
685
|
|
|
670
|
-
const inlinedContext =
|
|
686
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
671
687
|
|
|
672
688
|
const outputRelPath = relSliceFile(base, mid, sid, "RESEARCH");
|
|
673
689
|
return loadPrompt("research-slice", {
|
|
@@ -697,15 +713,7 @@ export async function buildPlanSlicePrompt(
|
|
|
697
713
|
inlined.push(await inlineFile(roadmapPath, roadmapRel, "Milestone Roadmap"));
|
|
698
714
|
const researchInline = await inlineFileOptional(researchPath, researchRel, "Slice Research");
|
|
699
715
|
if (researchInline) inlined.push(researchInline);
|
|
700
|
-
|
|
701
|
-
const sliceSourcePaths: string[] = [];
|
|
702
|
-
if (existsSync(resolveGsdRootFile(base, "REQUIREMENTS")))
|
|
703
|
-
sliceSourcePaths.push(`- **Requirements**: \`${relGsdRootFile("REQUIREMENTS")}\``);
|
|
704
|
-
if (existsSync(resolveGsdRootFile(base, "DECISIONS")))
|
|
705
|
-
sliceSourcePaths.push(`- **Decisions**: \`${relGsdRootFile("DECISIONS")}\``);
|
|
706
|
-
const sliceSourceFilePaths = sliceSourcePaths.length > 0
|
|
707
|
-
? sliceSourcePaths.join("\n")
|
|
708
|
-
: "_No requirements/decisions files found._";
|
|
716
|
+
const sliceSourceFilePaths = buildSourceFileList(base);
|
|
709
717
|
|
|
710
718
|
const knowledgeInlinePS = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
711
719
|
if (knowledgeInlinePS) inlined.push(knowledgeInlinePS);
|
|
@@ -719,7 +727,7 @@ export async function buildPlanSlicePrompt(
|
|
|
719
727
|
const planOverridesInline = formatOverridesSection(planActiveOverrides);
|
|
720
728
|
if (planOverridesInline) inlined.unshift(planOverridesInline);
|
|
721
729
|
|
|
722
|
-
const inlinedContext =
|
|
730
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
723
731
|
|
|
724
732
|
// Build executor context constraints from the budget engine
|
|
725
733
|
const executorContextConstraints = formatExecutorConstraints();
|
|
@@ -894,7 +902,7 @@ export async function buildCompleteSlicePrompt(
|
|
|
894
902
|
const completeOverridesInline = formatOverridesSection(completeActiveOverrides);
|
|
895
903
|
if (completeOverridesInline) inlined.unshift(completeOverridesInline);
|
|
896
904
|
|
|
897
|
-
const inlinedContext =
|
|
905
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
898
906
|
|
|
899
907
|
const sliceRel = relSlicePath(base, mid, sid);
|
|
900
908
|
const sliceSummaryPath = join(base, `${sliceRel}/${sid}-SUMMARY.md`);
|
|
@@ -953,7 +961,7 @@ export async function buildCompleteMilestonePrompt(
|
|
|
953
961
|
if (contextInline) inlined.push(contextInline);
|
|
954
962
|
inlined.push(inlineTemplate("milestone-summary", "Milestone Summary"));
|
|
955
963
|
|
|
956
|
-
const inlinedContext =
|
|
964
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
957
965
|
|
|
958
966
|
const milestoneSummaryPath = join(base, `${relMilestonePath(base, mid)}/${mid}-SUMMARY.md`);
|
|
959
967
|
|
|
@@ -1024,7 +1032,7 @@ export async function buildValidateMilestonePrompt(
|
|
|
1024
1032
|
const contextInline = await inlineFileOptional(contextPath, contextRel, "Milestone Context");
|
|
1025
1033
|
if (contextInline) inlined.push(contextInline);
|
|
1026
1034
|
|
|
1027
|
-
const inlinedContext =
|
|
1035
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1028
1036
|
|
|
1029
1037
|
const validationOutputPath = join(base, `${relMilestonePath(base, mid)}/${mid}-VALIDATION.md`);
|
|
1030
1038
|
const roadmapOutputPath = `${relMilestonePath(base, mid)}/${mid}-ROADMAP.md`;
|
|
@@ -1078,7 +1086,7 @@ export async function buildReplanSlicePrompt(
|
|
|
1078
1086
|
const replanOverridesInline = formatOverridesSection(replanActiveOverrides);
|
|
1079
1087
|
if (replanOverridesInline) inlined.unshift(replanOverridesInline);
|
|
1080
1088
|
|
|
1081
|
-
const inlinedContext =
|
|
1089
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1082
1090
|
|
|
1083
1091
|
const replanPath = join(base, `${relSlicePath(base, mid, sid)}/${sid}-REPLAN.md`);
|
|
1084
1092
|
|
|
@@ -1111,7 +1119,7 @@ export async function buildReplanSlicePrompt(
|
|
|
1111
1119
|
}
|
|
1112
1120
|
|
|
1113
1121
|
export async function buildRunUatPrompt(
|
|
1114
|
-
mid: string, sliceId: string, uatPath: string,
|
|
1122
|
+
mid: string, sliceId: string, uatPath: string, base: string,
|
|
1115
1123
|
): Promise<string> {
|
|
1116
1124
|
const inlined: string[] = [];
|
|
1117
1125
|
inlined.push(await inlineFile(resolveSliceFile(base, mid, sliceId, "UAT"), uatPath, `${sliceId} UAT`));
|
|
@@ -1126,10 +1134,9 @@ export async function buildRunUatPrompt(
|
|
|
1126
1134
|
const projectInline = await inlineProjectFromDb(base);
|
|
1127
1135
|
if (projectInline) inlined.push(projectInline);
|
|
1128
1136
|
|
|
1129
|
-
const inlinedContext =
|
|
1137
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1130
1138
|
|
|
1131
1139
|
const uatResultPath = join(base, relSliceFile(base, mid, sliceId, "UAT-RESULT"));
|
|
1132
|
-
const uatType = extractUatType(uatContent) ?? "human-experience";
|
|
1133
1140
|
|
|
1134
1141
|
return loadPrompt("run-uat", {
|
|
1135
1142
|
workingDirectory: base,
|
|
@@ -1137,7 +1144,6 @@ export async function buildRunUatPrompt(
|
|
|
1137
1144
|
sliceId,
|
|
1138
1145
|
uatPath,
|
|
1139
1146
|
uatResultPath,
|
|
1140
|
-
uatType,
|
|
1141
1147
|
inlinedContext,
|
|
1142
1148
|
});
|
|
1143
1149
|
}
|
|
@@ -1165,7 +1171,7 @@ export async function buildReassessRoadmapPrompt(
|
|
|
1165
1171
|
const knowledgeInlineRA = await inlineGsdRootFile(base, "knowledge.md", "Project Knowledge");
|
|
1166
1172
|
if (knowledgeInlineRA) inlined.push(knowledgeInlineRA);
|
|
1167
1173
|
|
|
1168
|
-
const inlinedContext =
|
|
1174
|
+
const inlinedContext = buildInlinedContextSection(inlined);
|
|
1169
1175
|
|
|
1170
1176
|
const assessmentPath = join(base, relSliceFile(base, mid, completedSliceId, "ASSESSMENT"));
|
|
1171
1177
|
|
|
@@ -42,6 +42,7 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "
|
|
|
42
42
|
import { atomicWriteSync } from "./atomic-write.js";
|
|
43
43
|
import { loadJsonFileOrNull } from "./json-persistence.js";
|
|
44
44
|
import { dirname, join } from "node:path";
|
|
45
|
+
import { parseUnitId } from "./unit-id.js";
|
|
45
46
|
|
|
46
47
|
// ─── Artifact Resolution & Verification ───────────────────────────────────────
|
|
47
48
|
|
|
@@ -49,9 +50,7 @@ import { dirname, join } from "node:path";
|
|
|
49
50
|
* Resolve the expected artifact for a unit to an absolute path.
|
|
50
51
|
*/
|
|
51
52
|
export function resolveExpectedArtifactPath(unitType: string, unitId: string, base: string): string | null {
|
|
52
|
-
const
|
|
53
|
-
const mid = parts[0]!;
|
|
54
|
-
const sid = parts[1];
|
|
53
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
55
54
|
switch (unitType) {
|
|
56
55
|
case "research-milestone": {
|
|
57
56
|
const dir = resolveMilestonePath(base, mid);
|
|
@@ -78,7 +77,6 @@ export function resolveExpectedArtifactPath(unitType: string, unitId: string, ba
|
|
|
78
77
|
return dir ? join(dir, buildSliceFileName(sid!, "UAT-RESULT")) : null;
|
|
79
78
|
}
|
|
80
79
|
case "execute-task": {
|
|
81
|
-
const tid = parts[2];
|
|
82
80
|
const dir = resolveSlicePath(base, mid, sid!);
|
|
83
81
|
return dir && tid ? join(dir, "tasks", buildTaskFileName(tid, "SUMMARY")) : null;
|
|
84
82
|
}
|
|
@@ -167,10 +165,7 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
167
165
|
|
|
168
166
|
// execute-task must also have its checkbox marked [x] in the slice plan
|
|
169
167
|
if (unitType === "execute-task") {
|
|
170
|
-
const
|
|
171
|
-
const mid = parts[0];
|
|
172
|
-
const sid = parts[1];
|
|
173
|
-
const tid = parts[2];
|
|
168
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
174
169
|
if (mid && sid && tid) {
|
|
175
170
|
const planAbs = resolveSliceFile(base, mid, sid, "PLAN");
|
|
176
171
|
if (planAbs && existsSync(planAbs)) {
|
|
@@ -187,9 +182,7 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
187
182
|
// but omitted T{tid}-PLAN.md files would be marked complete, causing execute-task
|
|
188
183
|
// to dispatch with a missing task plan (see issue #739).
|
|
189
184
|
if (unitType === "plan-slice") {
|
|
190
|
-
const
|
|
191
|
-
const mid = parts[0];
|
|
192
|
-
const sid = parts[1];
|
|
185
|
+
const { milestone: mid, slice: sid } = parseUnitId(unitId);
|
|
193
186
|
if (mid && sid) {
|
|
194
187
|
try {
|
|
195
188
|
const planContent = readFileSync(absPath, "utf-8");
|
|
@@ -213,9 +206,8 @@ export function verifyExpectedArtifact(unitType: string, unitId: string, base: s
|
|
|
213
206
|
// state machine keeps returning the same complete-slice unit (roadmap still shows
|
|
214
207
|
// the slice incomplete), so dispatchNextUnit recurses forever.
|
|
215
208
|
if (unitType === "complete-slice") {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
const sid = parts[1];
|
|
209
|
+
const { milestone: mid, slice: sid } = parseUnitId(unitId);
|
|
210
|
+
|
|
219
211
|
if (mid && sid) {
|
|
220
212
|
const dir = resolveSlicePath(base, mid, sid);
|
|
221
213
|
if (dir) {
|
|
@@ -268,9 +260,7 @@ export function writeBlockerPlaceholder(unitType: string, unitId: string, base:
|
|
|
268
260
|
}
|
|
269
261
|
|
|
270
262
|
export function diagnoseExpectedArtifact(unitType: string, unitId: string, base: string): string | null {
|
|
271
|
-
const
|
|
272
|
-
const mid = parts[0];
|
|
273
|
-
const sid = parts[1];
|
|
263
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
274
264
|
switch (unitType) {
|
|
275
265
|
case "research-milestone":
|
|
276
266
|
return `${relMilestoneFile(base, mid!, "RESEARCH")} (milestone research)`;
|
|
@@ -281,7 +271,6 @@ export function diagnoseExpectedArtifact(unitType: string, unitId: string, base:
|
|
|
281
271
|
case "plan-slice":
|
|
282
272
|
return `${relSliceFile(base, mid!, sid!, "PLAN")} (slice plan)`;
|
|
283
273
|
case "execute-task": {
|
|
284
|
-
const tid = parts[2];
|
|
285
274
|
return `Task ${tid} marked [x] in ${relSliceFile(base, mid!, sid!, "PLAN")} + summary written`;
|
|
286
275
|
}
|
|
287
276
|
case "complete-slice":
|
|
@@ -539,10 +528,7 @@ export async function selfHealRuntimeRecords(
|
|
|
539
528
|
* These are shown when automatic reconciliation is not possible.
|
|
540
529
|
*/
|
|
541
530
|
export function buildLoopRemediationSteps(unitType: string, unitId: string, base: string): string | null {
|
|
542
|
-
const
|
|
543
|
-
const mid = parts[0];
|
|
544
|
-
const sid = parts[1];
|
|
545
|
-
const tid = parts[2];
|
|
531
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
546
532
|
switch (unitType) {
|
|
547
533
|
case "execute-task": {
|
|
548
534
|
if (!mid || !sid || !tid) break;
|
|
@@ -38,7 +38,7 @@ import {
|
|
|
38
38
|
import { selfHealRuntimeRecords } from "./auto-recovery.js";
|
|
39
39
|
import { ensureGitignore, untrackRuntimeFiles } from "./gitignore.js";
|
|
40
40
|
import { nativeIsRepo, nativeInit } from "./native-git-bridge.js";
|
|
41
|
-
import {
|
|
41
|
+
import { createGitService } from "./git-service.js";
|
|
42
42
|
import {
|
|
43
43
|
captureIntegrationBranch,
|
|
44
44
|
detectWorktreeName,
|
|
@@ -63,6 +63,8 @@ import { debugLog, enableDebug, isDebugEnabled, getDebugLogPath } from "./debug-
|
|
|
63
63
|
import type { AutoSession } from "./auto/session.js";
|
|
64
64
|
import { existsSync, mkdirSync, readdirSync, statSync, unlinkSync } from "node:fs";
|
|
65
65
|
import { join } from "node:path";
|
|
66
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
67
|
+
import { parseUnitId } from "./unit-id.js";
|
|
66
68
|
|
|
67
69
|
export interface BootstrapDeps {
|
|
68
70
|
shouldUseWorktreeIsolation: () => boolean;
|
|
@@ -129,14 +131,16 @@ export async function bootstrapAutoSession(
|
|
|
129
131
|
}
|
|
130
132
|
|
|
131
133
|
// Initialize GitServiceImpl
|
|
132
|
-
s.gitService =
|
|
134
|
+
s.gitService = createGitService(s.basePath);
|
|
133
135
|
|
|
134
|
-
// Check for crash from previous session (use both old and new lock data)
|
|
136
|
+
// Check for crash from previous session (use both old and new lock data).
|
|
137
|
+
// Skip if the lock PID matches this process — acquireSessionLock() writes
|
|
138
|
+
// to the same auto.lock file before this check, so we'd always false-positive.
|
|
135
139
|
const crashLock = readCrashLock(base);
|
|
136
|
-
if (crashLock) {
|
|
140
|
+
if (crashLock && crashLock.pid !== process.pid) {
|
|
137
141
|
// We already hold the session lock, so no concurrent session is running.
|
|
138
142
|
// The crash lock is from a dead process — recover context from it.
|
|
139
|
-
const recoveredMid = crashLock.unitId.
|
|
143
|
+
const recoveredMid = parseUnitId(crashLock.unitId).milestone;
|
|
140
144
|
const milestoneAlreadyComplete = recoveredMid
|
|
141
145
|
? !!resolveMilestoneFile(base, recoveredMid, "SUMMARY")
|
|
142
146
|
: false;
|
|
@@ -199,11 +203,11 @@ export async function bootstrapAutoSession(
|
|
|
199
203
|
if (!midMatch) continue;
|
|
200
204
|
const mid = midMatch[1];
|
|
201
205
|
if (resolveMilestoneFile(base, mid, "SUMMARY")) {
|
|
202
|
-
try { unlinkSync(join(runtimeUnitsDir, file)); } catch (e) { debugLog("stale-unit-cleanup-failed", { file, error:
|
|
206
|
+
try { unlinkSync(join(runtimeUnitsDir, file)); } catch (e) { debugLog("stale-unit-cleanup-failed", { file, error: getErrorMessage(e) }); }
|
|
203
207
|
}
|
|
204
208
|
}
|
|
205
209
|
}
|
|
206
|
-
} catch (e) { debugLog("stale-unit-dir-cleanup-failed", { error:
|
|
210
|
+
} catch (e) { debugLog("stale-unit-dir-cleanup-failed", { error: getErrorMessage(e) }); }
|
|
207
211
|
|
|
208
212
|
let state = await deriveState(base);
|
|
209
213
|
|
|
@@ -330,18 +334,18 @@ export async function bootstrapAutoSession(
|
|
|
330
334
|
if (existingWtPath) {
|
|
331
335
|
const wtPath = enterAutoWorktree(base, s.currentMilestoneId);
|
|
332
336
|
s.basePath = wtPath;
|
|
333
|
-
s.gitService =
|
|
337
|
+
s.gitService = createGitService(s.basePath);
|
|
334
338
|
ctx.ui.notify(`Entered auto-worktree at ${wtPath}`, "info");
|
|
335
339
|
} else {
|
|
336
340
|
const wtPath = createAutoWorktree(base, s.currentMilestoneId);
|
|
337
341
|
s.basePath = wtPath;
|
|
338
|
-
s.gitService =
|
|
342
|
+
s.gitService = createGitService(s.basePath);
|
|
339
343
|
ctx.ui.notify(`Created auto-worktree at ${wtPath}`, "info");
|
|
340
344
|
}
|
|
341
345
|
registerSigtermHandler(s.originalBasePath);
|
|
342
346
|
} catch (err) {
|
|
343
347
|
ctx.ui.notify(
|
|
344
|
-
`Auto-worktree setup failed: ${
|
|
348
|
+
`Auto-worktree setup failed: ${getErrorMessage(err)}. Continuing in project root.`,
|
|
345
349
|
"warning",
|
|
346
350
|
);
|
|
347
351
|
}
|
|
@@ -433,7 +437,7 @@ export async function bootstrapAutoSession(
|
|
|
433
437
|
}
|
|
434
438
|
} catch (err) {
|
|
435
439
|
ctx.ui.notify(
|
|
436
|
-
`Secrets check error: ${
|
|
440
|
+
`Secrets check error: ${getErrorMessage(err)}. Continuing without secrets.`,
|
|
437
441
|
"warning",
|
|
438
442
|
);
|
|
439
443
|
}
|
|
@@ -451,7 +455,7 @@ export async function bootstrapAutoSession(
|
|
|
451
455
|
ctx.ui.notify("Removed stale .git/index.lock from prior crash.", "info");
|
|
452
456
|
}
|
|
453
457
|
}
|
|
454
|
-
} catch (e) { debugLog("git-lock-cleanup-failed", { error:
|
|
458
|
+
} catch (e) { debugLog("git-lock-cleanup-failed", { error: getErrorMessage(e) }); }
|
|
455
459
|
|
|
456
460
|
// Pre-flight: validate milestone queue
|
|
457
461
|
try {
|
|
@@ -39,6 +39,7 @@ import {
|
|
|
39
39
|
import type { AutoSession } from "./auto/session.js";
|
|
40
40
|
import { existsSync, mkdirSync, writeFileSync } from "node:fs";
|
|
41
41
|
import { join } from "node:path";
|
|
42
|
+
import { parseUnitId } from "./unit-id.js";
|
|
42
43
|
|
|
43
44
|
export interface StuckContext {
|
|
44
45
|
s: AutoSession;
|
|
@@ -99,7 +100,7 @@ export async function checkStuckAndRecover(sctx: StuckContext): Promise<StuckRes
|
|
|
99
100
|
|
|
100
101
|
// Final reconciliation pass for execute-task
|
|
101
102
|
if (unitType === "execute-task") {
|
|
102
|
-
const
|
|
103
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
103
104
|
if (mid && sid && tid) {
|
|
104
105
|
const status = await inspectExecuteTaskDurability(basePath, unitId);
|
|
105
106
|
if (status) {
|
|
@@ -168,7 +169,7 @@ export async function checkStuckAndRecover(sctx: StuckContext): Promise<StuckRes
|
|
|
168
169
|
// Adaptive self-repair: each retry attempts a different remediation step.
|
|
169
170
|
if (unitType === "execute-task") {
|
|
170
171
|
const status = await inspectExecuteTaskDurability(basePath, unitId);
|
|
171
|
-
const
|
|
172
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
172
173
|
if (status && mid && sid && tid) {
|
|
173
174
|
if (status.summaryExists && !status.taskChecked) {
|
|
174
175
|
const repaired = skipExecuteTask(basePath, mid, sid, tid, status, "self-repair", 0);
|
|
@@ -18,6 +18,7 @@ import {
|
|
|
18
18
|
writeBlockerPlaceholder,
|
|
19
19
|
} from "./auto-recovery.js";
|
|
20
20
|
import { existsSync } from "node:fs";
|
|
21
|
+
import { parseUnitId } from "./unit-id.js";
|
|
21
22
|
|
|
22
23
|
export interface RecoveryContext {
|
|
23
24
|
basePath: string;
|
|
@@ -128,7 +129,7 @@ export async function recoverTimedOutUnit(
|
|
|
128
129
|
|
|
129
130
|
// Retries exhausted — write missing durable artifacts and advance.
|
|
130
131
|
const diagnostic = formatExecuteTaskRecoveryStatus(status);
|
|
131
|
-
const
|
|
132
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(unitId);
|
|
132
133
|
const skipped = mid && sid && tid
|
|
133
134
|
? skipExecuteTask(basePath, mid, sid, tid, status, reason, maxRecoveryAttempts)
|
|
134
135
|
: false;
|
|
@@ -20,6 +20,7 @@ import { closeoutUnit, type CloseoutOptions } from "./auto-unit-closeout.js";
|
|
|
20
20
|
import { saveActivityLog } from "./activity-log.js";
|
|
21
21
|
import { recoverTimedOutUnit, type RecoveryContext } from "./auto-timeout-recovery.js";
|
|
22
22
|
import type { AutoSession } from "./auto/session.js";
|
|
23
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
23
24
|
|
|
24
25
|
export interface SupervisionContext {
|
|
25
26
|
s: AutoSession;
|
|
@@ -127,7 +128,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
127
128
|
);
|
|
128
129
|
await pauseAuto(ctx, pi);
|
|
129
130
|
} catch (err) {
|
|
130
|
-
const message =
|
|
131
|
+
const message = getErrorMessage(err);
|
|
131
132
|
console.error(`[idle-watchdog] Unhandled error: ${message}`);
|
|
132
133
|
try {
|
|
133
134
|
ctx.ui.notify(`Idle watchdog error: ${message}`, "warning");
|
|
@@ -159,7 +160,7 @@ export function startUnitSupervision(sctx: SupervisionContext): void {
|
|
|
159
160
|
);
|
|
160
161
|
await pauseAuto(ctx, pi);
|
|
161
162
|
} catch (err) {
|
|
162
|
-
const message =
|
|
163
|
+
const message = getErrorMessage(err);
|
|
163
164
|
console.error(`[hard-timeout] Unhandled error: ${message}`);
|
|
164
165
|
try {
|
|
165
166
|
ctx.ui.notify(`Hard timeout error: ${message}`, "warning");
|
|
@@ -24,6 +24,8 @@ import { writeVerificationJSON } from "./verification-evidence.js";
|
|
|
24
24
|
import { removePersistedKey } from "./auto-recovery.js";
|
|
25
25
|
import type { AutoSession, PendingVerificationRetry } from "./auto/session.js";
|
|
26
26
|
import { join } from "node:path";
|
|
27
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
28
|
+
import { parseUnitId } from "./unit-id.js";
|
|
27
29
|
|
|
28
30
|
export interface VerificationContext {
|
|
29
31
|
s: AutoSession;
|
|
@@ -57,10 +59,9 @@ export async function runPostUnitVerification(
|
|
|
57
59
|
const prefs = effectivePrefs?.preferences;
|
|
58
60
|
|
|
59
61
|
// Read task plan verify field
|
|
60
|
-
const
|
|
62
|
+
const { milestone: mid, slice: sid, task: tid } = parseUnitId(s.currentUnit.id);
|
|
61
63
|
let taskPlanVerify: string | undefined;
|
|
62
|
-
if (
|
|
63
|
-
const [mid, sid, tid] = parts;
|
|
64
|
+
if (mid && sid && tid) {
|
|
64
65
|
const planFile = resolveSliceFile(s.basePath, mid, sid, "PLAN");
|
|
65
66
|
if (planFile) {
|
|
66
67
|
const planContent = await loadFile(planFile);
|
|
@@ -152,9 +153,8 @@ export async function runPostUnitVerification(
|
|
|
152
153
|
|
|
153
154
|
// Write verification evidence JSON
|
|
154
155
|
const attempt = s.verificationRetryCount.get(s.currentUnit.id) ?? 0;
|
|
155
|
-
if (
|
|
156
|
+
if (mid && sid && tid) {
|
|
156
157
|
try {
|
|
157
|
-
const [mid, sid, tid] = parts;
|
|
158
158
|
const sDir = resolveSlicePath(s.basePath, mid, sid);
|
|
159
159
|
if (sDir) {
|
|
160
160
|
const tasksDir = join(sDir, "tasks");
|
|
@@ -204,7 +204,7 @@ export async function runPostUnitVerification(
|
|
|
204
204
|
try {
|
|
205
205
|
await dispatchNextUnit(ctx, pi);
|
|
206
206
|
} catch (retryDispatchErr) {
|
|
207
|
-
const msg =
|
|
207
|
+
const msg = getErrorMessage(retryDispatchErr);
|
|
208
208
|
ctx.ui.notify(`Verification retry dispatch error: ${msg}`, "error");
|
|
209
209
|
startDispatchGapWatchdog(ctx, pi);
|
|
210
210
|
}
|
|
@@ -38,6 +38,7 @@ import {
|
|
|
38
38
|
nativeBranchDelete,
|
|
39
39
|
nativeBranchExists,
|
|
40
40
|
} from "./native-git-bridge.js";
|
|
41
|
+
import { getErrorMessage } from "./error-utils.js";
|
|
41
42
|
|
|
42
43
|
// ─── Module State ──────────────────────────────────────────────────────────
|
|
43
44
|
|
|
@@ -81,7 +82,7 @@ export function runWorktreePostCreateHook(sourceDir: string, worktreeDir: string
|
|
|
81
82
|
});
|
|
82
83
|
return null;
|
|
83
84
|
} catch (err) {
|
|
84
|
-
const msg =
|
|
85
|
+
const msg = getErrorMessage(err);
|
|
85
86
|
return `Worktree post-create hook failed: ${msg}`;
|
|
86
87
|
}
|
|
87
88
|
}
|
|
@@ -141,7 +142,7 @@ export function createAutoWorktree(basePath: string, milestoneId: string): strin
|
|
|
141
142
|
// Don't store originalBase -- caller can retry or clean up.
|
|
142
143
|
throw new GSDError(
|
|
143
144
|
GSD_IO_ERROR,
|
|
144
|
-
`Auto-worktree created at ${info.path} but chdir failed: ${
|
|
145
|
+
`Auto-worktree created at ${info.path} but chdir failed: ${getErrorMessage(err)}`,
|
|
145
146
|
);
|
|
146
147
|
}
|
|
147
148
|
|
|
@@ -168,7 +169,7 @@ export function teardownAutoWorktree(
|
|
|
168
169
|
} catch (err) {
|
|
169
170
|
throw new GSDError(
|
|
170
171
|
GSD_IO_ERROR,
|
|
171
|
-
`Failed to chdir back to ${originalBasePath} during teardown: ${
|
|
172
|
+
`Failed to chdir back to ${originalBasePath} during teardown: ${getErrorMessage(err)}`,
|
|
172
173
|
);
|
|
173
174
|
}
|
|
174
175
|
|
|
@@ -274,7 +275,7 @@ export function enterAutoWorktree(basePath: string, milestoneId: string): string
|
|
|
274
275
|
} catch (err) {
|
|
275
276
|
throw new GSDError(
|
|
276
277
|
GSD_IO_ERROR,
|
|
277
|
-
`Failed to enter auto-worktree at ${p}: ${
|
|
278
|
+
`Failed to enter auto-worktree at ${p}: ${getErrorMessage(err)}`,
|
|
278
279
|
);
|
|
279
280
|
}
|
|
280
281
|
|