[GH-ISSUE #2571] fix(javascript): child-process options disappear when args are omitted #886

Closed
opened 2026-10-15 18:29:18 +00:00 by bsakaguchi · 2 comments
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2571

Summary

When JavaScript semantic analysis reads an imported child-process call with a literal options object as the second argument, it reports that no environment was supplied. For spawn and fork, it also reports default stdio even when the source explicitly selects a mode. The same options produce correct metadata when an empty args array precedes them. This loses facts about supported calls while retaining a complete resolution result.

Steps to reproduce

  1. With the repository dependencies installed, add the following focused regression test as src/domain/javascript/childProcessOptionsRegression.test.ts. The fixture is analyzed as text and does not launch a child process.
import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";

it("recovers options when the args array is omitted", () => {
  const ir = analyzeJavaScriptSemantics(`
    import { spawn, execFile, fork } from "node:child_process";
    spawn("tool", { env: { A: "1" }, stdio: "pipe" });
    execFile("tool", { env: { A: "1" } });
    fork("worker.js", {
      env: { A: "1" },
      stdio: ["pipe", "pipe", "pipe", "ipc"],
    });
  `);
  expect(ir.childProcessSpawns.map(
    ({ method, environmentSupplied, stdioMode, resolution }) =>
      ({ method, environmentSupplied, stdioMode, resolution }),
  )).toEqual([
    { method: "spawn", environmentSupplied: true, stdioMode: "pipe", resolution: "complete" },
    { method: "execFile", environmentSupplied: true, stdioMode: "default", resolution: "complete" },
    { method: "fork", environmentSupplied: true, stdioMode: "array", resolution: "complete" },
  ]);
});
  1. Run the focused test.
npm run test:focused -- src/domain/javascript/childProcessOptionsRegression.test.ts
  1. Compare the returned metadata with the expected values. For a discriminating control, insert [] between the first argument and the options object in each call, then analyze the modified fixture.

Expected behavior

The analyzer recognizes the supported form that omits the optional args array and reads the literal options object. Each call reports environmentSupplied: true. The spawn call reports stdioMode: "pipe", and the fork call reports stdioMode: "array". The execFile call has no stdio option and retains stdioMode: "default". Supplying an explicit empty args array should preserve these results.

Actual behavior

The verifier ran a focused domain test with this input. All three calls reported environmentSupplied: false, stdioMode: "default", and resolution: "complete". The options assertion failed. Two controls passed: explicit empty args arrays produced the expected metadata, and calls with no options retained absent environments and default stdio. The test runner exited with status 1 because of the failing assertion.

Evidence

  • Expected source: Node v26 child-process documentation, the spawn, execFile, and fork signatures with optional args; src/domain/javascript/javascriptSemanticIr.ts:262 defines the child-process metadata, and src/domain/javascript/javascriptSemanticAnalysis.native_bindings.test.ts:6 checks literal env and stdio recovery when an args array is present. The installed Node implementation also accepts object second arguments for all three methods; execFile does not accept stdio as an effective option.
  • Failure source: src/domain/javascript/javascriptSemanticChildProcesses.ts:328 selects the third argument for all three methods. src/domain/javascript/javascriptSemanticChildProcesses.ts:124 derives the environment and stdio fields from that selected object and sets complete resolution from the literal command. The focused execution observed false/default/complete for each second-argument call, while the explicit-args controls returned true/pipe/complete, true/default/complete, and true/array/complete respectively.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Imported-binding admission was checked at src/domain/javascript/javascriptSemanticChildProcesses.ts:265. The named imports in the fixture qualify as child-process calls, so the failure is not caused by an unbound global or an unrelated function.

Restoration check

The unchanged second-argument fixture must produce the expected environment and stdio metadata. Retain the explicit empty-args control to prove that both supported call forms agree. Also retain calls with omitted options so that restoring option recovery does not invent an environment or explicit stdio where none was supplied. Preserve complete resolution for these literal supported calls.

Runtime or environment

  • Failure verified at a2d8b94dd5 and still present at 8b181716ac
  • Node.js: v26.10.0
  • npm: 11.19.1
  • Vitest: 5.0.3
  • Verification scope: the domain semantic-analysis entry point; no child process from the analyzed fixture was executed.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Valid calls receive incorrect environment and stdio metadata, which can mislead analysis of child-process behavior. The reproduced loss is limited to the no-args options form; no crash, data loss, or severe workflow failure was demonstrated.
  • Workaround status: available
  • Workaround basis: For editable analysis input, an explicit empty args array before the options object restored the expected metadata in the focused controls. This does not establish an available workaround for immutable packaged targets.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2571 ## Summary When JavaScript semantic analysis reads an imported child-process call with a literal options object as the second argument, it reports that no environment was supplied. For spawn and fork, it also reports default stdio even when the source explicitly selects a mode. The same options produce correct metadata when an empty args array precedes them. This loses facts about supported calls while retaining a complete resolution result. ## Steps to reproduce 1. With the repository dependencies installed, add the following focused regression test as `src/domain/javascript/childProcessOptionsRegression.test.ts`. The fixture is analyzed as text and does not launch a child process. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; it("recovers options when the args array is omitted", () => { const ir = analyzeJavaScriptSemantics(` import { spawn, execFile, fork } from "node:child_process"; spawn("tool", { env: { A: "1" }, stdio: "pipe" }); execFile("tool", { env: { A: "1" } }); fork("worker.js", { env: { A: "1" }, stdio: ["pipe", "pipe", "pipe", "ipc"], }); `); expect(ir.childProcessSpawns.map( ({ method, environmentSupplied, stdioMode, resolution }) => ({ method, environmentSupplied, stdioMode, resolution }), )).toEqual([ { method: "spawn", environmentSupplied: true, stdioMode: "pipe", resolution: "complete" }, { method: "execFile", environmentSupplied: true, stdioMode: "default", resolution: "complete" }, { method: "fork", environmentSupplied: true, stdioMode: "array", resolution: "complete" }, ]); }); ``` 2. Run the focused test. ```sh npm run test:focused -- src/domain/javascript/childProcessOptionsRegression.test.ts ``` 3. Compare the returned metadata with the expected values. For a discriminating control, insert `[]` between the first argument and the options object in each call, then analyze the modified fixture. ## Expected behavior The analyzer recognizes the supported form that omits the optional args array and reads the literal options object. Each call reports `environmentSupplied: true`. The spawn call reports `stdioMode: "pipe"`, and the fork call reports `stdioMode: "array"`. The execFile call has no stdio option and retains `stdioMode: "default"`. Supplying an explicit empty args array should preserve these results. ## Actual behavior The verifier ran a focused domain test with this input. All three calls reported `environmentSupplied: false`, `stdioMode: "default"`, and `resolution: "complete"`. The options assertion failed. Two controls passed: explicit empty args arrays produced the expected metadata, and calls with no options retained absent environments and default stdio. The test runner exited with status 1 because of the failing assertion. ## Evidence - Expected source: [Node v26 child-process documentation](https://nodejs.org/docs/latest-v26.x/api/child_process.html), the spawn, execFile, and fork signatures with optional args; `src/domain/javascript/javascriptSemanticIr.ts:262` defines the child-process metadata, and `src/domain/javascript/javascriptSemanticAnalysis.native_bindings.test.ts:6` checks literal env and stdio recovery when an args array is present. The installed Node implementation also accepts object second arguments for all three methods; execFile does not accept stdio as an effective option. - Failure source: `src/domain/javascript/javascriptSemanticChildProcesses.ts:328` selects the third argument for all three methods. `src/domain/javascript/javascriptSemanticChildProcesses.ts:124` derives the environment and stdio fields from that selected object and sets complete resolution from the literal command. The focused execution observed false/default/complete for each second-argument call, while the explicit-args controls returned true/pipe/complete, true/default/complete, and true/array/complete respectively. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Imported-binding admission was checked at `src/domain/javascript/javascriptSemanticChildProcesses.ts:265`. The named imports in the fixture qualify as child-process calls, so the failure is not caused by an unbound global or an unrelated function. ## Restoration check The unchanged second-argument fixture must produce the expected environment and stdio metadata. Retain the explicit empty-args control to prove that both supported call forms agree. Also retain calls with omitted options so that restoring option recovery does not invent an environment or explicit stdio where none was supplied. Preserve complete resolution for these literal supported calls. ## Runtime or environment - Failure verified at a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and still present at 8b181716aca2cb467be499cba226abfb5ae19141 - Node.js: v26.10.0 - npm: 11.19.1 - Vitest: 5.0.3 - Verification scope: the domain semantic-analysis entry point; no child process from the analyzed fixture was executed. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Valid calls receive incorrect environment and stdio metadata, which can mislead analysis of child-process behavior. The reproduced loss is limited to the no-args options form; no crash, data loss, or severe workflow failure was demonstrated. - Workaround status: available - Workaround basis: For editable analysis input, an explicit empty args array before the options object restored the expected metadata in the focused controls. This does not establish an available workaround for immutable packaged targets.
bsakaguchi 2026-10-15 18:29:18 +00:00
Author
Owner

@xu91102 commented on GitHub (Oct 13, 2026):

Reproduced on current main d13c9f840d36a321a56917e75c48afcb8d72920e through both the compiled CLI and production stdio MCP, beyond the original domain-only reproduction.

The real Node control launches test-owned children using spawn(command, options), execFile(command, options), and fork(module, options) without args arrays. All three receive their explicitly supplied environment values and exit normally. The analyzer reports environment_supplied: false and stdio_mode: "default" for the short forms; adding an explicit [] preserves the supplied environment and pipe/array modes.

Both public regression cases currently fail after the Node controls pass. The regression also retains explicit-args and omitted-options controls and checks the supplies-env relations. I am preparing a producer-level fix to the overload selection, with no schema or execution-authority change.

<!-- gh-comment-id:6143213751 --> @xu91102 commented on GitHub (Oct 13, 2026): Reproduced on current main `d13c9f840d36a321a56917e75c48afcb8d72920e` through both the compiled CLI and production stdio MCP, beyond the original domain-only reproduction. The real Node control launches test-owned children using `spawn(command, options)`, `execFile(command, options)`, and `fork(module, options)` without args arrays. All three receive their explicitly supplied environment values and exit normally. The analyzer reports `environment_supplied: false` and `stdio_mode: "default"` for the short forms; adding an explicit `[]` preserves the supplied environment and pipe/array modes. Both public regression cases currently fail after the Node controls pass. The regression also retains explicit-args and omitted-options controls and checks the `supplies-env` relations. I am preparing a producer-level fix to the overload selection, with no schema or execution-authority change.
Author
Owner

@morluto commented on GitHub (Oct 14, 2026):

Static child-process analysis now recognizes options in the overload where the arguments array is omitted, while preserving explicit-argument behavior and leaving absent options unknown. The regression controls pass on current main.

Root cause: Spawn option extraction always selected arguments[2], ignoring Node's supported second-argument options overload when args is omitted.

Validation: src/domain/javascript/childProcessOptionsRegression.test.ts: 'detects options without an args array' checks spawn, execFile, and fork second-argument options, explicit-args controls, and omitted-options defaults. These assertions passed in CI at 500e32399; the reviewed implementation is present on current main. Fix: c8e85b962.

Verification scope: Static source analysis only; no child process from the fixture is launched.

<!-- gh-comment-id:6161946652 --> @morluto commented on GitHub (Oct 14, 2026): Static child-process analysis now recognizes options in the overload where the arguments array is omitted, while preserving explicit-argument behavior and leaving absent options unknown. The regression controls pass on current main. Root cause: Spawn option extraction always selected arguments[2], ignoring Node's supported second-argument options overload when args is omitted. Validation: src/domain/javascript/childProcessOptionsRegression.test.ts: 'detects options without an args array' checks spawn, execFile, and fork second-argument options, explicit-args controls, and omitted-options defaults. These assertions passed in [CI at 500e32399](https://github.com/morluto/rea/actions/runs/38514789942); the reviewed implementation is present on current main. Fix: [c8e85b962](https://github.com/morluto/rea/commit/c8e85b962). Verification scope: Static source analysis only; no child process from the fixture is launched.
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#886
No description provided.