From de728db20068a9d3b56c9ff9a39d5c7076edd3e0 Mon Sep 17 00:00:00 2001 From: ag-linden Date: Wed, 1 Jul 2026 10:39:10 -0500 Subject: [PATCH] fix(scheduler): normalize project cancellation paths --- src/orchestration/taskScheduler.cancel.test.ts | 18 ++++++++++++++++++ src/orchestration/taskScheduler.ts | 8 +++++++- 2 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/orchestration/taskScheduler.cancel.test.ts b/src/orchestration/taskScheduler.cancel.test.ts index ba4d3a7..4c9c37c 100644 --- a/src/orchestration/taskScheduler.cancel.test.ts +++ b/src/orchestration/taskScheduler.cancel.test.ts @@ -1,4 +1,6 @@ import { describe, it, expect, beforeEach } from 'vitest'; +import { homedir } from 'node:os'; +import { resolve } from 'node:path'; import { TaskScheduler } from './taskScheduler.js'; import type { TaskItem } from './decisionEngine.js'; import type { PipelineResult } from '../agents/pairPipeline.js'; @@ -58,6 +60,22 @@ describe('TaskScheduler cancellation', () => { expect(b.wasAborted()).toBe(false); }); + it('cancelProjectTasks matches home-expanded project paths', () => { + const d = deferredExecutor(); + sched.startTask(task('home'), resolve(homedir(), 'dev/WAVE'), d.exec); + const n = sched.cancelProjectTasks('~/dev/WAVE'); + expect(n).toBe(1); + expect(d.wasAborted()).toBe(true); + }); + + it('cancelProjectTasks matches relative project paths', () => { + const d = deferredExecutor(); + sched.startTask(task('relative'), resolve(process.cwd(), 'relative-WAVE'), d.exec); + const n = sched.cancelProjectTasks('./relative-WAVE'); + expect(n).toBe(1); + expect(d.wasAborted()).toBe(true); + }); + it("a cancelled result is not counted as failed", async () => { const d = deferredExecutor(); const events: string[] = []; diff --git a/src/orchestration/taskScheduler.ts b/src/orchestration/taskScheduler.ts index 65a004c..7de1ca4 100644 --- a/src/orchestration/taskScheduler.ts +++ b/src/orchestration/taskScheduler.ts @@ -4,11 +4,17 @@ // ============================================ import { EventEmitter } from 'node:events'; +import { homedir } from 'node:os'; +import { resolve } from 'node:path'; import type { TaskItem } from './decisionEngine.js'; import type { PipelineResult } from '../agents/pairPipeline.js'; function normalizeProjectPath(path: string): string { - const normalized = path.replace(/\\/g, '/').replace(/\/+$/g, ''); + const slashed = path.replace(/\\/g, '/'); + const expanded = slashed === '~' || slashed.startsWith('~/') + ? `${homedir()}${slashed.slice(1)}` + : slashed; + const normalized = resolve(expanded).replace(/\\/g, '/').replace(/\/+$/g, ''); return normalized || '/'; }