[GH-ISSUE #6508] [Bug] Promise source resolution links a pre-initialization read to a future factory #3904

Closed
opened 2026-10-31 00:45:06 +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/6508

Before submitting

  • I searched open issues for related reports.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

Evidence, artifacts, or reconstruction

Observed behavior

analyze-javascript-application reports source_resolution: "complete" for task.then(...) and emits a resolved chains edge from a Promise.resolve(1) initializer that occurs later. The source's read is in the temporal dead zone: a separate execution completes normally with its catch handler recording "ReferenceError", so the earlier chain cannot consume the later Promise.

Reproduction

The exact fixture below was analyzed once during initial investigation and executed once in a separate Node process. Place it in app/main.mjs:

export let observed;
try { observed = task.then(value => value); }
catch (error) { observed = error.name; }
const task = Promise.resolve(1);

Run the source CLI with the pinned Node runtime. REA_CHECKOUT denotes the checkout; app is the absolute fixture directory. The original run used this argument sequence with disposable paths:

node "$REA_CHECKOUT/scripts/rea.mjs" analyze-javascript-application "$PWD/app" --format json

Inspect the Promise nodes and chains relationships in normalized_result.semantic_graph. The CLI exits 0 and links the line-4 factory to the line-2 chain as resolved. Separately importing this inert module and printing JSON.stringify(m.observed) returned "ReferenceError", exit status 0.

A separately analyzed positive control initialized the binding before its read and retained a complete source and a resolved chain:

const task = Promise.resolve(1);
export const result = task.then(value => value);

Expected behavior

A source Promise identity cannot be resolved to a binding initializer that has not executed at the read. Preserve an unresolved/candidate relationship or explain the unavailable binding instead of asserting complete provenance. The normal initialized-before-read control must remain resolved. REA's documented identity rule says variable and class initializers must precede use (artifact reconstruction); ECMAScript GetBindingValue throws for an uninitialized lexical binding. This case needs only straight-line source ordering, not general runtime execution or interprocedural reachability.

Impact and scope

The emitted resolved source relationship points to a Promise that cannot supply the read in this valid, successfully completing module. Ownership/provenance traces can thereby invent a connection across initialization. The exact caught-TDZ fixture and an earlier uncaught read-before-initializer fixture both exhibited complete source resolution; the latter was analyzed only and was not used as the runtime baseline. Other ordering forms were not exhaustively tested.

REA version

6.7.0 source build at 4c547008c53ee9f0ff2368e7d874a2c3347aa235. This reports the stated revision, which is ahead of the published 6.7.0 tag; it does not assert reproduction on the release tag.

Runtime and platform

Node.js v24.18.0; Linux x86_64. No browser or native analysis provider was involved.

Target and artifact context

A disposable directory containing the JavaScript fixture shown above. Analysis used an isolated HOME with REA/client override environment variables removed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Observed node/relationship excerpts, with graph IDs replaced by their roles:

{"method":"then","operation_kind":"chain","ownership":"assigned","source_resolution":"complete"}
{"source":"line-4 Promise.resolve","target":"line-2 task.then","relation":"chains","resolution":"resolved"}

The aggregate semantic result contained no promise-ownership unknown for this relationship. The separate Node runtime printed:

"ReferenceError"
  • Evidence provenance: observed public CLI and separate fixture-execution output; causal explanation below is source-derived.
  • Local verification: reproduced.
  • Reproduction completeness: complete.
  • Expected source: the documented initialization-before-use identity rule and ECMAScript GetBindingValue contract linked above.
  • Failure source: complete source resolution and the resolved chains relationship from the later initializer to the earlier read.

Typed outcome or recovery expected

An unresolved or candidate Promise source with explicit initialization/order uncertainty; no resolved chain to the later initializer. Preserve the resolved positive control.

Analysis or suspected cause

Source-derived: resolvePromiseExpression resolves an identifier's single immutable initializer without consulting its read position or initialization state. It recursively selects that initializer's Promise candidate and marks the source complete. The graph projector then emits a resolved chains edge. Other value/identity owners use read-position selection; this path does not.

Additional context

Targeted open-issue searches included Promise in:title, "Promise" "initializer", "promise" "source_resolution", "promise-ownership", and "chaining"; no matching open report was found. Open issue #5302 concerns export return-value evaluation of an early var read, rather than this independent Promise-source collector and its resolved semantic edge. Closed issues were not searched or attested. A broader unauthenticated search was rate limited; the parent subsequently completed an authenticated open-issue search for Promise initialization without finding a duplicate. No fix was made, and no additional runtime re-verification was run after drafting.

Restoration criterion: this fixture must no longer resolve the line-2 chain to the line-4 factory, while initializing task before the read must still produce a resolved chain. Keeping only a generic dynamic-call unknown elsewhere in the graph does not remove the contradictory resolved source edge.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded valid JavaScript input produces an incorrect resolved Promise relationship in the public static-analysis result. Automated ownership/provenance consumers can follow that false fact; no target mutation or broad service failure was observed.
  • Workaround status: unverified
  • Workaround basis: Manual inspection of the retained source can expose the mismatch, but no CLI setting or end-to-end consumer workaround was verified to suppress just these incorrect Promise relationships.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6508 ## Before submitting - [x] I searched open issues for related reports. - [x] I removed credentials, tokens, private paths, and sensitive target data. ## Area Evidence, artifacts, or reconstruction ## Observed behavior `analyze-javascript-application` reports `source_resolution: "complete"` for `task.then(...)` and emits a resolved `chains` edge from a `Promise.resolve(1)` initializer that occurs later. The source's read is in the temporal dead zone: a separate execution completes normally with its catch handler recording `"ReferenceError"`, so the earlier chain cannot consume the later Promise. ## Reproduction The exact fixture below was analyzed once during initial investigation and executed once in a separate Node process. Place it in `app/main.mjs`: ```js export let observed; try { observed = task.then(value => value); } catch (error) { observed = error.name; } const task = Promise.resolve(1); ``` Run the source CLI with the pinned Node runtime. `REA_CHECKOUT` denotes the checkout; `app` is the absolute fixture directory. The original run used this argument sequence with disposable paths: ```sh node "$REA_CHECKOUT/scripts/rea.mjs" analyze-javascript-application "$PWD/app" --format json ``` Inspect the Promise nodes and `chains` relationships in `normalized_result.semantic_graph`. The CLI exits 0 and links the line-4 factory to the line-2 chain as resolved. Separately importing this inert module and printing `JSON.stringify(m.observed)` returned `"ReferenceError"`, exit status 0. A separately analyzed positive control initialized the binding before its read and retained a complete source and a resolved chain: ```js const task = Promise.resolve(1); export const result = task.then(value => value); ``` ## Expected behavior A source Promise identity cannot be resolved to a binding initializer that has not executed at the read. Preserve an unresolved/candidate relationship or explain the unavailable binding instead of asserting complete provenance. The normal initialized-before-read control must remain resolved. REA's documented identity rule says variable and class initializers must precede use ([artifact reconstruction](https://github.com/morluto/rea/blob/4c547008c53ee9f0ff2368e7d874a2c3347aa235/docs/javascript-artifact-reconstruction.md#L470-L477)); ECMAScript [GetBindingValue](https://tc39.es/ecma262/multipage/executable-code-and-execution-contexts.html#sec-declarative-environment-records-getbindingvalue-n-s) throws for an uninitialized lexical binding. This case needs only straight-line source ordering, not general runtime execution or interprocedural reachability. ## Impact and scope The emitted resolved source relationship points to a Promise that cannot supply the read in this valid, successfully completing module. Ownership/provenance traces can thereby invent a connection across initialization. The exact caught-TDZ fixture and an earlier uncaught read-before-initializer fixture both exhibited complete source resolution; the latter was analyzed only and was not used as the runtime baseline. Other ordering forms were not exhaustively tested. ## REA version 6.7.0 source build at `4c547008c53ee9f0ff2368e7d874a2c3347aa235`. This reports the stated revision, which is ahead of the published 6.7.0 tag; it does not assert reproduction on the release tag. ## Runtime and platform Node.js v24.18.0; Linux x86_64. No browser or native analysis provider was involved. ## Target and artifact context A disposable directory containing the JavaScript fixture shown above. Analysis used an isolated HOME with REA/client override environment variables removed. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Observed node/relationship excerpts, with graph IDs replaced by their roles: ```json {"method":"then","operation_kind":"chain","ownership":"assigned","source_resolution":"complete"} {"source":"line-4 Promise.resolve","target":"line-2 task.then","relation":"chains","resolution":"resolved"} ``` The aggregate semantic result contained no `promise-ownership` unknown for this relationship. The separate Node runtime printed: ```json "ReferenceError" ``` - Evidence provenance: observed public CLI and separate fixture-execution output; causal explanation below is source-derived. - Local verification: reproduced. - Reproduction completeness: complete. - Expected source: the documented initialization-before-use identity rule and ECMAScript GetBindingValue contract linked above. - Failure source: complete source resolution and the resolved `chains` relationship from the later initializer to the earlier read. ## Typed outcome or recovery expected An unresolved or candidate Promise source with explicit initialization/order uncertainty; no resolved chain to the later initializer. Preserve the resolved positive control. ## Analysis or suspected cause Source-derived: [`resolvePromiseExpression`](https://github.com/morluto/rea/blob/4c547008c53ee9f0ff2368e7d874a2c3347aa235/src/domain/javascript/javascriptSemanticPromises.ts#L439-L465) resolves an identifier's single immutable initializer without consulting its read position or initialization state. It recursively selects that initializer's Promise candidate and marks the source complete. The [graph projector](https://github.com/morluto/rea/blob/4c547008c53ee9f0ff2368e7d874a2c3347aa235/src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts#L125-L148) then emits a resolved `chains` edge. Other value/identity owners use read-position selection; this path does not. ## Additional context Targeted open-issue searches included `Promise in:title`, `"Promise" "initializer"`, `"promise" "source_resolution"`, `"promise-ownership"`, and `"chaining"`; no matching open report was found. Open issue #5302 concerns export return-value evaluation of an early `var` read, rather than this independent Promise-source collector and its resolved semantic edge. Closed issues were not searched or attested. A broader unauthenticated search was rate limited; the parent subsequently completed an authenticated open-issue search for Promise initialization without finding a duplicate. No fix was made, and no additional runtime re-verification was run after drafting. Restoration criterion: this fixture must no longer resolve the line-2 chain to the line-4 factory, while initializing `task` before the read must still produce a resolved chain. Keeping only a generic dynamic-call unknown elsewhere in the graph does not remove the contradictory resolved source edge. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A bounded valid JavaScript input produces an incorrect resolved Promise relationship in the public static-analysis result. Automated ownership/provenance consumers can follow that false fact; no target mutation or broad service failure was observed. - Workaround status: unverified - Workaround basis: Manual inspection of the retained source can expose the mismatch, but no CLI setting or end-to-end consumer workaround was verified to suppress just these incorrect Promise relationships.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

resolvePromiseExpression calls semanticBindingAtRead and refuses unordered or nondefinitely initialized bindings. The exact early task.then pattern produced sourceResolution unresolved and no sourcePromiseIds; initialized-before-read control produced complete and one source Promise ID. Existing regression checks both outcomes.

Implementation: javascriptSemanticPromises.ts, javascriptSemanticAnalysis.initialization_boundaries.test.ts.

The relevant regression checks in javascriptSemanticAnalysis.initialization_boundaries.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380748711 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. resolvePromiseExpression calls semanticBindingAtRead and refuses unordered or nondefinitely initialized bindings. The exact early task.then pattern produced sourceResolution unresolved and no sourcePromiseIds; initialized-before-read control produced complete and one source Promise ID. Existing regression checks both outcomes. Implementation: [javascriptSemanticPromises.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticPromises.ts#L443), [javascriptSemanticAnalysis.initialization_boundaries.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticAnalysis.initialization_boundaries.test.ts#L158). The relevant regression checks in [javascriptSemanticAnalysis.initialization_boundaries.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticAnalysis.initialization_boundaries.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#3904
No description provided.