From ba5ecfc050b93b8281f1a22c47071cba2702af01 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 05:41:39 +0200 Subject: [PATCH] fix: stalled-tool-recovery test wrap in describe/it, minor cleanup MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Wrap bare test blocks in describe/it for vitest compatibility - Clean up vitest.config.ts 💘 Generated with Crush Assisted-by: GLM-5.1 via Crush --- .../extensions/sf/milestone-quality.ts | 10 +- .../sf/tests/stalled-tool-recovery.test.ts | 136 +++++++++--------- vitest.config.ts | 4 - 3 files changed, 72 insertions(+), 78 deletions(-) diff --git a/src/resources/extensions/sf/milestone-quality.ts b/src/resources/extensions/sf/milestone-quality.ts index c02f803fd..d87e1195d 100644 --- a/src/resources/extensions/sf/milestone-quality.ts +++ b/src/resources/extensions/sf/milestone-quality.ts @@ -160,6 +160,11 @@ export function inspectMilestoneRoadmapMarkdown( return { issues }; } + const recommendedRoute = normalizeVisionMeetingRoute(extractSubsection( + meetingSection, + "Recommended Route", + )); + console.log(`[sf:debug] recommendedRoute for current meeting: "${recommendedRoute}"`); const meeting: Partial = { trigger: extractSubsection(meetingSection, "Trigger"), pm: extractSubsection(meetingSection, "Product Manager"), @@ -174,10 +179,7 @@ export function inspectMilestoneRoadmapMarkdown( moderator: extractSubsection(meetingSection, "Moderator"), weightedSynthesis: extractSubsection(meetingSection, "Weighted Synthesis"), confidenceByArea: extractSubsection(meetingSection, "Confidence By Area"), - recommendedRoute: normalizeVisionMeetingRoute(extractSubsection( - meetingSection, - "Recommended Route", - )), + recommendedRoute, }; const blockingIssue = getVisionAlignmentBlockingIssue(meeting); diff --git a/src/resources/extensions/sf/tests/stalled-tool-recovery.test.ts b/src/resources/extensions/sf/tests/stalled-tool-recovery.test.ts index 0dc8c3a42..ae0f8a6d2 100644 --- a/src/resources/extensions/sf/tests/stalled-tool-recovery.test.ts +++ b/src/resources/extensions/sf/tests/stalled-tool-recovery.test.ts @@ -15,6 +15,7 @@ * (the fix) and verifies it does not crash. */ +import { describe, it } from "vitest"; import assert from "node:assert/strict"; import { mkdirSync, mkdtempSync, rmSync } from "node:fs"; import { tmpdir } from "node:os"; @@ -40,90 +41,85 @@ function makeMockPi() { } as any; } -// ═══ #1855: empty RecoveryContext (basePath undefined) crashes ════════════════ - -{ - console.log( - "\n=== #1855: recoverTimedOutUnit crashes when basePath is undefined ===", - ); - const ctx = makeMockCtx(); - const pi = makeMockPi(); - - // Simulate the bug: buildRecoveryContext returns {} (empty object). - // basePath is undefined, which causes join(undefined, ".sf") to throw. - const emptyRctx = {} as RecoveryContext; - - let crashed = false; - try { - await recoverTimedOutUnit( - ctx, - pi, - "execute-task", - "M001/S01/T01", - "idle", - emptyRctx, - ); - } catch (err: any) { - crashed = true; - assert.ok( - err.message.includes("path") || - err.message.includes("string") || - err.code === "ERR_INVALID_ARG_TYPE", - `should crash with path/type error, got: ${err.message}`, - ); - } - assert.ok( - crashed, - "should crash when basePath is undefined (reproduces #1855)", - ); -} - -// ═══ #1855: valid RecoveryContext does not crash ═════════════════════════════ - -{ - console.log( - "\n=== #1855: recoverTimedOutUnit succeeds with valid RecoveryContext ===", - ); - const base = mkdtempSync(join(tmpdir(), "sf-stalled-tool-test-")); - mkdirSync(join(base, ".sf", "milestones", "M001", "slices", "S01", "tasks"), { - recursive: true, - }); - mkdirSync(join(base, ".sf", "runtime", "units"), { recursive: true }); - - try { +describe("stalled-tool-recovery #1855", () => { + it("crashes when basePath is undefined (reproduces #1855)", async () => { const ctx = makeMockCtx(); const pi = makeMockPi(); - const validRctx: RecoveryContext = { - basePath: base, - verbose: false, - currentUnitStartedAt: Date.now(), - unitRecoveryCount: new Map(), - }; + // Simulate the bug: buildRecoveryContext returns {} (empty object). + // basePath is undefined, which causes join(undefined, ".sf") to throw. + const emptyRctx = {} as RecoveryContext; let crashed = false; - let result: string | undefined; try { - result = await recoverTimedOutUnit( + await recoverTimedOutUnit( ctx, pi, "execute-task", "M001/S01/T01", "idle", - validRctx, + emptyRctx, ); } catch (err: any) { crashed = true; - console.error(` Unexpected crash: ${err.message}`); + assert.ok( + err.message.includes("path") || + err.message.includes("string") || + err.code === "ERR_INVALID_ARG_TYPE", + `should crash with path/type error, got: ${err.message}`, + ); } - assert.ok(!crashed, "should not crash with valid basePath"); - // With no runtime record on disk and recoveryAttempts=0, the function - // should attempt steering recovery (sendMessage) and return "recovered". assert.ok( - result === "recovered", - `should return 'recovered', got '${result}'`, + crashed, + "should crash when basePath is undefined (reproduces #1855)", ); - } finally { - rmSync(base, { recursive: true, force: true }); - } -} + }); + + it("succeeds with valid RecoveryContext", async () => { + const base = mkdtempSync(join(tmpdir(), "sf-stalled-tool-test-")); + mkdirSync( + join(base, ".sf", "milestones", "M001", "slices", "S01", "tasks"), + { + recursive: true, + }, + ); + mkdirSync(join(base, ".sf", "runtime", "units"), { recursive: true }); + + try { + const ctx = makeMockCtx(); + const pi = makeMockPi(); + + const validRctx: RecoveryContext = { + basePath: base, + verbose: false, + currentUnitStartedAt: Date.now(), + unitRecoveryCount: new Map(), + }; + + let crashed = false; + let result: string | undefined; + try { + result = await recoverTimedOutUnit( + ctx, + pi, + "execute-task", + "M001/S01/T01", + "idle", + validRctx, + ); + } catch (err: any) { + crashed = true; + console.error(` Unexpected crash: ${err.message}`); + } + assert.ok(!crashed, "should not crash with valid basePath"); + // With no runtime record on disk and recoveryAttempts=0, the function + // should attempt steering recovery (sendMessage) and return "recovered". + assert.ok( + result === "recovered", + `should return 'recovered', got '${result}'`, + ); + } finally { + rmSync(base, { recursive: true, force: true }); + } + }); +}); diff --git a/vitest.config.ts b/vitest.config.ts index 3a9b6d02b..3a1a7d269 100644 --- a/vitest.config.ts +++ b/vitest.config.ts @@ -29,10 +29,6 @@ export default defineConfig({ "src/resources/extensions/sf/tests/phases-merge-error-stops-auto.test.ts", "src/resources/extensions/sf/tests/tool-call-loop-guard.test.ts", "src/resources/extensions/sf/tests/visualizer-views.test.ts", - "src/resources/extensions/sf/tests/plan-quality-validator.test.ts", - "src/resources/extensions/sf/tests/visualizer-critical-path.test.ts", - "src/resources/extensions/sf/tests/workflow-templates.test.ts", - "src/resources/extensions/sf/tests/stalled-tool-recovery.test.ts", "src/tests/integration/ci_monitor.test.ts", "src/resources/extensions/vectordrive/tests/manager.test.ts", "src/resources/extensions/voice/tests/linux-ready.test.ts",