[GH-ISSUE #7323] [Bug] Application analysis resolves an unbound require as CommonJS inside a type-module package #4351

Open
opened 2026-10-31 00:49:08 +00:00 by bsakaguchi · 0 comments
Owner

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

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 built analyze-javascript-application CLI succeeds on a directory whose inventoried package.json declares "type":"module", but reports main.js as module_format:"commonjs", counts one import relationship, and emits a resolved semantic require edge to dep.cjs. Node prints ReferenceError for that fixture because the file has no require binding. With identical source named main.mjs, REA reports esm and no semantic require edge to the dependency. This concerns loader attribution, independently of whether a static relationship executes.

Reproduction

From the baseline checkout with its locked dependencies and runtime already built, put Node 24.22.1 on PATH and run the following Python 3 script from the repository root. It creates four local fixtures, isolates CLI HOME/config/cache/temp directories, invokes the real built CLI, retains its output, and separately executes only these harmless self-authored programs as Node controls. The dependency exports the number 1. This complete script was executed once; a prior independent probe of the same four fixture inputs produced the same results, for two failing type-module cases across two completed sets.

python3 <<'PY'
import json, os, pathlib, shutil, subprocess, tempfile
repo = pathlib.Path.cwd()
node = shutil.which('node')
work = pathlib.Path(tempfile.mkdtemp(prefix='rea-module-scope-'))
env = {'PATH': str(pathlib.Path(node).parent) + os.pathsep + os.defpath,
       'REA_LOG_LEVEL': 'silent'}
for key in ('HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME',
            'XDG_STATE_HOME', 'TMPDIR'):
    directory = work / key.lower()
    directory.mkdir()
    env[key] = str(directory)
source = 'try { console.log(require("./dep.cjs")); } catch (error) { console.log(error.name); }\n'
for name, suffix, manifest in (
    ('esm_package', '.js', {'type': 'module'}),
    ('esm_suffix', '.mjs', {'type': 'module'}),
    ('cjs_package', '.js', {'type': 'commonjs'}),
    ('unknown_package', '.js', {}),
):
    case = work / name
    case.mkdir()
    main = 'main' + suffix
    (case / 'package.json').write_text(json.dumps(manifest) + '\n')
    (case / main).write_text(source)
    (case / 'dep.cjs').write_text('module.exports = 1;\n')
    cli = subprocess.run([node, str(repo / 'scripts/rea.mjs'),
        'analyze-javascript-application', str(case),
        '--artifact-format', 'directory', '--json'], cwd=work, env=env,
        text=True, capture_output=True, timeout=90)
    (work / (name + '.json')).write_text(cli.stdout)
    (work / (name + '.stderr')).write_text(cli.stderr)
    cli.check_returncode()
    graph = json.loads(cli.stdout)['normalized_result']['graph']
    modules = [n for n in graph['nodes'] if n['kind'] == 'javascript-module'
        and any(o['properties'].get('semantic_role') == 'source-module'
                and o['properties'].get('path') == main
                for o in n['observations'])]
    module = modules[0]
    props = next(o['properties'] for o in module['observations']
        if o['properties'].get('semantic_role') == 'source-module')
    edges = [e for e in graph['edges']
        if e['source_node_id'] == module['node_id']
        and e['properties'].get('module_link_kind') == 'require']
    oracle = subprocess.run([node, str(case / main)], cwd=work, env=env,
        text=True, capture_output=True, timeout=10, check=True)
    print(json.dumps({'case': name, 'cli_exit': cli.returncode,
        'module_format': props['module_format'],
        'import_relationships': props['import_relationships'],
        'semantic_require_targets': [e['properties']['resolved_path'] for e in edges],
        'node_stdout': oracle.stdout.strip()}))
PY

Expected behavior

An inventoried "type":"module" package establishes ES-module scope for its .js files. An unbound require in that scope should not become a resolved Node-loader dependency or classify the source as CommonJS. The application workflow already omits the semantic dependency for the identical .mjs control; the CommonJS and no-type controls should retain their current loader edges. Preserving an unresolved syntax candidate is compatible with this expectation, and the expectation does not require executing analyzed code.

Impact and scope

The demonstrated package layout receives an incorrect resolved dependency, import count and module-format fact in completed application Evidence. The source inventory remains available. Only synthetic directory fixtures were tested; real-application frequency, downstream tracing/comparison consequences, installed-release behavior, MCP transport and other platforms were not measured. No unchanged-source invocation correcting this structured result was verified.

REA version

Package metadata 6.8.0; source revision ace4dbf36e8c8f36a0e192aff9a11484ed428358. Used the built checkout, not an installed release.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1 arm64; Python 3 for the probe. No external analysis provider.

Target and artifact context

Four local directory fixtures containing a readable UTF-8 package manifest, one main source file and dep.cjs. Source selection was omitted, so the manifest and both source files were included. No real application, custom loader or runtime capture was involved.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Executed script output:

{"case": "esm_package", "cli_exit": 0, "module_format": "commonjs", "import_relationships": 1, "semantic_require_targets": ["dep.cjs"], "node_stdout": "ReferenceError"}
{"case": "esm_suffix", "cli_exit": 0, "module_format": "esm", "import_relationships": 0, "semantic_require_targets": [], "node_stdout": "ReferenceError"}
{"case": "cjs_package", "cli_exit": 0, "module_format": "commonjs", "import_relationships": 1, "semantic_require_targets": ["dep.cjs"], "node_stdout": "1"}
{"case": "unknown_package", "cli_exit": 0, "module_format": "commonjs", "import_relationships": 1, "semantic_require_targets": ["dep.cjs"], "node_stdout": "1"}

In the failing case, the module-to-module edge has relation:"imports", module_link_kind:"require", resolution_mode:"require", resolved_path:"dep.cjs", and resolution_status:"resolved"; its authority/state/confidence are static-relationship-inference / inferred / high. The separate asset-level reference also resolves the dependency. The .mjs control retains an unresolved asset-level expression candidate, so this report does not claim that all syntax-reference edges should disappear.

  • Expected source: the observed .mjs control and the shared known-module guard in src/domain/javascript/javascriptSemanticRequireOrigin.ts:157–187,1560–1605; package-aware parser behavior in src/domain/javascript/javascriptSourceParser.ts:168–194. docs/javascript-artifact-reconstruction.md:13–66 documents this supported directory workflow. The historical import discussion in docs/cli.md:689–708 describes the same no-implicit-wrapper rule, with historical-workflow scope.
  • Failure source: completed public CLI Evidence above. Source inspection finds the application worker parsing without package context at src/composition/javascriptAnalysisWorkerProcess.ts:27,47–54, package-field projection omitting type at src/application/javascript/JavaScriptJsonDocuments.ts:48–57, and link-to-edge/module-format projection at src/application/javascript/JavaScriptModuleRelationships.ts:157–180,254–330,454–476.
  • Evidence provenance: observed for CLI results and Node controls; static for the producing-boundary explanation.
  • Local verification: reproduced through the built public CLI in two completed four-case sets. No fix was applied.
  • Reproduction completeness: complete for this CLI directory-analysis case.

Additional context

Closed #6891 concerns historical import-reference-source graphs. Its closing change passed scoped package facts through that importer; the application worker is a different caller and still reproduces this case. Closed #1793 restores missing semantic dependencies for explicit CommonJS sources and does not establish a loader binding for a known ES module. The supplied snapshot and current searches of open and closed issues and PRs contained no matching application-workflow report.

Restoration check: rerun the script and observe that the type-module .js case has no resolved semantic require edge to dep.cjs and is no longer labeled CommonJS. The .mjs control should remain free of that semantic edge; the CommonJS and no-type controls must retain one resolved semantic edge and their existing import counts. The Node controls should remain unchanged. This is a proposed check, not an observed repair.

NOT_ENOUGH_INFO: introducing revision, last known good version and a graph-preserving workaround. AI-assisted verification and drafting; no source changes.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A completed public CLI graph contains a wrong resolved dependency, count and module-format fact for the demonstrated package scope; no severe downstream incident or broader frequency was established.
  • Workaround status: unverified
  • Workaround basis: The .mjs control changes the analyzed filename, and no public invocation preserving the original input while correcting its structured graph was demonstrated.
Originally created by @coygeek on GitHub (Oct 26, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/7323 ## 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 built `analyze-javascript-application` CLI succeeds on a directory whose inventoried `package.json` declares `"type":"module"`, but reports `main.js` as `module_format:"commonjs"`, counts one import relationship, and emits a resolved semantic `require` edge to `dep.cjs`. Node prints `ReferenceError` for that fixture because the file has no `require` binding. With identical source named `main.mjs`, REA reports `esm` and no semantic `require` edge to the dependency. This concerns loader attribution, independently of whether a static relationship executes. ## Reproduction From the baseline checkout with its locked dependencies and runtime already built, put Node 24.22.1 on PATH and run the following Python 3 script from the repository root. It creates four local fixtures, isolates CLI HOME/config/cache/temp directories, invokes the real built CLI, retains its output, and separately executes only these harmless self-authored programs as Node controls. The dependency exports the number 1. This complete script was executed once; a prior independent probe of the same four fixture inputs produced the same results, for two failing type-module cases across two completed sets. ```sh python3 <<'PY' import json, os, pathlib, shutil, subprocess, tempfile repo = pathlib.Path.cwd() node = shutil.which('node') work = pathlib.Path(tempfile.mkdtemp(prefix='rea-module-scope-')) env = {'PATH': str(pathlib.Path(node).parent) + os.pathsep + os.defpath, 'REA_LOG_LEVEL': 'silent'} for key in ('HOME', 'XDG_CONFIG_HOME', 'XDG_DATA_HOME', 'XDG_CACHE_HOME', 'XDG_STATE_HOME', 'TMPDIR'): directory = work / key.lower() directory.mkdir() env[key] = str(directory) source = 'try { console.log(require("./dep.cjs")); } catch (error) { console.log(error.name); }\n' for name, suffix, manifest in ( ('esm_package', '.js', {'type': 'module'}), ('esm_suffix', '.mjs', {'type': 'module'}), ('cjs_package', '.js', {'type': 'commonjs'}), ('unknown_package', '.js', {}), ): case = work / name case.mkdir() main = 'main' + suffix (case / 'package.json').write_text(json.dumps(manifest) + '\n') (case / main).write_text(source) (case / 'dep.cjs').write_text('module.exports = 1;\n') cli = subprocess.run([node, str(repo / 'scripts/rea.mjs'), 'analyze-javascript-application', str(case), '--artifact-format', 'directory', '--json'], cwd=work, env=env, text=True, capture_output=True, timeout=90) (work / (name + '.json')).write_text(cli.stdout) (work / (name + '.stderr')).write_text(cli.stderr) cli.check_returncode() graph = json.loads(cli.stdout)['normalized_result']['graph'] modules = [n for n in graph['nodes'] if n['kind'] == 'javascript-module' and any(o['properties'].get('semantic_role') == 'source-module' and o['properties'].get('path') == main for o in n['observations'])] module = modules[0] props = next(o['properties'] for o in module['observations'] if o['properties'].get('semantic_role') == 'source-module') edges = [e for e in graph['edges'] if e['source_node_id'] == module['node_id'] and e['properties'].get('module_link_kind') == 'require'] oracle = subprocess.run([node, str(case / main)], cwd=work, env=env, text=True, capture_output=True, timeout=10, check=True) print(json.dumps({'case': name, 'cli_exit': cli.returncode, 'module_format': props['module_format'], 'import_relationships': props['import_relationships'], 'semantic_require_targets': [e['properties']['resolved_path'] for e in edges], 'node_stdout': oracle.stdout.strip()})) PY ``` ## Expected behavior An inventoried `"type":"module"` package establishes ES-module scope for its `.js` files. An unbound `require` in that scope should not become a resolved Node-loader dependency or classify the source as CommonJS. The application workflow already omits the semantic dependency for the identical `.mjs` control; the CommonJS and no-type controls should retain their current loader edges. Preserving an unresolved syntax candidate is compatible with this expectation, and the expectation does not require executing analyzed code. ## Impact and scope The demonstrated package layout receives an incorrect resolved dependency, import count and module-format fact in completed application Evidence. The source inventory remains available. Only synthetic directory fixtures were tested; real-application frequency, downstream tracing/comparison consequences, installed-release behavior, MCP transport and other platforms were not measured. No unchanged-source invocation correcting this structured result was verified. ## REA version Package metadata 6.8.0; source revision `ace4dbf36e8c8f36a0e192aff9a11484ed428358`. Used the built checkout, not an installed release. ## Runtime and platform Node.js v24.22.1; macOS 27.0.1 arm64; Python 3 for the probe. No external analysis provider. ## Target and artifact context Four local directory fixtures containing a readable UTF-8 package manifest, one main source file and `dep.cjs`. Source selection was omitted, so the manifest and both source files were included. No real application, custom loader or runtime capture was involved. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Executed script output: ```jsonl {"case": "esm_package", "cli_exit": 0, "module_format": "commonjs", "import_relationships": 1, "semantic_require_targets": ["dep.cjs"], "node_stdout": "ReferenceError"} {"case": "esm_suffix", "cli_exit": 0, "module_format": "esm", "import_relationships": 0, "semantic_require_targets": [], "node_stdout": "ReferenceError"} {"case": "cjs_package", "cli_exit": 0, "module_format": "commonjs", "import_relationships": 1, "semantic_require_targets": ["dep.cjs"], "node_stdout": "1"} {"case": "unknown_package", "cli_exit": 0, "module_format": "commonjs", "import_relationships": 1, "semantic_require_targets": ["dep.cjs"], "node_stdout": "1"} ``` In the failing case, the module-to-module edge has `relation:"imports"`, `module_link_kind:"require"`, `resolution_mode:"require"`, `resolved_path:"dep.cjs"`, and `resolution_status:"resolved"`; its authority/state/confidence are `static-relationship-inference` / `inferred` / `high`. The separate asset-level reference also resolves the dependency. The `.mjs` control retains an unresolved asset-level expression candidate, so this report does not claim that all syntax-reference edges should disappear. - Expected source: the observed `.mjs` control and the shared known-module guard in `src/domain/javascript/javascriptSemanticRequireOrigin.ts:157–187,1560–1605`; package-aware parser behavior in `src/domain/javascript/javascriptSourceParser.ts:168–194`. `docs/javascript-artifact-reconstruction.md:13–66` documents this supported directory workflow. The historical import discussion in `docs/cli.md:689–708` describes the same no-implicit-wrapper rule, with historical-workflow scope. - Failure source: completed public CLI Evidence above. Source inspection finds the application worker parsing without package context at `src/composition/javascriptAnalysisWorkerProcess.ts:27,47–54`, package-field projection omitting `type` at `src/application/javascript/JavaScriptJsonDocuments.ts:48–57`, and link-to-edge/module-format projection at `src/application/javascript/JavaScriptModuleRelationships.ts:157–180,254–330,454–476`. - Evidence provenance: observed for CLI results and Node controls; static for the producing-boundary explanation. - Local verification: reproduced through the built public CLI in two completed four-case sets. No fix was applied. - Reproduction completeness: complete for this CLI directory-analysis case. ## Additional context Closed [#6891](https://github.com/morluto/rea/issues/6891) concerns historical `import-reference-source` graphs. Its closing change passed scoped package facts through that importer; the application worker is a different caller and still reproduces this case. Closed [#1793](https://github.com/morluto/rea/issues/1793) restores missing semantic dependencies for explicit CommonJS sources and does not establish a loader binding for a known ES module. The supplied snapshot and current searches of open and closed issues and PRs contained no matching application-workflow report. Restoration check: rerun the script and observe that the type-module `.js` case has no resolved semantic `require` edge to `dep.cjs` and is no longer labeled CommonJS. The `.mjs` control should remain free of that semantic edge; the CommonJS and no-type controls must retain one resolved semantic edge and their existing import counts. The Node controls should remain unchanged. This is a proposed check, not an observed repair. NOT_ENOUGH_INFO: introducing revision, last known good version and a graph-preserving workaround. AI-assisted verification and drafting; no source changes. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A completed public CLI graph contains a wrong resolved dependency, count and module-format fact for the demonstrated package scope; no severe downstream incident or broader frequency was established. - Workaround status: unverified - Workaround basis: The `.mjs` control changes the analyzed filename, and no public invocation preserving the original input while correcting its structured graph was demonstrated.
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#4351
No description provided.