[PR #1577] [MERGED] fix(javascript): retain runtime scope in bundled module relationships #2984

Closed
opened 2026-10-17 21:12:32 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1577
Author: @dakjdakd
Created: 10/10/2026
Status: ✅ Merged
Merged: 10/10/2026
Merged by: @N0zoM1z0

Base: main ← Head: fix/webpack-runtime-scope


📝 Commits (1)

  • 294426d fix(javascript): retain runtime scope in bundled module relationships

📊 Changes

14 files changed (+396 additions, -43 deletions)

View changed files

📝 src/application/javascript/ElectronBoundaryGraphContext.ts (+6 -4)
📝 src/application/javascript/ElectronBoundaryGraphIpc.ts (+2 -0)
📝 src/application/javascript/ElectronBoundaryGraphNative.ts (+6 -1)
📝 src/application/javascript/ElectronBoundaryGraphWindows.ts (+18 -3)
📝 src/application/javascript/JavaScriptArtifactGraphBundlers.ts (+2 -2)
📝 src/application/javascript/JavaScriptArtifactGraphContext.ts (+11 -4)
📝 src/application/javascript/JavaScriptArtifactGraphFindings.ts (+35 -15)
📝 src/domain/javascript/electronStaticAnalysisTypes.ts (+12 -0)
📝 src/domain/javascript/javascriptStaticAnalysisBundler.ts (+13 -4)
📝 src/domain/javascript/javascriptStaticAnalysisFindings.ts (+8 -4)
📝 src/domain/javascript/javascriptStaticAnalysisModuleIndex.test.ts (+33 -6)
📝 src/domain/javascript/javascriptStaticAnalysisState.ts (+1 -0)
📝 src/domain/javascript/javascriptStaticAnalysisTypes.ts (+8 -0)
➕ tests/boundary/cli/javascriptBundlerRuntimeScopes.test.ts (+241 -0)

📄 Description

Summary

Fixes #1576.

Preserve a recovered factory's runtime from static parsing through application graph projection. Previously, two Webpack/Rspack runtimes in the same asset could reuse module IDs and cause one runtime's entrypoint, dependencies and behavior to attach to the other's factories. This change makes module ownership resolve through (asset path, runtime, module ID) and uses collision-free tuple encoding for module and chunk lookups.

The public graph already distinguishes module identities by runtime namespace. This PR makes lookup and finding ownership consistent with those identities, while retaining shared module resolution across chunks of the same runtime.

Problem and expected behavior

Consider two registrations in bundle.js, one named webpackChunkAppA and another named webpackChunkAppB. Both define module 1, which requires module 2 and calls a different endpoint. The graph creates four distinct module nodes, but the lookup used to connect them previously stored only the asset path and module ID. Registering AppB overwrote AppA's entries.

The observable contradiction was an AppA entry edge with runtime: "webpackChunkAppA" and resolution_status: "resolved" pointing at a node with namespace webpackChunkAppB:module. The call to /a retained its AppA source range but originated at AppB module 1. Both require operations could resolve through AppB's 1 and 2 factories. Successful graph construction and complete coverage did not disclose this incorrect attribution.

The expected invariant is straightforward: every bundled finding belongs to its recovered factory's runtime, and a runtime's entry or require lookup may select only that runtime's modules. Registrations in different chunks of the same runtime still share their module namespace. Registration order must not change this meaning.

The identity tuple also needs an unambiguous encoding. A runtime string ending in \0x and a module string beginning with x\0 can collide if variable-length fields are joined with NUL. The added regression includes those strings and analogous chunk keys, so runtime separation covers the actual source-string domain rather than only conventional identifier spellings.

Change and scope

The change starts at the producer instead of recovering ownership from graph labels later, following the existing architecture ownership rules and behavioral testing guidance.

javascriptStaticAnalysisState.ts adds runtime to JavaScriptModuleRange. javascriptStaticAnalysisBundler.ts passes the registration's runtime into factory recovery, including the existing esbuild wrapper runtime names. Each indexed factory range now contains the identity context needed to attribute expressions encountered during the shared traversal.

javascriptStaticAnalysisFindings.ts attaches module_runtime when finalizing a finding inside a recovered factory. Findings outside factories retain their previous shape. javascriptStaticAnalysisTypes.ts and electronStaticAnalysisTypes.ts declare the field as optional, allowing existing internal finding values to remain representable without claiming a runtime they do not contain. The existing range index and its overlap precedence are unchanged.

The application projection consumes that same ownership in every affected path:

  • JavaScriptArtifactGraphContext.ts encodes module and chunk lookup tuples with the existing compositeKey utility. sourceNodeFor requires runtime when selecting a recovered bundled module.
  • JavaScriptArtifactGraphBundlers.ts registers and resolves modules using the registration runtime, including runtime entry loads.
  • JavaScriptArtifactGraphFindings.ts carries runtime into reference, endpoint, storage and discovered-role ownership. Require targets use the source finding's runtime; asynchronous chunk candidate selection also filters by that runtime before using the chunk lookup.
  • ElectronBoundaryGraphContext.ts shares the same source lookup. ElectronBoundaryGraphWindows.ts, ElectronBoundaryGraphIpc.ts and ElectronBoundaryGraphNative.ts pass runtime for windows, bridge APIs, utility processes, IPC operations, sender validations and native-addon findings.

The remaining two files provide the behavioral regression and strengthen the existing producer coverage. These 14 files implement one ownership correction across the producer and its consumers; there is no new service, dependency or alternate graph-building path.

This does not add cross-file runtime reconstruction or change ordinary source-module resolution. If an older internal finding lacks runtime, it cannot select a bundled module by ID alone. Existing unresolved-reference projection remains available, rather than confidently choosing another runtime's factory.

Contract and boundary impact

  • Semantic owner and earliest changed stage: Factory recovery owns the registration runtime. Its indexed range now retains that ownership before static finding finalization; application and Electron projection consume it.
  • CLI and MCP/tool-catalog contract: No request, operation, public JSON Schema or tool-catalog changes. The existing analyze-javascript-application output has corrected relationship ownership. module_runtime is intermediate static-analysis data and is not added to the public normalized result schema.
  • Provider, bridge, target-format, or platform compatibility: Shared static JavaScript projection covers existing Webpack/Rspack registrations and esbuild wrappers. No bridge or provider protocol changes. Validation ran on Windows; the tuple and attribution changes have no platform-specific branch.
  • Evidence, artifact, provenance, or reconstruction contract: Module nodes keep their existing runtime namespaces and artifact-bound identities. Relationships keep the original source ranges, digests and evidence kinds. Their source/target selection now agrees with the recovered runtime. Module/chunk lookup encoding is internal and does not replace public node identity formatting.
  • Process execution, authorization, cleanup, or containment impact: None. Target JavaScript continues to be parsed as data. No application factories, network calls or Electron constructors in the fixture are executed.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact: None. No dependency, package version, installation logic, authored skill or tracked generated contract changes. Generated-document checks passed, and build outputs are excluded from the commit.

Evidence and regression coverage

  • Tests added or updated: tests/boundary/cli/javascriptBundlerRuntimeScopes.test.ts adds seven compiled CLI scenarios. src/domain/javascript/javascriptStaticAnalysisModuleIndex.test.ts now checks runtime as well as module key for recovered references across its existing factory-range fixture.
  • Base reproduction or other evidence: The regression fixtures reproduce incorrect ownership with colliding IDs, reversed registration order and mixed Webpack/Rspack runtimes on the affected implementation. Single-runtime, disjoint-ID and same-runtime cross-chunk controls distinguish runtime identity loss from general parsing failure. The source-string tuple fixture demonstrates why NUL-containing runtime/module keys must remain distinct. All seven scenarios pass with this correction.
  • User-visible CLI/MCP output: The new tests invoke the compiled CLI, parse its returned Evidence through the public result schema, and inspect the actual application graph. AppA's entry target and /a owner are now AppA module 1; its require target is AppA module 2. The corresponding AppB relationships stay in AppB. Storage, BrowserWindow and factory-level async-chunk relationships are checked in the same returned graph.
  • Remaining proof gaps: Verification establishes bounded static reconstruction through the compiled CLI on Windows; MCP shares the application workflow but was not separately exercised for these fixtures. The full deterministic suite remains a CI check. These fixtures do not establish arbitrary bootstrap equivalence, production incidence, or additional cross-file recovery behavior.

The seven CLI scenarios cover reused numeric IDs, reversed registrations, one runtime, disjoint IDs, mixed Webpack/Rspack runtimes, multiple chunks sharing one runtime, and delimiter-containing runtime/module/chunk strings. The existing self-reference CLI tests also run alongside them to preserve the graph's self-edge omission behavior. Existing esbuild/Vite and Electron composition tests verify the affected consumer families without introducing another fixture framework.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

Validation used Node.js 24.18.0 and npm 11.16.0, as pinned by the repository. The branch was rebased onto main revision c19e15a2b3f09b3f693d7eab5263d83f4d2d1cd4; the CLI, source, full static and generated-document checks below were rerun on that baseline. The branch contains one focused fix commit, 294426d5.

  • npm run test:focused -- tests/boundary/cli/javascriptBundlerRuntimeScopes.test.ts tests/boundary/cli/javascriptBundlerSelfReferences.test.ts — 10 tests passed across two files: seven runtime-scope cases and three existing self-reference cases. This also built the runtime required by the compiled CLI boundary.
  • npm run test:local -- src/domain/javascript/javascriptStaticAnalysisModuleIndex.test.ts tests/composition/javascript-applications/javascriptBundlerAdapters.test.ts tests/composition/javascript-applications/electronApplicationAnalysis.test.ts — 13 tests passed across three files. The strengthened module-index assertions were also rerun successfully.
  • npm run check — passed, including typecheck, lint, resolved module boundaries, repository formatting, knip, metadata and architecture guards.
  • npm run docs:check — passed against the final implementation, including the generated catalog, skill, evidence and error-schema freshness checks.
  • npm run verify:test-discovery — passed; all 736 test files were discovered exactly once.
  • npm run verify:architecture-guards — passed across 1,025 source files.
  • git diff --check — passed. The pre-commit formatting and lint checks also passed.

Compatibility, safety, and release

  • Breaking changes or migration steps: None. Public requests and result schemas are unchanged. Corrected graph relationships may change graph content and derived graph digests for previously misattributed bundles, which is the intended behavioral correction.
  • Real Hopper/Ghidra, browser, or OS coverage: The real compiled REA CLI was exercised on Windows with inert JavaScript fixtures, matching the static analysis boundary changed here.
  • Package or release metadata impact: None. No version bump, lockfile update, package-layout change or generated artifact is included.
  • Security, privacy, process, or containment review: Source strings remain data, including NUL-containing keys. Tuple encoding prevents distinct source identities from aliasing in lookup maps. The fix adds no target execution, external communication, credentials or filesystem mutation beyond existing test fixture materialization.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1577 **Author:** [@dakjdakd](https://github.com/dakjdakd) **Created:** 10/10/2026 **Status:** ✅ Merged **Merged:** 10/10/2026 **Merged by:** [@N0zoM1z0](https://github.com/N0zoM1z0) **Base:** `main` ← **Head:** `fix/webpack-runtime-scope` --- ### 📝 Commits (1) - [`294426d`](https://github.com/morluto/rea/commit/294426d5f88b4aef1c732f87b200b1a4a57cfadf) fix(javascript): retain runtime scope in bundled module relationships ### 📊 Changes **14 files changed** (+396 additions, -43 deletions) <details> <summary>View changed files</summary> 📝 `src/application/javascript/ElectronBoundaryGraphContext.ts` (+6 -4) 📝 `src/application/javascript/ElectronBoundaryGraphIpc.ts` (+2 -0) 📝 `src/application/javascript/ElectronBoundaryGraphNative.ts` (+6 -1) 📝 `src/application/javascript/ElectronBoundaryGraphWindows.ts` (+18 -3) 📝 `src/application/javascript/JavaScriptArtifactGraphBundlers.ts` (+2 -2) 📝 `src/application/javascript/JavaScriptArtifactGraphContext.ts` (+11 -4) 📝 `src/application/javascript/JavaScriptArtifactGraphFindings.ts` (+35 -15) 📝 `src/domain/javascript/electronStaticAnalysisTypes.ts` (+12 -0) 📝 `src/domain/javascript/javascriptStaticAnalysisBundler.ts` (+13 -4) 📝 `src/domain/javascript/javascriptStaticAnalysisFindings.ts` (+8 -4) 📝 `src/domain/javascript/javascriptStaticAnalysisModuleIndex.test.ts` (+33 -6) 📝 `src/domain/javascript/javascriptStaticAnalysisState.ts` (+1 -0) 📝 `src/domain/javascript/javascriptStaticAnalysisTypes.ts` (+8 -0) ➕ `tests/boundary/cli/javascriptBundlerRuntimeScopes.test.ts` (+241 -0) </details> ### 📄 Description ## Summary Fixes #1576. Preserve a recovered factory's runtime from static parsing through application graph projection. Previously, two Webpack/Rspack runtimes in the same asset could reuse module IDs and cause one runtime's entrypoint, dependencies and behavior to attach to the other's factories. This change makes module ownership resolve through `(asset path, runtime, module ID)` and uses collision-free tuple encoding for module and chunk lookups. The public graph already distinguishes module identities by runtime namespace. This PR makes lookup and finding ownership consistent with those identities, while retaining shared module resolution across chunks of the same runtime. ## Problem and expected behavior Consider two registrations in `bundle.js`, one named `webpackChunkAppA` and another named `webpackChunkAppB`. Both define module `1`, which requires module `2` and calls a different endpoint. The graph creates four distinct module nodes, but the lookup used to connect them previously stored only the asset path and module ID. Registering AppB overwrote AppA's entries. The observable contradiction was an AppA entry edge with `runtime: "webpackChunkAppA"` and `resolution_status: "resolved"` pointing at a node with namespace `webpackChunkAppB:module`. The call to `/a` retained its AppA source range but originated at AppB module `1`. Both require operations could resolve through AppB's `1` and `2` factories. Successful graph construction and `complete` coverage did not disclose this incorrect attribution. The expected invariant is straightforward: every bundled finding belongs to its recovered factory's runtime, and a runtime's entry or require lookup may select only that runtime's modules. Registrations in different chunks of the same runtime still share their module namespace. Registration order must not change this meaning. The identity tuple also needs an unambiguous encoding. A runtime string ending in `\0x` and a module string beginning with `x\0` can collide if variable-length fields are joined with NUL. The added regression includes those strings and analogous chunk keys, so runtime separation covers the actual source-string domain rather than only conventional identifier spellings. ## Change and scope The change starts at the producer instead of recovering ownership from graph labels later, following the existing [architecture ownership rules](https://github.com/morluto/rea/blob/0e19db9fce74af2081144102f5787cf90a450244/AGENTS.md) and [behavioral testing guidance](https://github.com/morluto/rea/blob/0e19db9fce74af2081144102f5787cf90a450244/docs/testing.md). `javascriptStaticAnalysisState.ts` adds runtime to `JavaScriptModuleRange`. `javascriptStaticAnalysisBundler.ts` passes the registration's runtime into factory recovery, including the existing esbuild wrapper runtime names. Each indexed factory range now contains the identity context needed to attribute expressions encountered during the shared traversal. `javascriptStaticAnalysisFindings.ts` attaches `module_runtime` when finalizing a finding inside a recovered factory. Findings outside factories retain their previous shape. `javascriptStaticAnalysisTypes.ts` and `electronStaticAnalysisTypes.ts` declare the field as optional, allowing existing internal finding values to remain representable without claiming a runtime they do not contain. The existing range index and its overlap precedence are unchanged. The application projection consumes that same ownership in every affected path: - `JavaScriptArtifactGraphContext.ts` encodes module and chunk lookup tuples with the existing `compositeKey` utility. `sourceNodeFor` requires runtime when selecting a recovered bundled module. - `JavaScriptArtifactGraphBundlers.ts` registers and resolves modules using the registration runtime, including runtime entry loads. - `JavaScriptArtifactGraphFindings.ts` carries runtime into reference, endpoint, storage and discovered-role ownership. Require targets use the source finding's runtime; asynchronous chunk candidate selection also filters by that runtime before using the chunk lookup. - `ElectronBoundaryGraphContext.ts` shares the same source lookup. `ElectronBoundaryGraphWindows.ts`, `ElectronBoundaryGraphIpc.ts` and `ElectronBoundaryGraphNative.ts` pass runtime for windows, bridge APIs, utility processes, IPC operations, sender validations and native-addon findings. The remaining two files provide the behavioral regression and strengthen the existing producer coverage. These 14 files implement one ownership correction across the producer and its consumers; there is no new service, dependency or alternate graph-building path. This does not add cross-file runtime reconstruction or change ordinary source-module resolution. If an older internal finding lacks runtime, it cannot select a bundled module by ID alone. Existing unresolved-reference projection remains available, rather than confidently choosing another runtime's factory. ## Contract and boundary impact - **Semantic owner and earliest changed stage:** Factory recovery owns the registration runtime. Its indexed range now retains that ownership before static finding finalization; application and Electron projection consume it. - **CLI and MCP/tool-catalog contract:** No request, operation, public JSON Schema or tool-catalog changes. The existing `analyze-javascript-application` output has corrected relationship ownership. `module_runtime` is intermediate static-analysis data and is not added to the public normalized result schema. - **Provider, bridge, target-format, or platform compatibility:** Shared static JavaScript projection covers existing Webpack/Rspack registrations and esbuild wrappers. No bridge or provider protocol changes. Validation ran on Windows; the tuple and attribution changes have no platform-specific branch. - **Evidence, artifact, provenance, or reconstruction contract:** Module nodes keep their existing runtime namespaces and artifact-bound identities. Relationships keep the original source ranges, digests and evidence kinds. Their source/target selection now agrees with the recovered runtime. Module/chunk lookup encoding is internal and does not replace public node identity formatting. - **Process execution, authorization, cleanup, or containment impact:** None. Target JavaScript continues to be parsed as data. No application factories, network calls or Electron constructors in the fixture are executed. - **Generated metadata (`docs/public/product-catalog.json`), package, or installation impact:** None. No dependency, package version, installation logic, authored skill or tracked generated contract changes. Generated-document checks passed, and build outputs are excluded from the commit. ## Evidence and regression coverage - **Tests added or updated:** `tests/boundary/cli/javascriptBundlerRuntimeScopes.test.ts` adds seven compiled CLI scenarios. `src/domain/javascript/javascriptStaticAnalysisModuleIndex.test.ts` now checks runtime as well as module key for recovered references across its existing factory-range fixture. - **Base reproduction or other evidence:** The regression fixtures reproduce incorrect ownership with colliding IDs, reversed registration order and mixed Webpack/Rspack runtimes on the affected implementation. Single-runtime, disjoint-ID and same-runtime cross-chunk controls distinguish runtime identity loss from general parsing failure. The source-string tuple fixture demonstrates why NUL-containing runtime/module keys must remain distinct. All seven scenarios pass with this correction. - **User-visible CLI/MCP output:** The new tests invoke the compiled CLI, parse its returned Evidence through the public result schema, and inspect the actual application graph. AppA's entry target and `/a` owner are now AppA module `1`; its require target is AppA module `2`. The corresponding AppB relationships stay in AppB. Storage, `BrowserWindow` and factory-level async-chunk relationships are checked in the same returned graph. - **Remaining proof gaps:** Verification establishes bounded static reconstruction through the compiled CLI on Windows; MCP shares the application workflow but was not separately exercised for these fixtures. The full deterministic suite remains a CI check. These fixtures do not establish arbitrary bootstrap equivalence, production incidence, or additional cross-file recovery behavior. The seven CLI scenarios cover reused numeric IDs, reversed registrations, one runtime, disjoint IDs, mixed Webpack/Rspack runtimes, multiple chunks sharing one runtime, and delimiter-containing runtime/module/chunk strings. The existing self-reference CLI tests also run alongside them to preserve the graph's self-edge omission behavior. Existing esbuild/Vite and Electron composition tests verify the affected consumer families without introducing another fixture framework. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed Validation used Node.js 24.18.0 and npm 11.16.0, as pinned by the repository. The branch was rebased onto main revision `c19e15a2b3f09b3f693d7eab5263d83f4d2d1cd4`; the CLI, source, full static and generated-document checks below were rerun on that baseline. The branch contains one focused fix commit, `294426d5`. - `npm run test:focused -- tests/boundary/cli/javascriptBundlerRuntimeScopes.test.ts tests/boundary/cli/javascriptBundlerSelfReferences.test.ts` — **10 tests passed** across two files: seven runtime-scope cases and three existing self-reference cases. This also built the runtime required by the compiled CLI boundary. - `npm run test:local -- src/domain/javascript/javascriptStaticAnalysisModuleIndex.test.ts tests/composition/javascript-applications/javascriptBundlerAdapters.test.ts tests/composition/javascript-applications/electronApplicationAnalysis.test.ts` — **13 tests passed** across three files. The strengthened module-index assertions were also rerun successfully. - `npm run check` — **passed**, including typecheck, lint, resolved module boundaries, repository formatting, knip, metadata and architecture guards. - `npm run docs:check` — **passed** against the final implementation, including the generated catalog, skill, evidence and error-schema freshness checks. - `npm run verify:test-discovery` — **passed**; all **736** test files were discovered exactly once. - `npm run verify:architecture-guards` — **passed** across **1,025** source files. - `git diff --check` — **passed**. The pre-commit formatting and lint checks also passed. ## Compatibility, safety, and release - **Breaking changes or migration steps:** None. Public requests and result schemas are unchanged. Corrected graph relationships may change graph content and derived graph digests for previously misattributed bundles, which is the intended behavioral correction. - **Real Hopper/Ghidra, browser, or OS coverage:** The real compiled REA CLI was exercised on Windows with inert JavaScript fixtures, matching the static analysis boundary changed here. - **Package or release metadata impact:** None. No version bump, lockfile update, package-layout change or generated artifact is included. - **Security, privacy, process, or containment review:** Source strings remain data, including NUL-containing keys. Tuple encoding prevents distinct source identities from aliasing in lookup maps. The fix adds no target execution, external communication, credentials or filesystem mutation beyond existing test fixture materialization. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-17 21:12:32 +00:00
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#2984
No description provided.