[GH-ISSUE #6654] [Bug] Historical reference import loses exact dynamic-import targets #4035

Closed
opened 2026-10-31 00:46:14 +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/6654

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

Observed at the production importReferenceSource application boundary: import(`./dep.mjs`) becomes an unknown/partial <dynamic-import> relationship even though its exact target is inventoried. The quoted-string control resolves to dep.mjs. The TypeScript expression import("./dep.mjs" as const) also loses its exact target in the same way.

All ten probe files are hashed, each has empty entry limitations, and parse_failures and exclusions are empty. The graph is partial because it retains the general pathname-race limitation; this report does not claim complete inventory.

Reproduction

The production importer completed this ten-file probe once. Save the importer portion below as /tmp/reference-import-probe.mts, then run it from the checkout with its locked dependencies and a working platform binary for tsx:

import { mkdir, writeFile } from 'node:fs/promises';
import { join, resolve } from 'node:path';
import { pathToFileURL } from 'node:url';
const repo = resolve(process.argv[2]);
const root = resolve(process.argv[3]);
await mkdir(root, { recursive: true });
const files = {
  'literal-control.mjs': 'export const load = () => import("./dep.mjs");\n',
  'template.mjs': 'export const load = () => import(`./dep.mjs`);\n',
  'cast.ts': 'export const load = () => import("./dep.mjs" as const);\n',
  'exported.cts': 'export import dep = require("./dep.cjs");\n',
  'plain.cts': 'import dep = require("./dep.cjs"); export { dep };\n',
  'query.ts': 'export type Alias = import("./types").Thing;\n',
  'type-control.ts': 'import type { Thing } from "./types"; export type Alias = Thing;\n',
  'dep.mjs': 'export const value = 1;\n',
  'dep.cjs': 'module.exports = { value: 1 };\n',
  'types.ts': 'export interface Thing { value: number }\n',
};
for (const [name, text] of Object.entries(files)) await writeFile(join(root, name), text);
const load = (name) => import(pathToFileURL(join(repo, 'src', name + '.ts')).href);
const { importReferenceSource } = await load('application/ReferenceSourceImport');
const { ArtifactResourceScope } = await load('artifacts/ArtifactResourceScope');
const resources = new ArtifactResourceScope();
try {
  const result = await importReferenceSource({
    root, caller: 'research-probe', policy: { secretPatterns: [] },
  }, resources);
  if (!result.ok) throw new Error(JSON.stringify(result.error));
  const graph = result.value;
  console.log(JSON.stringify({
    entries: graph.entries.map(({ path, content_state, limitations }) => ({ path, content_state, limitations })),
    relationships: graph.relationships, parse_failures: graph.parse_failures,
    exclusions: graph.exclusions, limitations: graph.limitations,
    inventory_state: graph.inventory_state,
  }));
} finally { await resources.close(); }
work="$(mktemp -d /tmp/reference-import.XXXXXX)"
node --import "$PWD/node_modules/tsx/dist/loader.mjs" \
  /tmp/reference-import-probe.mts "$PWD" "$work/source"

Only source inspection runs; no fixture module is loaded or executed. The recorded graph preceded an unrelated module-not-found failure in an optional compiler control after this importer portion. The claim here is the observed importer result, not a clean whole-process or public CLI exit. No probe was rerun after this draft was written.

Expected behavior

Retain the exact string for an expression-free template and a runtime-transparent TypeScript wrapper, then resolve it as the equivalent quoted import. Preserve unknown/partial status for genuinely computed imports.

The existing exact-literal owner reads these forms without evaluating code. The historical-source parser already uses that owner for CommonJS arguments. The historical-source contract retains relationships and source hashes for comparison.

Impact and scope

The observed historical graph loses a known local dependency identity for valid JavaScript and TypeScript syntax. The quoted form works, but rewriting historical source changes its bytes and hashes. Real-world frequency and downstream comparison effects were not measured. Source inventory remains available; no mutation or severe harm was observed.

REA version

Package metadata 6.7.0; source revision 23def04096869b85c4d3c14a73ed09168483c9f6 with locked dependencies. Published tarball execution was not tested.

Runtime and platform

Node.js v24.22.1; Linux x64, kernel 7.2.8-9-omarchy; installed locked tsx with matching esbuild 0.28.2 supplied from a scratch dependency installation. No native provider or analyzed source execution.

Target and artifact context

Benign UTF-8 source tree shown above. Relevant cases are template.mjs, cast.ts, literal-control.mjs, and dep.mjs; the other files are independent TypeScript controls in the same executed probe.

Interface

Other

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Observed relationships:

[
  {"from_path":"cast.ts","to":"<dynamic-import>","kind":"imports","resolution":"unknown","parse_state":"partial"},
  {"from_path":"literal-control.mjs","to":"dep.mjs","kind":"imports","resolution":"internal","parse_state":"parsed"},
  {"from_path":"template.mjs","to":"<dynamic-import>","kind":"imports","resolution":"unknown","parse_state":"partial"}
]

All three importers and dep.mjs have content_state: "hashed" and limitations: []. Evidence provenance: executed production application boundary. CLI/MCP transport, published package, and downstream comparison were not exercised.

Typed outcome or recovery expected

Both exact-expression cases should retain to: "dep.mjs", resolution: "internal", and parse_state: "parsed", preserving all original hashes. Computed imports must retain explicit uncertainty.

Analysis or suspected cause

Source-derived: the ImportExpression branch calls safeModuleName, which accepts only StringLiteral. It bypasses the existing exact-literal owner used by the CommonJS branch and replaces both exact expressions with the generic placeholder.

Additional context

Closed #4382 explicitly requests CommonJS template restoration only. Its PR #4401 states that the ImportExpression branch remains unchanged; #4409 also retains the old dynamic-template behavior. Closed #2841 and #2599 concern captured browser/source-map producers, not historical-source import. Bounded open/closed searches found no report requesting restoration of this remaining historical-import branch. The duplicate search was refreshed immediately before submission. Last known good version: NOT_ENOUGH_INFO; no regression range was established.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: The executed valid fixture loses exact dependency identity at the historical-source producer while source inventory remains available; broader or severe downstream consequences were not demonstrated.
  • Workaround status: unverified
  • Workaround basis: Quoted source resolves, but changing analyzed source changes its identity. No REA workflow preserving the original bytes and restoring these graph relationships was tested.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6654 ## 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 Observed at the production `importReferenceSource` application boundary: ``import(`./dep.mjs`)`` becomes an unknown/partial `<dynamic-import>` relationship even though its exact target is inventoried. The quoted-string control resolves to `dep.mjs`. The TypeScript expression `import("./dep.mjs" as const)` also loses its exact target in the same way. All ten probe files are hashed, each has empty entry limitations, and `parse_failures` and `exclusions` are empty. The graph is `partial` because it retains the general pathname-race limitation; this report does not claim complete inventory. ## Reproduction The production importer completed this ten-file probe once. Save the importer portion below as `/tmp/reference-import-probe.mts`, then run it from the checkout with its locked dependencies and a working platform binary for `tsx`: ```typescript import { mkdir, writeFile } from 'node:fs/promises'; import { join, resolve } from 'node:path'; import { pathToFileURL } from 'node:url'; const repo = resolve(process.argv[2]); const root = resolve(process.argv[3]); await mkdir(root, { recursive: true }); const files = { 'literal-control.mjs': 'export const load = () => import("./dep.mjs");\n', 'template.mjs': 'export const load = () => import(`./dep.mjs`);\n', 'cast.ts': 'export const load = () => import("./dep.mjs" as const);\n', 'exported.cts': 'export import dep = require("./dep.cjs");\n', 'plain.cts': 'import dep = require("./dep.cjs"); export { dep };\n', 'query.ts': 'export type Alias = import("./types").Thing;\n', 'type-control.ts': 'import type { Thing } from "./types"; export type Alias = Thing;\n', 'dep.mjs': 'export const value = 1;\n', 'dep.cjs': 'module.exports = { value: 1 };\n', 'types.ts': 'export interface Thing { value: number }\n', }; for (const [name, text] of Object.entries(files)) await writeFile(join(root, name), text); const load = (name) => import(pathToFileURL(join(repo, 'src', name + '.ts')).href); const { importReferenceSource } = await load('application/ReferenceSourceImport'); const { ArtifactResourceScope } = await load('artifacts/ArtifactResourceScope'); const resources = new ArtifactResourceScope(); try { const result = await importReferenceSource({ root, caller: 'research-probe', policy: { secretPatterns: [] }, }, resources); if (!result.ok) throw new Error(JSON.stringify(result.error)); const graph = result.value; console.log(JSON.stringify({ entries: graph.entries.map(({ path, content_state, limitations }) => ({ path, content_state, limitations })), relationships: graph.relationships, parse_failures: graph.parse_failures, exclusions: graph.exclusions, limitations: graph.limitations, inventory_state: graph.inventory_state, })); } finally { await resources.close(); } ``` ```sh work="$(mktemp -d /tmp/reference-import.XXXXXX)" node --import "$PWD/node_modules/tsx/dist/loader.mjs" \ /tmp/reference-import-probe.mts "$PWD" "$work/source" ``` Only source inspection runs; no fixture module is loaded or executed. The recorded graph preceded an unrelated module-not-found failure in an optional compiler control after this importer portion. The claim here is the observed importer result, not a clean whole-process or public CLI exit. No probe was rerun after this draft was written. ## Expected behavior Retain the exact string for an expression-free template and a runtime-transparent TypeScript wrapper, then resolve it as the equivalent quoted import. Preserve unknown/partial status for genuinely computed imports. The existing [exact-literal owner](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/domain/javascript/javascriptAstValues.ts#L61) reads these forms without evaluating code. The historical-source parser already [uses that owner for CommonJS arguments](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/domain/referenceSourceImportParsing.ts#L91). The [historical-source contract](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/docs/cli.md#L342) retains relationships and source hashes for comparison. ## Impact and scope The observed historical graph loses a known local dependency identity for valid JavaScript and TypeScript syntax. The quoted form works, but rewriting historical source changes its bytes and hashes. Real-world frequency and downstream comparison effects were not measured. Source inventory remains available; no mutation or severe harm was observed. ## REA version Package metadata 6.7.0; source revision `23def04096869b85c4d3c14a73ed09168483c9f6` with locked dependencies. Published tarball execution was not tested. ## Runtime and platform Node.js v24.22.1; Linux x64, kernel `7.2.8-9-omarchy`; installed locked `tsx` with matching esbuild 0.28.2 supplied from a scratch dependency installation. No native provider or analyzed source execution. ## Target and artifact context Benign UTF-8 source tree shown above. Relevant cases are `template.mjs`, `cast.ts`, `literal-control.mjs`, and `dep.mjs`; the other files are independent TypeScript controls in the same executed probe. ## Interface Other ## Earliest failure stage Artifact or evidence registration ## Relevant output or evidence Observed relationships: ```json [ {"from_path":"cast.ts","to":"<dynamic-import>","kind":"imports","resolution":"unknown","parse_state":"partial"}, {"from_path":"literal-control.mjs","to":"dep.mjs","kind":"imports","resolution":"internal","parse_state":"parsed"}, {"from_path":"template.mjs","to":"<dynamic-import>","kind":"imports","resolution":"unknown","parse_state":"partial"} ] ``` All three importers and `dep.mjs` have `content_state: "hashed"` and `limitations: []`. Evidence provenance: executed production application boundary. CLI/MCP transport, published package, and downstream comparison were not exercised. ## Typed outcome or recovery expected Both exact-expression cases should retain `to: "dep.mjs"`, `resolution: "internal"`, and `parse_state: "parsed"`, preserving all original hashes. Computed imports must retain explicit uncertainty. ## Analysis or suspected cause Source-derived: the [ImportExpression branch](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/domain/referenceSourceImportParsing.ts#L249) calls `safeModuleName`, which [accepts only StringLiteral](https://github.com/morluto/rea/blob/23def04096869b85c4d3c14a73ed09168483c9f6/src/domain/referenceSourceImportParsing.ts#L70). It bypasses the existing exact-literal owner used by the CommonJS branch and replaces both exact expressions with the generic placeholder. ## Additional context Closed [#4382](https://github.com/morluto/rea/issues/4382) explicitly requests CommonJS template restoration only. Its [PR #4401](https://github.com/morluto/rea/pull/4401) states that the ImportExpression branch remains unchanged; [#4409](https://github.com/morluto/rea/pull/4409) also retains the old dynamic-template behavior. Closed [#2841](https://github.com/morluto/rea/issues/2841) and [#2599](https://github.com/morluto/rea/issues/2599) concern captured browser/source-map producers, not historical-source import. Bounded open/closed searches found no report requesting restoration of this remaining historical-import branch. The duplicate search was refreshed immediately before submission. Last known good version: NOT_ENOUGH_INFO; no regression range was established. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: The executed valid fixture loses exact dependency identity at the historical-source producer while source inventory remains available; broader or severe downstream consequences were not demonstrated. - Workaround status: unverified - Workaround basis: Quoted source resolves, but changing analyzed source changes its identity. No REA workflow preserving the original bytes and restoring these graph relationships was tested.
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#4035
No description provided.