[GH-ISSUE #6892] [Bug] Historical reference import silently drops optional require.resolve dependencies #4227

Closed
opened 2026-10-31 00:47:52 +00:00 by bsakaguchi · 1 comment
Owner

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

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 registered source CLI import-reference-source silently drops the dependency from both require.resolve?.("./dep.cjs") and require?.resolve("./dep.cjs") in an ordinary .cjs file. Both Node resolve-only controls identify the existing dep.cjs, exactly like the direct require.resolve("./dep.cjs") control. REA retains a parsed internal dependency only for the direct form.

All three imports exit 0, hash both files, and return no parse failures or entry-specific limitations. No overwritten member, local shadow, computed request or nullable custom loader is involved.

Reproduction

Tested once in a six-case probe at the revision below. The exact relevant fixture contents and CLI/Node operations appear in this standalone excerpt; only checkout/output paths and the surrounding case selection are parameterized. This excerpt was not separately rerun.

With the checkout's locked dependencies installed, save the script below as /tmp/rea-reference-optional-resolver.mjs, then run from that checkout:

work="$(mktemp -d)"
node --import ./node_modules/tsx/dist/loader.mjs \
  /tmp/rea-reference-optional-resolver.mjs "$PWD" "$work"
import { mkdir, writeFile } from 'node:fs/promises';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
import { execFileSync } from 'node:child_process';
const [repo, out] = process.argv.slice(2);
const { createCli, closeCliJavaScriptResources } =
  await import(pathToFileURL(join(repo, 'src/cli.ts')).href);
const cases = {
  cjs_control: {
    main: 'main.cjs',
    source: 'console.log(require.resolve("./dep.cjs"));\n',
  },
  optional_resolve: {
    main: 'main.cjs',
    source: 'console.log(require.resolve?.("./dep.cjs"));\n',
  },
  optional_receiver: {
    main: 'main.cjs',
    source: 'console.log(require?.resolve("./dep.cjs"));\n',
  },
};
try {
  for (const [name, fixture] of Object.entries(cases)) {
    const root = join(out, name);
    await mkdir(root, { recursive: true });
    await writeFile(join(root, fixture.main), fixture.source);
    await writeFile(join(root, 'dep.cjs'),
      'throw new Error("dependency must not execute");\n');
    if (fixture.manifest)
      await writeFile(join(root, 'package.json'),
        JSON.stringify(fixture.manifest));
    let stdout = '', exit = 0;
    await createCli({
      REA_LOG_LEVEL: 'silent', REA_REFERENCE_SECRET_PATTERNS_JSON: '[]',
    }).serve(['import-reference-source', root, '--json'], {
      env: {}, exit: code => { exit = code; },
      stdout: text => { stdout += text; },
    });
    const graph = JSON.parse(stdout);
    const oracle = execFileSync(process.execPath, [join(root, fixture.main)],
      { encoding: 'utf8' }).replaceAll(root, '$FIXTURE_ROOT').trim();
    console.log(JSON.stringify({
      case: name, exit, entries: graph.entries.map(entry => entry.path),
      relationships: graph.relationships,
      parse_failures: graph.parse_failures,
      entry_limitations: graph.entries.flatMap(entry => entry.limitations),
      oracle,
    }));
  }
} finally { await closeCliJavaScriptResources(); }

REA reads the inert fixtures. The separate Node controls execute only the self-authored main files and call the resolver, which never loads the throwing dependency.

Expected behavior

Retain the same historical dependency for these two exact, unmodified built-in resolver spellings as for the direct-call control, or record a specific unsupported-coverage limitation instead of silently losing the reference. The CommonJS wrapper supplies require, and its original resolve method is present; neither optional chain skips the lookup in this fixture.

The historical CommonJS contract includes require.resolve and distinguishes loader identity and lookup from execution. The executed Node controls and direct REA control establish the same target without loading it. The existing boundary fixture currently groups these valid optional forms with ignored calls; that source behavior explains the gap rather than proving the dependency absent.

Impact and scope

Two valid source spellings lose a historical dependency while their file inventory remains available. Each was observed once, alongside the direct control. Real-project prevalence, other optional-call forms, downstream comparison effects, installed-package behavior, MCP and other platforms were not measured.

The overall graph is partial because of its existing generic filesystem limitation; the omission has no parser-specific or entry-specific explanation. Changing the historical source to direct-call syntax changes its content identity and is not a verified unchanged-source workaround.

REA version

Source revision ef475fcf8b0601c61052b7091568ba574546417d, package metadata 6.7.0. The installed REA package was not used.

Runtime and platform

Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64; repository-locked dependencies with npm pinned to 11.16.0. No native provider required.

Target and artifact context

Three independent local UTF-8 CommonJS fixture directories, each containing main.cjs with one displayed resolver spelling and the same inert dep.cjs. No package manifest, custom loader or target execution by REA.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Original output from the one executed combined probe, with fixture-root text normalized by that probe:

{"case":"cjs_control","exit":0,"entries":["dep.cjs","main.cjs"],"relationships":[{"from_path":"main.cjs","to":"dep.cjs","kind":"requires","resolution":"internal","parse_state":"parsed"}],"parse_failures":[],"entry_limitations":[],"oracle":"$FIXTURE_ROOT/dep.cjs"}
{"case":"optional_resolve","exit":0,"entries":["dep.cjs","main.cjs"],"relationships":[],"parse_failures":[],"entry_limitations":[],"oracle":"$FIXTURE_ROOT/dep.cjs"}
{"case":"optional_receiver","exit":0,"entries":["dep.cjs","main.cjs"],"relationships":[],"parse_failures":[],"entry_limitations":[],"oracle":"$FIXTURE_ROOT/dep.cjs"}
  • Expected source: executed same-tree Node resolver controls and retained direct-call historical edge.
  • Failure source: registered createCli().serve() command using production filesystem traversal, importer, graph validation and output projection.
  • Evidence provenance: observed.
  • Local verification: reproduced once before drafting.
  • Reproduction completeness: complete at the source-CLI boundary; the packaged executable dispatcher and MCP were not run.

Public reachability: CLI registration and importer invocation.

Typed outcome or recovery expected

A parsed internal requires relationship from main.cjs to dep.cjs for each exact optional form, with existing file hashes retained; otherwise an explicit scoped limitation. Preserve lexical-shadowing and overwritten-resolver uncertainty. This is a proposed restoration check, not an executed fix.

Analysis or suspected cause

Source-derived: historical expression collection admits only Babel CallExpression and ImportExpression; optional calls use OptionalCallExpression. The historical require-binding collector independently rejects every node that is not CallExpression. Both producing gates omit these fixtures before resolver identity and dependency projection can run.

Additional context

Open and closed issue searches covered optional require/resolver syntax and historical/reference-source import. No exact historical-import report surfaced. Closed #2883 concerns fetched source-map dependency extraction in analyze_web_bundle, a different producer. Open #3885 concerns native-addon binding records in application analysis and explicitly retains the ordinary dependency for its optional call. This report concerns the historical-source graph losing a resolve-only dependency through its separate collectors, without any native addon or browser.

NOT_ENOUGH_INFO: introducing revision, last known good version, installed-release behavior and real-world prevalence. AI-assisted research and drafting. No fix, tracked-source mutation, build, broad test suite or issue filing was performed. The draft is frozen after writing; no post-draft reproduction or independent review was performed.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Two valid exact built-in resolver forms demonstrably lose a historical dependency while the matched direct-call control retains it; source files remain available and no severe downstream incident was established.
  • Workaround status: unverified
  • Workaround basis: Direct-call syntax resolves in the control, but rewriting historical source changes its identity; no public workflow restoring the original structured graph was verified.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6892 ## 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 registered source CLI `import-reference-source` silently drops the dependency from both `require.resolve?.("./dep.cjs")` and `require?.resolve("./dep.cjs")` in an ordinary `.cjs` file. Both Node resolve-only controls identify the existing `dep.cjs`, exactly like the direct `require.resolve("./dep.cjs")` control. REA retains a parsed internal dependency only for the direct form. All three imports exit 0, hash both files, and return no parse failures or entry-specific limitations. No overwritten member, local shadow, computed request or nullable custom loader is involved. ## Reproduction Tested once in a six-case probe at the revision below. The exact relevant fixture contents and CLI/Node operations appear in this standalone excerpt; only checkout/output paths and the surrounding case selection are parameterized. This excerpt was not separately rerun. With the checkout's locked dependencies installed, save the script below as `/tmp/rea-reference-optional-resolver.mjs`, then run from that checkout: ```sh work="$(mktemp -d)" node --import ./node_modules/tsx/dist/loader.mjs \ /tmp/rea-reference-optional-resolver.mjs "$PWD" "$work" ``` ```javascript import { mkdir, writeFile } from 'node:fs/promises'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; import { execFileSync } from 'node:child_process'; const [repo, out] = process.argv.slice(2); const { createCli, closeCliJavaScriptResources } = await import(pathToFileURL(join(repo, 'src/cli.ts')).href); const cases = { cjs_control: { main: 'main.cjs', source: 'console.log(require.resolve("./dep.cjs"));\n', }, optional_resolve: { main: 'main.cjs', source: 'console.log(require.resolve?.("./dep.cjs"));\n', }, optional_receiver: { main: 'main.cjs', source: 'console.log(require?.resolve("./dep.cjs"));\n', }, }; try { for (const [name, fixture] of Object.entries(cases)) { const root = join(out, name); await mkdir(root, { recursive: true }); await writeFile(join(root, fixture.main), fixture.source); await writeFile(join(root, 'dep.cjs'), 'throw new Error("dependency must not execute");\n'); if (fixture.manifest) await writeFile(join(root, 'package.json'), JSON.stringify(fixture.manifest)); let stdout = '', exit = 0; await createCli({ REA_LOG_LEVEL: 'silent', REA_REFERENCE_SECRET_PATTERNS_JSON: '[]', }).serve(['import-reference-source', root, '--json'], { env: {}, exit: code => { exit = code; }, stdout: text => { stdout += text; }, }); const graph = JSON.parse(stdout); const oracle = execFileSync(process.execPath, [join(root, fixture.main)], { encoding: 'utf8' }).replaceAll(root, '$FIXTURE_ROOT').trim(); console.log(JSON.stringify({ case: name, exit, entries: graph.entries.map(entry => entry.path), relationships: graph.relationships, parse_failures: graph.parse_failures, entry_limitations: graph.entries.flatMap(entry => entry.limitations), oracle, })); } } finally { await closeCliJavaScriptResources(); } ``` REA reads the inert fixtures. The separate Node controls execute only the self-authored main files and call the resolver, which never loads the throwing dependency. ## Expected behavior Retain the same historical dependency for these two exact, unmodified built-in resolver spellings as for the direct-call control, or record a specific unsupported-coverage limitation instead of silently losing the reference. The CommonJS wrapper supplies `require`, and its original `resolve` method is present; neither optional chain skips the lookup in this fixture. The [historical CommonJS contract](https://github.com/morluto/rea/blob/ef475fcf8b0601c61052b7091568ba574546417d/docs/cli.md#L441-L450) includes `require.resolve` and distinguishes loader identity and lookup from execution. The executed Node controls and direct REA control establish the same target without loading it. The [existing boundary fixture](https://github.com/morluto/rea/blob/ef475fcf8b0601c61052b7091568ba574546417d/tests/boundary/filesystem/referenceSourceComputedRequire.test.ts#L25-L64) currently groups these valid optional forms with ignored calls; that source behavior explains the gap rather than proving the dependency absent. ## Impact and scope Two valid source spellings lose a historical dependency while their file inventory remains available. Each was observed once, alongside the direct control. Real-project prevalence, other optional-call forms, downstream comparison effects, installed-package behavior, MCP and other platforms were not measured. The overall graph is partial because of its existing generic filesystem limitation; the omission has no parser-specific or entry-specific explanation. Changing the historical source to direct-call syntax changes its content identity and is not a verified unchanged-source workaround. ## REA version Source revision `ef475fcf8b0601c61052b7091568ba574546417d`, package metadata 6.7.0. The installed REA package was not used. ## Runtime and platform Node.js v24.22.1; Linux 7.2.8-9-omarchy x86_64; repository-locked dependencies with npm pinned to 11.16.0. No native provider required. ## Target and artifact context Three independent local UTF-8 CommonJS fixture directories, each containing `main.cjs` with one displayed resolver spelling and the same inert `dep.cjs`. No package manifest, custom loader or target execution by REA. ## Interface CLI ## Earliest failure stage Artifact or evidence registration ## Relevant output or evidence Original output from the one executed combined probe, with fixture-root text normalized by that probe: ```jsonl {"case":"cjs_control","exit":0,"entries":["dep.cjs","main.cjs"],"relationships":[{"from_path":"main.cjs","to":"dep.cjs","kind":"requires","resolution":"internal","parse_state":"parsed"}],"parse_failures":[],"entry_limitations":[],"oracle":"$FIXTURE_ROOT/dep.cjs"} {"case":"optional_resolve","exit":0,"entries":["dep.cjs","main.cjs"],"relationships":[],"parse_failures":[],"entry_limitations":[],"oracle":"$FIXTURE_ROOT/dep.cjs"} {"case":"optional_receiver","exit":0,"entries":["dep.cjs","main.cjs"],"relationships":[],"parse_failures":[],"entry_limitations":[],"oracle":"$FIXTURE_ROOT/dep.cjs"} ``` - Expected source: executed same-tree Node resolver controls and retained direct-call historical edge. - Failure source: registered `createCli().serve()` command using production filesystem traversal, importer, graph validation and output projection. - Evidence provenance: observed. - Local verification: reproduced once before drafting. - Reproduction completeness: complete at the source-CLI boundary; the packaged executable dispatcher and MCP were not run. Public reachability: [CLI registration and importer invocation](https://github.com/morluto/rea/blob/ef475fcf8b0601c61052b7091568ba574546417d/src/cli/utilityCommands.ts#L279-L373). ## Typed outcome or recovery expected A parsed internal `requires` relationship from `main.cjs` to `dep.cjs` for each exact optional form, with existing file hashes retained; otherwise an explicit scoped limitation. Preserve lexical-shadowing and overwritten-resolver uncertainty. This is a proposed restoration check, not an executed fix. ## Analysis or suspected cause Source-derived: [historical expression collection](https://github.com/morluto/rea/blob/ef475fcf8b0601c61052b7091568ba574546417d/src/domain/referenceSourceImportParsing.ts#L216-L233) admits only Babel `CallExpression` and `ImportExpression`; optional calls use `OptionalCallExpression`. The [historical require-binding collector](https://github.com/morluto/rea/blob/ef475fcf8b0601c61052b7091568ba574546417d/src/domain/referenceSourceRequireBindings.ts#L35-L59) independently rejects every node that is not `CallExpression`. Both producing gates omit these fixtures before resolver identity and dependency projection can run. ## Additional context Open and closed issue searches covered optional require/resolver syntax and historical/reference-source import. No exact historical-import report surfaced. Closed [#2883](https://github.com/morluto/rea/issues/2883) concerns fetched source-map dependency extraction in `analyze_web_bundle`, a different producer. Open [#3885](https://github.com/morluto/rea/issues/3885) concerns native-addon binding records in application analysis and explicitly retains the ordinary dependency for its optional call. This report concerns the historical-source graph losing a resolve-only dependency through its separate collectors, without any native addon or browser. NOT_ENOUGH_INFO: introducing revision, last known good version, installed-release behavior and real-world prevalence. AI-assisted research and drafting. No fix, tracked-source mutation, build, broad test suite or issue filing was performed. The draft is frozen after writing; no post-draft reproduction or independent review was performed. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Two valid exact built-in resolver forms demonstrably lose a historical dependency while the matched direct-call control retains it; source files remain available and no severe downstream incident was established. - Workaround status: unverified - Workaround basis: Direct-call syntax resolves in the control, but rewriting historical source changes its identity; no public workflow restoring the original structured graph was verified.
Author
Owner

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

Resolved by github.com/morluto/rea@f2e5fd148a. OptionalCallExpression admission is shared by extraction and loader-binding ownership. The CLI fixture retains the built-in optional calls and checks overwritten/shadowed negative controls. Validation: tests/boundary/cli/referenceSourceModuleSemantics.test.ts passed in the recorded round2/3 execution; this review did not rerun tests. Coverage is limited to the cited control; no broader real-provider, Windows or performance claim.

<!-- gh-comment-id:6376102078 --> @morluto commented on GitHub (Oct 23, 2026): Resolved by https://github.com/morluto/rea/commit/f2e5fd148a85fa737302dd194a33172c4561093e. OptionalCallExpression admission is shared by extraction and loader-binding ownership. The CLI fixture retains the built-in optional calls and checks overwritten/shadowed negative controls. Validation: `tests/boundary/cli/referenceSourceModuleSemantics.test.ts` passed in the recorded round2/3 execution; this review did not rerun tests. Coverage is limited to the cited control; no broader real-provider, Windows or performance claim.
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#4227
No description provided.