[GH-ISSUE #6883] [Bug] Historical reference import drops imports inside ambient module declarations #4220

Closed
opened 2026-10-31 00:47:49 +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/6883

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

import-reference-source silently omits a static import inside a TypeScript ambient module. The same import at the file's top level produces an imports relationship. All three files are inventoried, the CLI exits 0, and parse_failures is empty. The ambient module's declares-module relationship is present, but its dependency is absent.

Reproduction

Tested the current source clone with locked dependencies. Save the script below as /tmp/reference-ambient.mjs, then run from the clone root:

node --import ./node_modules/tsx/dist/loader.mjs /tmp/reference-ambient.mjs "$PWD"
import { mkdtemp, writeFile, rm } from 'node:fs/promises';
import { join } from 'node:path';
import { pathToFileURL } from 'node:url';
const repo = process.argv[2];
const { createCli, closeCliJavaScriptResources } = await import(pathToFileURL(join(repo, 'src/cli.ts')).href);
const root = await mkdtemp('/tmp/rea-ambient-');
const files = {
  'dep.d.ts': 'declare module "fixture-dependency" { export interface Marker { id: string } }\n',
  'consumer.d.ts': 'declare module "fixture-consumer" { import type { Marker } from "fixture-dependency"; export const marker: Marker; }\n',
  'control.d.ts': 'import type { Marker } from "fixture-dependency"; export declare const marker: Marker;\n'
};
try {
  for (const [name, source] of Object.entries(files)) await writeFile(join(root, name), source);
  let stdout = '', exit = 0;
  await createCli({ REA_LOG_LEVEL: 'silent' }).serve(['import-reference-source', root, '--json'], {
    env: {}, exit: code => { exit = code; }, stdout: text => { stdout += text; }
  });
  const graph = JSON.parse(stdout);
  console.log(JSON.stringify({ exit, entries: graph.entries.map(entry => entry.path), relationships: graph.relationships, parse_failures: graph.parse_failures, limitations: graph.limitations }, null, 2));
} finally { await closeCliJavaScriptResources(); await rm(root, { recursive: true, force: true }); }

The exact script body ran once, with only its scratch filename normalized above. A separate production-importer probe observed the same omission. The repository's pinned typescript6 6.0.3 compiler also checked these three files with noEmit: true, strict: true, types: [], and skipLibCheck: false: zero diagnostics.

Expected behavior

Retain the ambient module's static dependency, as for the top-level declaration control, or state that this declaration scope is unsupported. This is a historical source dependency, not a claim of runtime loading. The TypeScript module reference documents imports inside ambient module bodies; moving the import to the top level changes a script's ambient declarations into module augmentations, so that is not an equivalent repair to the source.

Impact and scope

A valid declaration file loses a dependency in its historical graph without a scoped limitation. The original files remain inventoried and available. Confirmed for the fixture above; real-project prevalence and downstream comparison effects were not measured. The graph's generic filesystem limitation is present; this report does not claim complete global graph coverage.

REA version

Source main, commit d0432d63c9fd359ee722c232993523765fe84cf9; package metadata 6.7.0. No installed REA package was used.

Runtime and platform

Node.js v24.22.1, locked tsx 4.23.15; Linux x64. No external analysis provider required.

Target and artifact context

Three self-authored inert .d.ts files shown above. No target code execution, custom loader, symlink, or package installation inside the fixture.

Interface

CLI

Earliest failure stage

Artifact or evidence registration

Relevant output or evidence

Observed source-CLI output, omitting only the generic limitations array:

{
  "exit": 0,
  "entries": ["consumer.d.ts", "control.d.ts", "dep.d.ts"],
  "relationships": [
    {"from_path":"consumer.d.ts","to":"fixture-consumer","kind":"declares-module","resolution":"unknown","parse_state":"parsed"},
    {"from_path":"control.d.ts","to":"fixture-dependency","kind":"imports","resolution":"external","parse_state":"parsed"},
    {"from_path":"dep.d.ts","to":"fixture-dependency","kind":"declares-module","resolution":"unknown","parse_state":"parsed"}
  ],
  "parse_failures": []
}

The consumer.d.ts → fixture-dependency import is missing. The public source-CLI registration, production filesystem traversal, importer, graph validation and JSON projection ran. The packaged launcher and MCP transport did not run. No post-draft reproduction was performed.

Analysis or suspected cause

Source-derived: extractImportDeclarations inspects only the program's immediate statements. Its TSModuleDeclaration branch records the declared module but does not inspect its body. The separate recursive expression pass collects call/import expressions rather than nested static declarations.

Additional context

Distinct from #6859: this fixture contains an ordinary ImportDeclaration in a module body, not a TSImportType expression. The import syntax already works at the top level. No fix was attempted. Proposed restoration check: retain the ambient-body dependency while preserving the existing top-level import and both module-declaration relationships.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A valid TypeScript declaration loses a dependency from the observed source-CLI historical graph; the matched top-level control retains it, and no severe downstream incident was established.
  • Workaround status: unverified
  • Workaround basis: Manual source inspection exposes the dependency, but no unchanged-source REA workflow restoring the graph was verified. Hoisting the import changes ambient-module semantics.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6883 ### 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 `import-reference-source` silently omits a static import inside a TypeScript ambient module. The same import at the file's top level produces an `imports` relationship. All three files are inventoried, the CLI exits 0, and `parse_failures` is empty. The ambient module's `declares-module` relationship is present, but its dependency is absent. ### Reproduction Tested the current source clone with locked dependencies. Save the script below as `/tmp/reference-ambient.mjs`, then run from the clone root: ```sh node --import ./node_modules/tsx/dist/loader.mjs /tmp/reference-ambient.mjs "$PWD" ``` ```javascript import { mkdtemp, writeFile, rm } from 'node:fs/promises'; import { join } from 'node:path'; import { pathToFileURL } from 'node:url'; const repo = process.argv[2]; const { createCli, closeCliJavaScriptResources } = await import(pathToFileURL(join(repo, 'src/cli.ts')).href); const root = await mkdtemp('/tmp/rea-ambient-'); const files = { 'dep.d.ts': 'declare module "fixture-dependency" { export interface Marker { id: string } }\n', 'consumer.d.ts': 'declare module "fixture-consumer" { import type { Marker } from "fixture-dependency"; export const marker: Marker; }\n', 'control.d.ts': 'import type { Marker } from "fixture-dependency"; export declare const marker: Marker;\n' }; try { for (const [name, source] of Object.entries(files)) await writeFile(join(root, name), source); let stdout = '', exit = 0; await createCli({ REA_LOG_LEVEL: 'silent' }).serve(['import-reference-source', root, '--json'], { env: {}, exit: code => { exit = code; }, stdout: text => { stdout += text; } }); const graph = JSON.parse(stdout); console.log(JSON.stringify({ exit, entries: graph.entries.map(entry => entry.path), relationships: graph.relationships, parse_failures: graph.parse_failures, limitations: graph.limitations }, null, 2)); } finally { await closeCliJavaScriptResources(); await rm(root, { recursive: true, force: true }); } ``` The exact script body ran once, with only its scratch filename normalized above. A separate production-importer probe observed the same omission. The repository's pinned `typescript6` 6.0.3 compiler also checked these three files with `noEmit: true`, `strict: true`, `types: []`, and `skipLibCheck: false`: zero diagnostics. ### Expected behavior Retain the ambient module's static dependency, as for the top-level declaration control, or state that this declaration scope is unsupported. This is a historical source dependency, not a claim of runtime loading. The [TypeScript module reference](https://www.typescriptlang.org/docs/handbook/modules/reference#ambient-modules) documents imports inside ambient module bodies; moving the import to the top level changes a script's ambient declarations into module augmentations, so that is not an equivalent repair to the source. ### Impact and scope A valid declaration file loses a dependency in its historical graph without a scoped limitation. The original files remain inventoried and available. Confirmed for the fixture above; real-project prevalence and downstream comparison effects were not measured. The graph's generic filesystem limitation is present; this report does not claim complete global graph coverage. ### REA version Source `main`, commit `d0432d63c9fd359ee722c232993523765fe84cf9`; package metadata `6.7.0`. No installed REA package was used. ### Runtime and platform Node.js v24.22.1, locked tsx 4.23.15; Linux x64. No external analysis provider required. ### Target and artifact context Three self-authored inert `.d.ts` files shown above. No target code execution, custom loader, symlink, or package installation inside the fixture. ### Interface CLI ### Earliest failure stage Artifact or evidence registration ### Relevant output or evidence Observed source-CLI output, omitting only the generic `limitations` array: ```json { "exit": 0, "entries": ["consumer.d.ts", "control.d.ts", "dep.d.ts"], "relationships": [ {"from_path":"consumer.d.ts","to":"fixture-consumer","kind":"declares-module","resolution":"unknown","parse_state":"parsed"}, {"from_path":"control.d.ts","to":"fixture-dependency","kind":"imports","resolution":"external","parse_state":"parsed"}, {"from_path":"dep.d.ts","to":"fixture-dependency","kind":"declares-module","resolution":"unknown","parse_state":"parsed"} ], "parse_failures": [] } ``` The `consumer.d.ts` → `fixture-dependency` import is missing. The public source-CLI registration, production filesystem traversal, importer, graph validation and JSON projection ran. The packaged launcher and MCP transport did not run. No post-draft reproduction was performed. ### Analysis or suspected cause Source-derived: [`extractImportDeclarations`](https://github.com/morluto/rea/blob/d0432d63c9fd359ee722c232993523765fe84cf9/src/domain/referenceSourceImportParsing.ts#L150-L213) inspects only the program's immediate statements. Its `TSModuleDeclaration` branch records the declared module but does not inspect its body. The separate recursive expression pass collects call/import expressions rather than nested static declarations. ### Additional context Distinct from [#6859](https://github.com/morluto/rea/issues/6859): this fixture contains an ordinary `ImportDeclaration` in a module body, not a `TSImportType` expression. The import syntax already works at the top level. No fix was attempted. Proposed restoration check: retain the ambient-body dependency while preserving the existing top-level import and both module-declaration relationships. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A valid TypeScript declaration loses a dependency from the observed source-CLI historical graph; the matched top-level control retains it, and no severe downstream incident was established. - Workaround status: unverified - Workaround basis: Manual source inspection exposes the dependency, but no unchanged-source REA workflow restoring the graph was verified. Hoisting the import changes ambient-module semantics.
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#4220
No description provided.