Skip to content

executor: replace custom spawn engine with execa wrapper - #20

Merged
HamzaHassanain merged 1 commit into
masterfrom
refactor/executor-execa
May 2, 2026
Merged

executor: replace custom spawn engine with execa wrapper#20
HamzaHassanain merged 1 commit into
masterfrom
refactor/executor-execa

Conversation

@HamzaHassanain

Copy link
Copy Markdown
Owner

Summary

  • Replaces the hand-rolled child_process.spawn plumbing in src/executor.ts with a thin wrapper over execa@5. Public surface (execute, executeWithRedirect, cleanup, registerTempFile, getTempFiles) is byte-for-byte identical, so every helper call site (helpers/{checker,validator,generator,solution,utils}.ts) is untouched.
  • src/executor.ts: 1003 → 466 lines. Gone: the cancellable-timeout dance, manual stdout/stderr collectors, close/error event wiring, and the resolve/reject relay through handleProcessClose / handleProcessError / handleTimeout.
  • Behavior preserved end-to-end: ulimit -v / -Xmx memory limits, MLE detection (exit 137, SIGABRT, bad_alloc / OutOfMemory / OOM / MemoryError in stderr), cross-platform process-tree kill (taskkill /T /F on Windows, process-group SIGKILL on Unix) layered on top of execa's per-process kill, all four callbacks (onSuccess / onError / onTimeout / onMemoryExceeded), Windows file-handle release delay, signal-handler cleanup.
  • tests/executor.test.ts rewritten to mock execa instead of child_process.spawn. Same behavioral coverage (47 tests, all the success/failure/timeout/MLE/redirect/cleanup/platform-specific assertions); the mock just resolves a fake ExecaReturnValue instead of emitting events on a fake ChildProcess.

Test plan

  • npm run build — clean
  • npm run lint — clean
  • npm run format:check — clean
  • npx vitest run726/726 passing across 20 files (47 in executor.test.ts)
  • Manual smoke: polyman new /tmp/x && cd /tmp/x && polyman download-testlib && polyman verify against a sample problem (Linux)
  • Windows smoke: TLE process tree-kill via taskkill /T /F (the gnarly path called out in NOTES.md)

🤖 Generated with Claude Code

Drop the hand-rolled cancellable-timeout / output-collector / close-handler
plumbing in src/executor.ts; delegate process spawning, timeout enforcement,
and output buffering to execa@5. Public surface (CommandExecutor.execute,
executeWithRedirect, cleanup, registerTempFile, getTempFiles) is unchanged so
no helper call sites had to move.

Behavior preserved:
  - ulimit -v / -Xmx memory-limit wrapping
  - MLE detection from exit 137, SIGABRT, and stderr patterns
  - cross-platform process-tree kill (taskkill /T /F on Windows,
    process-group SIGKILL on Unix) layered on top of execa's per-process kill
  - onSuccess / onError / onTimeout / onMemoryExceeded callback semantics
  - Windows file-handle release delay
  - SIGINT/SIGTERM/uncaught/unhandled cleanup hooks

tests/executor.test.ts rewritten to mock execa (default export) instead of
child_process.spawn. The behavioral surface area covered is identical; the
mock just stops emitting close/error events on a fake ChildProcess and
instead resolves a fake ExecaReturnValue.

src/executor.ts: 1003 -> 466 lines.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@HamzaHassanain
HamzaHassanain merged commit 6f3da00 into master May 2, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant