Repository navigation
Description
Found while building .squad/e2e/windows-test-baseline.md (EECOM, baseline commit 369bba8f).
Summary
LocalPollingProvider.execute() parses a script task's ref by splitting on whitespace with no quote or escape handling. Any command whose executable path contains a space is therefore unrunnable — which on Windows includes the default Node install (C:\Program Files\nodejs\node.exe) and anything under C:\Program Files.
This is a genuine cross-platform defect. It was previously categorized as "scheduler timing", which it is not.
Mechanism
packages/squad-sdk/src/runtime/scheduler.ts, LocalPollingProvider.execute, case 'script':
const argv = entry.task.ref.trim().split(/\s+/);
const command = argv[0]!;
const args = argv.slice(1);
const { stdout } = await execFileAsync(command, args, { ... });Given ref = "C:\Program Files\nodejs\node.exe -e console.log('hi')" this yields:
command = "C:\Program"args = ["Files\nodejs\node.exe", "-e", "console.log('hi')"]
execFile then fails with ENOENT. Because the failure is a spawn failure rather than a non-zero exit, the "rich error capture" path surfaces code: undefined and stderr: '' — so the provider reports a failure that carries no diagnostic information at all.
Linux CI never sees this because process.execPath there is /usr/bin/node — no spaces.
Controlled proof
test/scheduler.test.ts builds its ref from process.execPath:
task: { type: 'script', ref: `${process.execPath} -e console.log('hello-from-scheduler')` },On Windows, 4 tests in Scheduler: LocalPollingProvider fail:
× should execute script tasks expected false to be true
× captures the exit code on a non-zero exit expected undefined to be 7
× captures stderr written by the child expected '' to contain 'boom-from-stderr'
× does not block the event loop expected false to be true
The failure signature — success: false, code: undefined, stderr: '' — is exactly a spawn ENOENT, not a script that ran and failed.
Suggested fix
Two independent problems, both worth addressing:
- Parsing. Replace the naive
split(/\s+/)with a parser that honours quoting, or change thetaskschema to accept a structured{ command, args[] }form so no parsing is needed. The structured form is preferable — it also removes an injection-adjacent surface and keeps the existingshell: falsesafety property.validateTaskRefwould need a matching update. - Error capture. In the
catch, distinguish spawn failure from non-zero exit and populate the result accordingly (e.g. surfaceerr.code === 'ENOENT'as a distinct, described failure). A block titled "rich error capture on script failure" that returnscode: undefined, stderr: ''for the most common real-world failure is misleading.
Success criteria
- A script task whose executable path contains spaces executes successfully (add a test using a path with a space, so it fails on Linux too if the parser regresses).
- All 4
Scheduler: LocalPollingProvidertests pass on Windows. - A spawn failure (
ENOENT) produces aTaskResultthat names the missing command, rather thancode: undefined/stderr: ''. - The new test fails against current
dev— verified red before green, per the standing rule.
Related
.squad/e2e/windows-test-baseline.mdrow 4.
Activity
docs(e2e): authoritative Windows test baseline at 369bba8 (#1797)
fix(sdk): support command paths containing spaces in scheduler script…