You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
node:child_process is at ~4% parity. #1780 closed via #1955 etc. (async spawn reactor, fork+IPC, exec/spawnSync encoding parity), but 57 tests still fail with "value is not a function" — large parts of the API surface remain stubbed.
Triage: the "57 stubbed methods" count is stale — common surface now passes
On current main (post #1955 and the subsequent node:net/child_process work) the common child_process surface matches Node byte-for-byte in direct comparisons (node vs the Perry-compiled program):
spawn ChildProcess: pid/kill/ref/unref are functions, send/disconnect correctly undefined (no IPC), connected=false, killed boolean, exitCode/signalCode/spawnfile correct; stdout.on('data'), stderr, and close all fire.
stdin: child.stdin.write(...) + .end() reach the child (echo round-trips); child.stdio is an array with the expected socket objects.
spawnSync/execFileSync/execSync/exec/execFile: status/stdout/stdio/pid/signal/output all match.
So the bulk of the original 57 "value is not a function" failures appear already closed; the radar count predates that work (issue cites perry 0.5.1032).
The one concrete remaining gap I could reproduce
serialization: 'advanced' loses type fidelity: a Buffer sent over fork IPC arrives as a plain object under Perry vs a real Buffer under Node:
Perry's IPC is newline-delimited JSON (child_process/fork.rs, reactor.rs::cp_ipc_send), and the serialization option is ignored. Implementing 'advanced' is non-trivial here because forked children run under node (exec_path defaults to "node"), so the wire format would have to match V8's structured-clone serialization (length-prefixed binary, versioned) — which is what test-child-process-advanced-serialization-* exercises (largebuffer, splitted-length-field framing).
Suggested re-scope: close out the already-working surface (optionally lock it in with a hand-authored node-suite case), and track advanced/V8 IPC serialization as the focused remaining sub-issue.
Addressed the concrete remaining gap (serialization: 'advanced' IPC) in #2453.
Confirmed the triage: the common surface (spawn streams, stdin/stdout/stderr, kill/ref/unref/send/disconnect, fork+json IPC, exec*/spawnSync) was already byte-for-byte. The PR adds a V8 structured-clone codec (child_process/v8_serde.rs) on the parent side so fork(..., { serialization: 'advanced' }) interoperates with the real node child — Buffers/TypedArrays keep full type fidelity in both directions, including the largebuffer and splitted-length-field framing cases. Verified byte-for-byte vs node --experimental-strip-types.
Current PR-cut index for this tracker after the merged child-process execution work:
The original “57 surface methods still stubbed” snapshot is stale. Merged PRs have added streaming spawn(), fork() IPC, exec / execFile / sync result and error fields, captured callable exports, and advanced IPC serialization. The remaining work should be treated as focused leaves under a node:child_process: subprocess lifecycle and setup parity cut boundary.
docs/runtime-parity-gaps.md:1701-1734 still lists many ChildProcess members/events as missing; some rows are stale because current source now constructs live stdio streams, lifecycle fields, and event methods.
crates/perry-runtime/src/child_process/reactor.rs:405-515 constructs streaming spawn() ChildProcess objects with kill, ref, unref, stdio streams, lifecycle fields, and spawnargs / spawnfile.
crates/perry-runtime/src/child_process/fork.rs:64-129 constructs forked ChildProcess objects with stdio, send, disconnect, connected, channel, and IPC launch setup.
crates/perry-runtime/src/child_process/mod.rs:1116-1124 documents that only cwd, env, and shell options are currently applied; stdio, timeout, maxBuffer, and related options remain ignored.
crates/perry-runtime/src/child_process/mod.rs:873-882 implements subprocess.send as a two-argument closure that ignores callback/options/handle behavior and always returns true.
test-files/test_parity_child_process.ts:101-155 probes ChildProcess shape/events and explicitly skips subprocess[Symbol.dispose]().
crates/perry-api-manifest/src/entries.rs:2615-2629 registers the public child-process execution helpers plus ChildProcess / Stream, but does not register _forkChild.
Non-goals: do not redo merged execution work from PRs 1858, 1926, 1955, 2170, 2453, or 2681; do not use this tracker for unrelated node:process APIs except the explicitly related child-side IPC follow-up #2529; do not batch with broad subsystem rewrites or unrelated modules.
This supports keeping the original broad “57 stubbed methods” snapshot narrowed to the existing leaf issues. It does not close the remaining option-semantics/default-export/_forkChild/disposer work; those surfaces are either not covered by this curated module slice or are tracked by the open child issues listed above. No Test262 coverage was involved.
I am taking a focused child-process setup/lifecycle parity leaf that belongs under the narrowed remaining surface here and the concrete option tracker #2555.
Planned PR cut: timeout termination signal handling for child_process paths that already exist in Perry: buffered exec() / execFile(), sync spawnSync() / execSync() / execFileSync(), and live spawn() / fork() timeout termination. This keeps the work in the subprocess option/lifecycle boundary and avoids redoing the already-merged spawn/fork/IPC execution work.
Added a child_process compatibility cut in draft PR #4262.
This PR focuses on timeout termination semantics across the shared child_process surface: spawnSync, execFileSync, buffered exec/execFile, live spawn, and fork now honor killSignal instead of hard-coding or defaulting observable timeout behavior to SIGTERM. It also adds a Node parity fixture for the covered paths.
Validation passed: cargo check -p perry-runtime, focused timeout-killsignal parity on Node v26, adjacent child_process parity checks for buffered limits, sync options, spawnSync result fields, and captured spawn optional args, plus fmt/diff/file-size checks.
Opened draft PR #4262 for the timeout/killSignal child_process leaf under this narrowed tracker: #4262
This does not revisit the already-merged execution/stdio/IPC work. It covers timeout termination signal reporting for existing sync, buffered async, live spawn, and fork paths, with the full curated child_process module passing locally on Node 26.
full curated child_process module: /root/perry-worktrees/perry-node-child-process-timeout-killsignal/test-parity/reports/parity_report_20260603_142901.json
I am taking another narrowed child-process lifecycle/setup leaf that belongs under this tracker and the concrete option tracker #2555.
Planned PR cut: AbortSignal support for the child_process paths Perry already implements: live spawn() / fork() and buffered exec() / execFile(). The intended behavior is Node-shaped options.signal validation, pre-aborted and later-aborted termination, AbortError [ABORT_ERR] error events/callback errors, and focused parity coverage for those lifecycle cases.
Non-goals: no redo of merged execution/stdio/IPC work, no numeric fd/custom-stream routing, no uid/gid/detached/platform flag work, and no broad validation sweep beyond options.signal.
Opened draft PR #4264 for the narrowed child-process AbortSignal lifecycle/setup leaf: #4264
This PR stays within the remaining child_process option/lifecycle surface. It validates options.signal, handles pre-aborted buffered exec() / execFile() callback errors, and wires live spawn() / fork() aborts through the existing reactor so error, killed, exit, and close metadata match Node for the covered cases.
Validation passed: focused AbortSignal parity, full curated child_process module parity, cargo check -p perry-runtime, fmt, diff check, and file-size check.
Non-goals remain the broader setup leaves: numeric fd/custom stream routing, uid/gid, detached/platform flags, and broad option validation outside options.signal.
I am taking another narrowed child_process setup/options leaf that belongs under this parent tracker and #2555.
Planned PR cut: custom stream handles in stdio arrays for existing spawn/fork/sync execution paths, using stream objects with open file descriptors and focused Node 26 parity coverage. This does not revisit the already-merged execution, IPC, timeout, AbortSignal, ignore/inherit, numeric-fd, uid/gid, detached, or argv0 slices.
Opened PR #4401 for the child_process stream-backed stdio slice: #4401
The change is scoped to fs stream objects with registered fds in stdio arrays and reuses the existing fd-backed routing path shared by live and sync child_process execution.
Validation: cargo check -p perry-runtime, release build for perry-runtime/perry, focused stdio-stream parity, adjacent stdio filters, and the full child_process parity module (26 pass, 0 fail).
Fresh current-main check on origin/main6408ae9: the full curated node-suite/child_process parity module is clean.
Validation command:
CARGO_TARGET_DIR=/root/perry-worktrees/perry-node-probe-20260604/target PERRY_NO_AUTO_OPTIMIZE=1 npm exec --yes --package=node@26 -- bash -lc './run_parity_tests.sh --suite node-suite --module child_process'\n\nResult:\n- 26 pass / 0 fail / 0 compile fail\n- Report: test-parity/reports/parity_report_20260604_223037.json\n\nThis supports the prior narrowing: the original broad "57 stubbed methods" snapshot is stale for the curated child_process surface. I am not opening a duplicate child_process PR from this scan.
Verified resolved on main (v0.5.1152). The original "57 stubbed methods / ~4% parity" snapshot is stale — it dated from the #800 sweep at perry 0.5.1032.
Activity
fix(child_process): reify spawn/spawnSync/execSync/fork/execFileSync …
proggeramlug commentedon May 28, 2026
Triage: the "57 stubbed methods" count is stale — common surface now passes
On current
main(post #1955 and the subsequentnode:net/child_processwork) the commonchild_processsurface matches Node byte-for-byte in direct comparisons (nodevs the Perry-compiled program):spawnChildProcess:pid/kill/ref/unrefare functions,send/disconnectcorrectlyundefined(no IPC),connected=false,killedboolean,exitCode/signalCode/spawnfilecorrect;stdout.on('data'),stderr, andcloseall fire.stdin:child.stdin.write(...)+.end()reach the child (echo round-trips);child.stdiois an array with the expected socket objects.fork+ IPC:send/disconnect/connectedwork,'message'and'disconnect'events fire, JSON ('json') serialization round-trips.spawnSync/execFileSync/execSync/exec/execFile: status/stdout/stdio/pid/signal/output all match.So the bulk of the original 57 "value is not a function" failures appear already closed; the radar count predates that work (issue cites perry 0.5.1032).
The one concrete remaining gap I could reproduce
serialization: 'advanced'loses type fidelity: aBuffersent overforkIPC arrives as a plain object under Perry vs a realBufferunder Node:Perry's IPC is newline-delimited JSON (
child_process/fork.rs,reactor.rs::cp_ipc_send), and theserializationoption is ignored. Implementing'advanced'is non-trivial here because forked children run undernode(exec_pathdefaults to"node"), so the wire format would have to match V8's structured-clone serialization (length-prefixed binary, versioned) — which is whattest-child-process-advanced-serialization-*exercises (largebuffer, splitted-length-field framing).Suggested re-scope: close out the already-working surface (optionally lock it in with a hand-authored
node-suitecase), and track advanced/V8 IPC serialization as the focused remaining sub-issue.proggeramlug commentedon May 29, 2026
Addressed the concrete remaining gap (
serialization: 'advanced'IPC) in #2453.Confirmed the triage: the common surface (
spawnstreams,stdin/stdout/stderr,kill/ref/unref/send/disconnect,fork+jsonIPC,exec*/spawnSync) was already byte-for-byte. The PR adds a V8 structured-clone codec (child_process/v8_serde.rs) on the parent side sofork(..., { serialization: 'advanced' })interoperates with the realnodechild — Buffers/TypedArrays keep full type fidelity in both directions, including thelargebufferandsplitted-length-fieldframing cases. Verified byte-for-byte vsnode --experimental-strip-types.feat(node:child_process): V8 'advanced' IPC serialization for fork() (#…
feat(node:child_process): V8 'advanced' IPC serialization for fork() (#…
andrewtdiz commentedon May 30, 2026
Current PR-cut index for this tracker after the merged child-process execution work:
The original “57 surface methods still stubbed” snapshot is stale. Merged PRs have added streaming
spawn(),fork()IPC,exec/execFile/ sync result and error fields, captured callable exports, and advanced IPC serialization. The remaining work should be treated as focused leaves under anode:child_process: subprocess lifecycle and setup parity cutboundary.Open child issues:
node:child_process: honor spawn and exec option semantics beyond cwd/env/shellnode:child_process: validate command, file, and args inputsnode:child_process: implement subprocess[Symbol.dispose] lifecycle methodnode:child_process: expose _forkChild IPC bootstrap helpernode:child_process: honor subprocess.send callback and closed-channel semanticsRelated but usually separate unless the PR intentionally adds a compiled-Perry child IPC harness:
node:process: implement child-side IPC channel surfaceEvidence anchors:
docs/runtime-parity-gaps.md:1701-1734still lists many ChildProcess members/events as missing; some rows are stale because current source now constructs live stdio streams, lifecycle fields, and event methods.crates/perry-runtime/src/child_process/reactor.rs:405-515constructs streamingspawn()ChildProcess objects withkill,ref,unref, stdio streams, lifecycle fields, andspawnargs/spawnfile.crates/perry-runtime/src/child_process/fork.rs:64-129constructs forked ChildProcess objects with stdio,send,disconnect,connected,channel, and IPC launch setup.crates/perry-runtime/src/child_process/mod.rs:1116-1124documents that onlycwd,env, andshelloptions are currently applied;stdio,timeout,maxBuffer, and related options remain ignored.crates/perry-runtime/src/child_process/mod.rs:873-882implementssubprocess.sendas a two-argument closure that ignores callback/options/handle behavior and always returnstrue.test-files/test_parity_child_process.ts:101-155probes ChildProcess shape/events and explicitly skipssubprocess[Symbol.dispose]().crates/perry-api-manifest/src/entries.rs:2615-2629registers the public child-process execution helpers plusChildProcess/Stream, but does not register_forkChild.Suggested PR boundaries:
spawn/exec/execFile/ sync helpers (node:child_process: honor spawn and exec option semantics beyond cwd/env/shell #2555, node:child_process: validate command, file, and args inputs #3079), or_forkChild, andsendobservable semantics (node:child_process: implement subprocess[Symbol.dispose] lifecycle method #2556, node:child_process: expose _forkChild IPC bootstrap helper #2717, node:child_process: honor subprocess.send callback and closed-channel semantics #3316).Non-goals: do not redo merged execution work from PRs 1858, 1926, 1955, 2170, 2453, or 2681; do not use this tracker for unrelated
node:processAPIs except the explicitly related child-side IPC follow-up #2529; do not batch with broad subsystem rewrites or unrelated modules.andrewtdiz commentedon May 31, 2026
Current-main curated child_process baseline at
22824ba90is green for the deterministic execution fixtures:Covered in this module run:
This supports keeping the original broad “57 stubbed methods” snapshot narrowed to the existing leaf issues. It does not close the remaining option-semantics/default-export/_forkChild/disposer work; those surfaces are either not covered by this curated module slice or are tracked by the open child issues listed above. No Test262 coverage was involved.
andrewtdiz commentedon Jun 3, 2026
I am taking a focused child-process setup/lifecycle parity leaf that belongs under the narrowed remaining surface here and the concrete option tracker #2555.
Planned PR cut: timeout termination signal handling for child_process paths that already exist in Perry: buffered
exec()/execFile(), syncspawnSync()/execSync()/execFileSync(), and livespawn()/fork()timeout termination. This keeps the work in the subprocess option/lifecycle boundary and avoids redoing the already-merged spawn/fork/IPC execution work.andrewtdiz commentedon Jun 3, 2026
Added a child_process compatibility cut in draft PR #4262.
This PR focuses on timeout termination semantics across the shared child_process surface:
spawnSync,execFileSync, bufferedexec/execFile, livespawn, andforknow honorkillSignalinstead of hard-coding or defaulting observable timeout behavior toSIGTERM. It also adds a Node parity fixture for the covered paths.Validation passed:
cargo check -p perry-runtime, focusedtimeout-killsignalparity on Node v26, adjacent child_process parity checks for buffered limits, sync options, spawnSync result fields, and captured spawn optional args, plus fmt/diff/file-size checks.andrewtdiz commentedon Jun 3, 2026
Opened draft PR #4262 for the timeout/killSignal child_process leaf under this narrowed tracker: #4262
This does not revisit the already-merged execution/stdio/IPC work. It covers timeout termination signal reporting for existing sync, buffered async, live spawn, and fork paths, with the full curated
child_processmodule passing locally on Node 26.Validation reports:
/root/perry-worktrees/perry-node-child-process-timeout-killsignal/test-parity/reports/parity_report_20260603_142824.json/root/perry-worktrees/perry-node-child-process-timeout-killsignal/test-parity/reports/parity_report_20260603_142901.jsonandrewtdiz commentedon Jun 3, 2026
I am taking another narrowed child-process lifecycle/setup leaf that belongs under this tracker and the concrete option tracker #2555.
Planned PR cut: AbortSignal support for the child_process paths Perry already implements: live
spawn()/fork()and bufferedexec()/execFile(). The intended behavior is Node-shapedoptions.signalvalidation, pre-aborted and later-aborted termination,AbortError [ABORT_ERR]error events/callback errors, and focused parity coverage for those lifecycle cases.Non-goals: no redo of merged execution/stdio/IPC work, no numeric fd/custom-stream routing, no uid/gid/detached/platform flag work, and no broad validation sweep beyond
options.signal.andrewtdiz commentedon Jun 3, 2026
Opened draft PR #4264 for the narrowed child-process AbortSignal lifecycle/setup leaf: #4264
This PR stays within the remaining child_process option/lifecycle surface. It validates
options.signal, handles pre-aborted bufferedexec()/execFile()callback errors, and wires livespawn()/fork()aborts through the existing reactor soerror,killed,exit, andclosemetadata match Node for the covered cases.Validation passed: focused AbortSignal parity, full curated
child_processmodule parity,cargo check -p perry-runtime, fmt, diff check, and file-size check.Non-goals remain the broader setup leaves: numeric fd/custom stream routing, uid/gid, detached/platform flags, and broad option validation outside
options.signal.andrewtdiz commentedon Jun 4, 2026
I am taking another narrowed child_process setup/options leaf that belongs under this parent tracker and #2555.
Planned PR cut: custom stream handles in
stdioarrays for existing spawn/fork/sync execution paths, using stream objects with open file descriptors and focused Node 26 parity coverage. This does not revisit the already-merged execution, IPC, timeout, AbortSignal, ignore/inherit, numeric-fd, uid/gid, detached, or argv0 slices.andrewtdiz commentedon Jun 4, 2026
Opened PR #4401 for the child_process stream-backed stdio slice: #4401
The change is scoped to fs stream objects with registered fds in
stdioarrays and reuses the existing fd-backed routing path shared by live and sync child_process execution.Validation:
cargo check -p perry-runtime, release build forperry-runtime/perry, focusedstdio-streamparity, adjacent stdio filters, and the full child_process parity module (26 pass, 0 fail).andrewtdiz commentedon Jun 4, 2026
Fresh current-main check on
origin/main6408ae9: the full curatednode-suite/child_processparity module is clean.Validation command:
CARGO_TARGET_DIR=/root/perry-worktrees/perry-node-probe-20260604/target PERRY_NO_AUTO_OPTIMIZE=1 npm exec --yes --package=node@26 -- bash -lc './run_parity_tests.sh --suite node-suite --module child_process'\n\nResult:\n- 26 pass / 0 fail / 0 compile fail\n- Report:test-parity/reports/parity_report_20260604_223037.json\n\nThis supports the prior narrowing: the original broad "57 stubbed methods" snapshot is stale for the curated child_process surface. I am not opening a duplicate child_process PR from this scan.proggeramlug commentedon Jun 10, 2026
Verified resolved on main (v0.5.1152). The original "57 stubbed methods / ~4% parity" snapshot is stale — it dated from the #800 sweep at perry 0.5.1032.
Curated
node-suite/child_processparity: 26/26 = 100.0% (0 fail, 0 compile-fail).Direct probe of every method this tracker called out as stubbed, all now present and functional:
ChildProcess.kill,ref,unref— functionsstdin.write/stdout.on('data')/stderr.pipe— functions, stdio round-trips end-to-endexecFile/execFileSync— functionssend/disconnect— functions onfork(correctlyundefinedon plainspawn, matching Node); full IPC message round-trip +disconnectevent fire byte-identical to Node 26Landed via the narrowing PRs under this tracker: #1955 (spawn reactor / fork-IPC / exec), #4262 (timeout killSignal), #4264 (AbortSignal lifecycle), #4401 (stream-backed stdio). Closing as completed.
feat(runtime): real child_process spawn/exec ChildProcess handle sema…