[GH-ISSUE #6678] [Bug] Reference-source import ignores require.resolve search-path options #4057

Closed
opened 2026-10-31 00:46:26 +00:00 by bsakaguchi · 0 comments
Owner

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

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

The public import-reference-source CLI ignores the second argument of an ordinary CommonJS require.resolve call. It records the default sibling dep.cjs as a parsed internal dependency even when { paths: [__dirname + "/sub"] } selects sub/dep.cjs. With { paths: [] }, Node reports MODULE_NOT_FOUND, but REA still asserts that same internal sibling.

The CLI exited 0 with empty stderr and no parse failures. All fixture files were hashed. The graph remained globally partial because of the usual pathname-race limitation; the individual relationships still claimed resolution: "internal" and parse_state: "parsed".

Reproduction

From an already built checkout with locked dependencies, the executed Python driver created this complete local fixture:

default.cjs:
console.log(require("node:path").relative(__dirname, require.resolve("./dep.cjs")));

same-base.cjs:
console.log(require("node:path").relative(__dirname, require.resolve("./dep.cjs", { paths: [__dirname] })));

other-base.cjs:
console.log(require("node:path").relative(__dirname, require.resolve("./dep.cjs", { paths: [__dirname + "/sub"] })));

empty-paths.cjs:
try { console.log(require.resolve("./dep.cjs", { paths: [] })); } catch (error) { console.log(error.code); }

dep.cjs and sub/dep.cjs, each:
throw new Error("This dependency must not execute");

The two dependency files in the executed fixture used distinct root/sub wording in their throw messages; neither executed. With ROOT as cwd and isolated HOME/XDG/TMPDIR directories, the driver invoked:

node REA_CHECKOUT/scripts/rea.mjs import-reference-source ROOT --json
node ROOT/default.cjs
node ROOT/same-base.cjs
node ROOT/other-base.cjs
node ROOT/empty-paths.cjs

Here ROOT and REA_CHECKOUT stand for the absolute disposable fixture and prepared checkout paths. These are the process calls actually made, with those path prefixes replaced. Each Node control exited 0 with empty stderr. One combined CLI import and its four controls were run; no post-draft reproduction was performed.

Expected behavior

Preserve require.resolve search-path semantics or mark the relationship unresolved/unknown when those options cannot be interpreted. Do not assert a default sibling after discarding result-affecting options. The no-options and same-base controls should retain their existing internal target.

Contract support is specific: REA's literal-member regression explicitly supports require.resolve; its loader-identity regression distinguishes attributable resolution from false dependencies. Node's API contract defines options.paths as replacing default lookup paths, and the executed controls establish its meaning for these exact relative requests. REA's uncertainty rule supplies the fallback contract. This report does not require evaluating arbitrary JavaScript options or adding a new resolver capability.

Impact and scope

P2: valid CommonJS input produces false historical dependency facts. The demonstrated scope is this existing member-call API with per-call search paths. Original file evidence remains available. Real-world frequency, MCP projection, and downstream comparison effects were not measured. No source mutation occurred. A result-preserving REA workaround was not established.

REA version

CLI --version: 6.7.0; source revision a7f967cf240eba8309cbf4526b2fc6338a52a617, with the prepared compiled runtime and locked dependencies.

Runtime and platform

Node.js v24.22.1, Linux x86_64. No external provider or third-party source was executed.

Target and artifact context

Self-authored local CommonJS tree with four resolver-only entry scripts and two dependency candidates. No package manifest, custom loader, Git repository, or exclusion is needed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed Node stdout:

default.cjs:     dep.cjs
same-base.cjs:   dep.cjs
other-base.cjs:  sub/dep.cjs
empty-paths.cjs: MODULE_NOT_FOUND

REA emitted this shape for all four sources, substituting each source filename:

{"from_path":"other-base.cjs","to":"dep.cjs","kind":"requires","resolution":"internal","parse_state":"parsed"}

Evidence provenance: observed public CLI plus native resolve-only controls. Local verification: reproduced. Reproduction completeness: complete for this fixture. Existing member-call tests cover literal arguments and mutations, but not a second-argument search-path override.

Typed outcome or recovery expected

For other-base.cjs, identify sub/dep.cjs or retain explicit resolution uncertainty. For empty-paths.cjs, do not claim that dep.cjs was resolved. Keep both default controls and all original hashes. These are proposed restoration assertions, not results of a fix.

Analysis or suspected cause

Source-derived: the parser reads only call.arguments[0] before emitting a runtime-require relationship. The resolver then uses the importing file's directory. The second argument and the resulting uncertainty are lost between these owners.

Additional context

Live open and closed issue searches found no report for require.resolve search-path options. #6612 concerns a loader base supplied to createRequire; this fixture uses the ordinary CommonJS wrapper and overrides resolution per call. Recovering createRequire bases alone would not repair it. Closed #3393 concerns a replaced resolver property; this fixture keeps Node's resolver unchanged. No last-known-good revision was tested.

Prepared with AI assistance.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Executed valid local CommonJS inputs produce definite internal relationships to the wrong file or to a file Node does not resolve. The demonstrated defect is bounded historical-graph correctness, with source evidence retained.
  • Workaround status: unverified
  • Workaround basis: Node controls identify the correct interpretation, but no REA workflow restoring the saved graph while preserving the original source was verified.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6678 ## Before submitting - [x] I searched the open and closed issues for a related report. - [x] I removed credentials, tokens, private paths, and sensitive target data. ## Area Evidence, artifacts, or reconstruction ## Observed behavior The public `import-reference-source` CLI ignores the second argument of an ordinary CommonJS `require.resolve` call. It records the default sibling `dep.cjs` as a parsed internal dependency even when `{ paths: [__dirname + "/sub"] }` selects `sub/dep.cjs`. With `{ paths: [] }`, Node reports `MODULE_NOT_FOUND`, but REA still asserts that same internal sibling. The CLI exited 0 with empty stderr and no parse failures. All fixture files were hashed. The graph remained globally partial because of the usual pathname-race limitation; the individual relationships still claimed `resolution: "internal"` and `parse_state: "parsed"`. ## Reproduction From an already built checkout with locked dependencies, the executed Python driver created this complete local fixture: ```text default.cjs: console.log(require("node:path").relative(__dirname, require.resolve("./dep.cjs"))); same-base.cjs: console.log(require("node:path").relative(__dirname, require.resolve("./dep.cjs", { paths: [__dirname] }))); other-base.cjs: console.log(require("node:path").relative(__dirname, require.resolve("./dep.cjs", { paths: [__dirname + "/sub"] }))); empty-paths.cjs: try { console.log(require.resolve("./dep.cjs", { paths: [] })); } catch (error) { console.log(error.code); } dep.cjs and sub/dep.cjs, each: throw new Error("This dependency must not execute"); ``` The two dependency files in the executed fixture used distinct root/sub wording in their throw messages; neither executed. With `ROOT` as cwd and isolated HOME/XDG/TMPDIR directories, the driver invoked: ```sh node REA_CHECKOUT/scripts/rea.mjs import-reference-source ROOT --json node ROOT/default.cjs node ROOT/same-base.cjs node ROOT/other-base.cjs node ROOT/empty-paths.cjs ``` Here `ROOT` and `REA_CHECKOUT` stand for the absolute disposable fixture and prepared checkout paths. These are the process calls actually made, with those path prefixes replaced. Each Node control exited 0 with empty stderr. One combined CLI import and its four controls were run; no post-draft reproduction was performed. ## Expected behavior Preserve `require.resolve` search-path semantics or mark the relationship unresolved/unknown when those options cannot be interpreted. Do not assert a default sibling after discarding result-affecting options. The no-options and same-base controls should retain their existing internal target. Contract support is specific: REA's [literal-member regression](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/tests/boundary/filesystem/referenceSourceComputedRequire.test.ts#L12-L21) explicitly supports `require.resolve`; its [loader-identity regression](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/tests/boundary/filesystem/referenceSourceShadowedRequire.test.ts#L282-L300) distinguishes attributable resolution from false dependencies. [Node's API contract](https://nodejs.org/docs/v24.22.1/api/modules.html#requireresolverequest-options) defines `options.paths` as replacing default lookup paths, and the executed controls establish its meaning for these exact relative requests. REA's [uncertainty rule](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/AGENTS.md#L19-L21) supplies the fallback contract. This report does not require evaluating arbitrary JavaScript options or adding a new resolver capability. ## Impact and scope P2: valid CommonJS input produces false historical dependency facts. The demonstrated scope is this existing member-call API with per-call search paths. Original file evidence remains available. Real-world frequency, MCP projection, and downstream comparison effects were not measured. No source mutation occurred. A result-preserving REA workaround was not established. ## REA version CLI `--version`: `6.7.0`; source revision `a7f967cf240eba8309cbf4526b2fc6338a52a617`, with the prepared compiled runtime and locked dependencies. ## Runtime and platform Node.js `v24.22.1`, Linux x86_64. No external provider or third-party source was executed. ## Target and artifact context Self-authored local CommonJS tree with four resolver-only entry scripts and two dependency candidates. No package manifest, custom loader, Git repository, or exclusion is needed. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Observed Node stdout: ```text default.cjs: dep.cjs same-base.cjs: dep.cjs other-base.cjs: sub/dep.cjs empty-paths.cjs: MODULE_NOT_FOUND ``` REA emitted this shape for **all four** sources, substituting each source filename: ```json {"from_path":"other-base.cjs","to":"dep.cjs","kind":"requires","resolution":"internal","parse_state":"parsed"} ``` Evidence provenance: observed public CLI plus native resolve-only controls. Local verification: reproduced. Reproduction completeness: complete for this fixture. Existing member-call tests cover literal arguments and mutations, but not a second-argument search-path override. ## Typed outcome or recovery expected For `other-base.cjs`, identify `sub/dep.cjs` or retain explicit resolution uncertainty. For `empty-paths.cjs`, do not claim that `dep.cjs` was resolved. Keep both default controls and all original hashes. These are proposed restoration assertions, not results of a fix. ## Analysis or suspected cause Source-derived: [the parser](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/domain/referenceSourceImportParsing.ts#L286-L299) reads only `call.arguments[0]` before emitting a runtime-require relationship. The [resolver](https://github.com/morluto/rea/blob/a7f967cf240eba8309cbf4526b2fc6338a52a617/src/application/ReferenceSourceImportEntries.ts#L269-L279) then uses the importing file's directory. The second argument and the resulting uncertainty are lost between these owners. ## Additional context Live open and closed issue searches found no report for `require.resolve` search-path options. [#6612](https://github.com/morluto/rea/issues/6612) concerns a loader base supplied to `createRequire`; this fixture uses the ordinary CommonJS wrapper and overrides resolution per call. Recovering `createRequire` bases alone would not repair it. Closed [#3393](https://github.com/morluto/rea/issues/3393) concerns a replaced resolver property; this fixture keeps Node's resolver unchanged. No last-known-good revision was tested. Prepared with AI assistance. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Executed valid local CommonJS inputs produce definite internal relationships to the wrong file or to a file Node does not resolve. The demonstrated defect is bounded historical-graph correctness, with source evidence retained. - Workaround status: unverified - Workaround basis: Node controls identify the correct interpretation, but no REA workflow restoring the saved graph while preserving the original source was verified.
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#4057
No description provided.