Skip to content

node:child_process: 57 surface methods still stubbed (ChildProcess.kill/disconnect/send + stdio + execFile) #2130

Closed

Description

@proggeramlug

Summary

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.

Examples: test-child-process-advanced-serialization-largebuffer.js, test-child-process-advanced-serialization-splitted-length-field.js, test-child-process-can-write-to-stdout.js.

What to do

Audit the child_process surface beyond what #1955 covered: ChildProcess instance methods (kill, disconnect, unref, ref, send), stdio socket wiring (stdin.write, stdout.on('data'), stderr.pipe), execFile/execFileSync paths, advanced serialization for IPC. Pair with #1852's net work where stdio sockets share lifecycle.

This is the largest single-API "stubbed-method" cluster in the radar — fully implementing it would lift c_p from 4% to a meaningful number.

Context: #800 sweep, perry 0.5.1032. Follow-up to #1780 (now closed).

Activity

  1. added
    bugConfirmed defect or regression
    parityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem
    on May 28, 2026
  2. proggeramlug commented on May 28, 2026

    @proggeramlug
    ContributorAuthor

    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.
    • fork + IPC: send/disconnect/connected work, '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: a Buffer sent over fork IPC arrives as a plain object under Perry vs a real Buffer under Node:

    fork(child, [], { serialization: 'advanced' }); child.send({ buf: Buffer.from([1,2,3,4,5]) })
    // Node child:  { isBuf: true,  len: 5, first: 1 }
    // Perry child: { isBuf: false, len: -1, first: -1 }
    

    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.

  3. proggeramlug commented on May 29, 2026

    @proggeramlug
    ContributorAuthor

    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.

  4. andrewtdiz commented on May 30, 2026

    @andrewtdiz
    Contributor

    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.

    Open child issues:

    Related but usually separate unless the PR intentionally adds a compiled-Perry child IPC harness:

    Evidence anchors:

    • 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.

    Suggested PR boundaries:

    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.

  5. andrewtdiz commented on May 31, 2026

    @andrewtdiz
    Contributor

    Current-main curated child_process baseline at 22824ba90 is green for the deterministic execution fixtures:

    $ ./run_parity_tests.sh --suite node-suite --module child_process
    Parity Pass:   7
    Parity Fail:   0
    Compile Fail:  0
    Report: /private/tmp/perry-node-compat-main/test-parity/reports/parity_report_20260531_152659.json

    Covered in this module run:

    async/captured-spawn-optional-args
    async/exec-encoding-buffer
    async/exec-error-fields
    sync/nonzero-throw
    sync/spawn-sync-encoding-buffer
    sync/spawn-sync-result-fields
    sync/sync-encoding-buffer
    

    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.

  6. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    Contributor

    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.

  7. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    Contributor

    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.

  8. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    Contributor

    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.

    Validation reports:

    • focused timeout fixture: /root/perry-worktrees/perry-node-child-process-timeout-killsignal/test-parity/reports/parity_report_20260603_142824.json
    • full curated child_process module: /root/perry-worktrees/perry-node-child-process-timeout-killsignal/test-parity/reports/parity_report_20260603_142901.json
  9. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    Contributor

    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.

  10. andrewtdiz commented on Jun 3, 2026

    @andrewtdiz
    Contributor

    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.

  11. andrewtdiz commented on Jun 4, 2026

    @andrewtdiz
    Contributor

    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.

  12. andrewtdiz commented on Jun 4, 2026

    @andrewtdiz
    Contributor

    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).

  13. andrewtdiz commented on Jun 4, 2026

    @andrewtdiz
    Contributor

    Fresh current-main check on origin/main 6408ae9: 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.
  14. proggeramlug commented on Jun 10, 2026

    @proggeramlug
    ContributorAuthor

    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_process parity: 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 — functions
    • stdin.write / stdout.on('data') / stderr.pipe — functions, stdio round-trips end-to-end
    • execFile / execFileSync — functions
    • send / disconnect — functions on fork (correctly undefined on plain spawn, matching Node); full IPC message round-trip + disconnect event fire byte-identical to Node 26

    Landed 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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugConfirmed defect or regressionparityCompatibility gap with Node.js, ECMAScript, or the supported ecosystem

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions