mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-26 04:15:48 -06:00
702e598e68
* fix(test): guard CLI process children against deadlock, not startup
`gateway-backed-exit.process.test.ts` raced a fixed 20s `execFile` deadline
against child startup. Each of its ~37 children cold-loads the whole CLI
command graph through TSX (~2.5s warm, ~16s on a cold checkout, 12-20s on a
loaded host), while every case asserts output, exit code, and state side
effects rather than latency. Under contention the deadline fired first and the
harness rethrew `execFile`'s opaque `Command failed` error with the captured
stdout/stderr discarded, so the failure named neither the deadline nor the
child's last startup step.
`help-exit.process.test.ts` already owned the correct shape: a deadlock guard
sized below the shared Vitest deadline whose failures embed both output tails.
Extract it to `cli-process-child.test-helpers.ts` and converge both suites on
one runner and one `CLI_PROCESS_DEADLOCK_GUARD_MS`, so the policy cannot drift
apart again.
Also fixes the `cron list` promptness case, which timed spawn-to-exit against
10s and therefore measured TSX startup. Its invariant is that a one-shot
command releases its Gateway socket once output is complete, so the clock now
starts at the first parseable JSON payload. That child also gains the
`NODE_DISABLE_COMPILE_CACHE` guard its siblings carry: it owns the NODE_OPTIONS
respawn, and CI's exported `NODE_COMPILE_CACHE` would stack a second detached
respawn on top whose inherited stdio pipes can outlive a killed parent.
The five two-child `it.each` cases split into one child each so a single guard
budget covers the file, and the hand-tuned per-case Vitest deadlines are gone.
* fix(test): release child pipes when the deadlock guard fires
The guard's SIGKILL reaches the launcher only. A respawning entrypoint hands its
stdio to a detached grandchild in its own process group, so that grandchild
survives the kill and keeps the runner's pipe ends open after the guard has
already rejected — the "still running with no output" stall this suite exists to
remove, reintroduced by its own guard. The cron case deliberately enables that
respawn, so the path is reachable, not hypothetical.
Release our ends of the pipes alongside the kill. Waiting for a process tree we
cannot reach would defeat a deadlock guard, so the orphan is left to die on EPIPE
or be reaped with the runner.
The regression drives the same topology: a launcher that hands stdio to a
detached grandchild which writes only after the guard fires. Pre-fix the runner
still receives that write ("expected [ 'launcher', 'launcherafter-guard' ] to
have a length of 1 but got 2"); post-fix it never arrives.
Reported by ClawSweeper on #129489.
18 lines
740 B
JavaScript
18 lines
740 B
JavaScript
// CLI process tests launch real Node+tsx children and must not contend with the
|
|
// shared CLI module graph. Keep the owned list explicit so full and focused runs agree.
|
|
export const cliProcessTestFiles = [
|
|
"src/cli/acp-cli-exit.process.test.ts",
|
|
"src/cli/cli-process-child.test-helpers.test.ts",
|
|
"src/cli/gateway-backed-exit.process.test.ts",
|
|
"src/cli/gateway-cli/shutdown-hard-exit.process.test.ts",
|
|
"src/cli/help-exit.process.test.ts",
|
|
"src/cli/hooks-cli.process.test.ts",
|
|
"src/cli/gateway-cli/run-loop.direct-stop-active-work.process.test.ts",
|
|
];
|
|
|
|
const cliProcessTestFileSet = new Set(cliProcessTestFiles);
|
|
|
|
export function isCliProcessTestFile(value) {
|
|
return cliProcessTestFileSet.has(value.replaceAll("\\", "/"));
|
|
}
|