[GH-ISSUE #4606] [Bug] Response endpoints come from conditional or later assignments #2391

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

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

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

On the tested main build, analyze-javascript-application assigns https://example.invalid/api to the response node for res.json() when the only assignment from that fetch is conditional or occurs after the read. In both fixtures, res is an incoming parameter: the consumer can read a caller-supplied response whose endpoint is unrelated to that fetch.

The ordered control keeps the correct endpoint, and a two-request ambiguity control returns a null endpoint. The published-package comparison returns null endpoints for all four fixtures, including the ordered control; the false exact endpoint attribution is observed on main only. Overall semantic graph coverage is unknown in all cases. This report concerns the exact response endpoint property, not a claim that the request executed.

Reproduction

  1. Use the source revision below with its pinned toolchain. Install locked dependencies with npm ci, then build with npm run build:cached.
  2. From the repository root, run this block. It analyzes four inert JavaScript modules with a separate fresh HOME per command, checks that the input source stays unchanged, and removes its temporary fixtures. It never imports or executes the modules or contacts their endpoint strings.
python3 - scripts/rea.mjs <<'PYREPRO'
import json
import os
from pathlib import Path
import subprocess
import sys
import tempfile

cases = {
    "ordered": 'export async function load() { const res = await fetch("https://example.invalid/api"); return res.json(); }',
    "parameter-conditional": 'export async function load(res, maybe) { if (maybe) res = await fetch("https://example.invalid/api"); return res.json(); }',
    "parameter-later": 'export async function load(res) { const value = await res.json(); res = await fetch("https://example.invalid/api"); return value; }',
    "multiple-requests": 'export async function load(maybe) { let res = await fetch("https://example.invalid/a"); if (maybe) res = await fetch("https://example.invalid/b"); return res.json(); }',
}
with tempfile.TemporaryDirectory(prefix="rea-response-") as directory:
    root = Path(directory)
    for name, source in cases.items():
        app = root / name
        app.mkdir()
        target = app / "main.js"
        target.write_text(source + "\n")
        home = root / (name + "-home")
        home.mkdir()
        result = subprocess.run(
            ["node", sys.argv[1], "analyze-javascript-application", str(app), "--json"],
            env={"PATH": os.environ["PATH"], "HOME": str(home)},
            capture_output=True, text=True, timeout=90, check=True,
        )
        graph = json.loads(result.stdout)["normalized_result"]["semantic_graph"]
        responses = [node for node in graph["nodes"] if node["kind"] == "response"]
        assert len(responses) == 1
        response = responses[0]
        edges = [edge for edge in graph["relations"] if edge["source_node_id"] == response["node_id"] and edge["relation"] == "consumed-by"]
        assert target.read_text() == source + "\n"
        print(json.dumps({"case": name, "endpoint": response["properties"]["endpoint"], "consumed_by": [edge["resolution"] for edge in edges], "graph_coverage": graph["coverage"]["status"]}))
PYREPRO

For the published-package comparison, install rea-agents@6.5.0 into a disposable directory and run the same Python block from that directory, replacing its first line with python3 - node_modules/rea-agents/scripts/rea.mjs <<'PYREPRO'. Both entry paths were executed with the pinned Node runtime. Each complete four-case block was run twice for each build, and the outputs were stable across both runs.

Expected behavior

Keep an exact response endpoint only when the receiver is attributable to that request at the read. A conditional overwrite of an incoming response leaves the caller's value possible, and an assignment after res.json() cannot supply the response consumed by that earlier call. Those cases should retain properties.endpoint: null and preserve uncertainty about the request source.

Existing response endpoint tests require null after competing request assignments or a non-request replacement. The shared initializer contract explicitly distinguishes a stable source fact from proof that an initializer executed before a read. The workflow documentation's conditional/read-position discussion concerns callback resolution and is supporting consistency evidence, rather than a separate response-specific promise.

Impact and scope

The observed failure misattributes response provenance for the two parameter fixtures. The CLI completes successfully, but an analyst relying on the exact response endpoint receives an identity the supplied source does not establish. No application code ran, input files changed, or external service was contacted. The executions exercised json() through the CLI; other consumer methods, MCP transport, and broader source shapes were not tested.

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Bounded semantic evidence integrity failure gives affected response consumers a false exact endpoint, while ordered and explicitly ambiguous controls remain distinguishable.
  • Workaround status: unverified
  • Workaround basis: No supported alternative query was demonstrated to restore the correct endpoint attribution for the same unmodified source. Manual source inspection can reveal the ambiguity but does not correct the returned graph.

REA version

Main source commit 1b56237cae, freshly built with locked dependencies. Separately installed npm package rea-agents@6.5.0, confirmed as the latest published package during this investigation. The source checkout also reports package version 6.5.0; it is not the published package's bytes.

Runtime and platform

Node.js v24.18.0 and npm 11.16.0, matching the source repository's pins; Darwin arm64. Both builds used the same Node runtime. Python 3 ran the reproduction harness. No browser or native analysis provider is required.

Target and artifact context

Local application directories containing only the inline JavaScript modules. The returned application Evidence contains the semantic graph. The .invalid URLs are inert static identifiers, and each CLI invocation receives only PATH and an isolated HOME as its environment.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Expected source: AGENTS.md (Architecture and Cleanup) requires unresolved identity to remain unknown; src/domain/javascript/javascriptSemanticAnalysis.response_endpoints.test.ts:37-61 requires null endpoints for ambiguous or replaced responses; src/domain/javascript/javascriptSemanticState.ts:184-205 distinguishes stable initialization from execution at a read position. docs/javascript-application-workflows.md:226-231 distinguishes resolved static identity from execution.
  • Failure source: the executed public CLI block above and output below. src/domain/javascript/javascriptSemanticDataEffects.ts:274-315 selects an endpoint using lexical binding/request uniqueness and initializer count; src/application/javascript/JavaScriptSemanticGraphDataProjection.ts:146-168 copies it into the public response node.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Main output, identical in both runs; every command exited zero:

{"case": "ordered", "endpoint": "https://example.invalid/api", "consumed_by": ["resolved"], "graph_coverage": "unknown"}
{"case": "parameter-conditional", "endpoint": "https://example.invalid/api", "consumed_by": ["resolved"], "graph_coverage": "unknown"}
{"case": "parameter-later", "endpoint": "https://example.invalid/api", "consumed_by": ["resolved"], "graph_coverage": "unknown"}
{"case": "multiple-requests", "endpoint": null, "consumed_by": ["candidate"], "graph_coverage": "unknown"}

For the published-package comparison, all four endpoints were null; the consumed_by and graph_coverage values matched the corresponding main rows. The existing response-endpoint test file passed all three tests on main; it does not cover either parameter fixture above.

Typed outcome or recovery expected

Re-run these fixtures: both parameter cases should return a null response endpoint, the ordered control should retain its exact endpoint, and the two-request control should retain its null endpoint and existing ambiguity. Preserve the response node and its consumer location. These controls distinguish restoring receiver attribution from suppressing all endpoints.

Analysis or suspected cause

Source-derived reasoning: the response collector finds requests syntactically assigned to the same lexical binding, then uses a single initializer as sufficient evidence for copying the unique request endpoint. It does not check whether that assignment can be skipped or occurs after this consumer. Projection copies that endpoint to the response node.

The consumed-by edge runs from the response node to its method-call site; it is not a request-to-response provenance edge. Its resolved state is included above as observed output, but this report does not require that edge to become a candidate merely because endpoint provenance is unknown. Nor does a resolved static edge prove execution.

Additional context

Closed #3328 reports lost endpoints on uniquely identified responses, and merged #3822 added endpoint preservation. This report concerns retaining an endpoint when the receiver source at the read is not established. Closed #4213 concerns endpoint trace seed selection, rather than incorrect response endpoint attribution. Searches of open and closed issues and pull requests found no report covering these conditional/future assignment fixtures. No last-known-good source revision or post-fix run was established.

Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4606 ### 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 On the tested main build, `analyze-javascript-application` assigns `https://example.invalid/api` to the response node for `res.json()` when the only assignment from that fetch is conditional or occurs after the read. In both fixtures, `res` is an incoming parameter: the consumer can read a caller-supplied response whose endpoint is unrelated to that fetch. The ordered control keeps the correct endpoint, and a two-request ambiguity control returns a null endpoint. The published-package comparison returns null endpoints for all four fixtures, including the ordered control; the false exact endpoint attribution is observed on main only. Overall semantic graph coverage is `unknown` in all cases. This report concerns the exact response endpoint property, not a claim that the request executed. ### Reproduction 1. Use the source revision below with its pinned toolchain. Install locked dependencies with `npm ci`, then build with `npm run build:cached`. 2. From the repository root, run this block. It analyzes four inert JavaScript modules with a separate fresh HOME per command, checks that the input source stays unchanged, and removes its temporary fixtures. It never imports or executes the modules or contacts their endpoint strings. ```sh python3 - scripts/rea.mjs <<'PYREPRO' import json import os from pathlib import Path import subprocess import sys import tempfile cases = { "ordered": 'export async function load() { const res = await fetch("https://example.invalid/api"); return res.json(); }', "parameter-conditional": 'export async function load(res, maybe) { if (maybe) res = await fetch("https://example.invalid/api"); return res.json(); }', "parameter-later": 'export async function load(res) { const value = await res.json(); res = await fetch("https://example.invalid/api"); return value; }', "multiple-requests": 'export async function load(maybe) { let res = await fetch("https://example.invalid/a"); if (maybe) res = await fetch("https://example.invalid/b"); return res.json(); }', } with tempfile.TemporaryDirectory(prefix="rea-response-") as directory: root = Path(directory) for name, source in cases.items(): app = root / name app.mkdir() target = app / "main.js" target.write_text(source + "\n") home = root / (name + "-home") home.mkdir() result = subprocess.run( ["node", sys.argv[1], "analyze-javascript-application", str(app), "--json"], env={"PATH": os.environ["PATH"], "HOME": str(home)}, capture_output=True, text=True, timeout=90, check=True, ) graph = json.loads(result.stdout)["normalized_result"]["semantic_graph"] responses = [node for node in graph["nodes"] if node["kind"] == "response"] assert len(responses) == 1 response = responses[0] edges = [edge for edge in graph["relations"] if edge["source_node_id"] == response["node_id"] and edge["relation"] == "consumed-by"] assert target.read_text() == source + "\n" print(json.dumps({"case": name, "endpoint": response["properties"]["endpoint"], "consumed_by": [edge["resolution"] for edge in edges], "graph_coverage": graph["coverage"]["status"]})) PYREPRO ``` For the published-package comparison, install `rea-agents@6.5.0` into a disposable directory and run the same Python block from that directory, replacing its first line with `python3 - node_modules/rea-agents/scripts/rea.mjs <<'PYREPRO'`. Both entry paths were executed with the pinned Node runtime. Each complete four-case block was run twice for each build, and the outputs were stable across both runs. ### Expected behavior Keep an exact response endpoint only when the receiver is attributable to that request at the read. A conditional overwrite of an incoming response leaves the caller's value possible, and an assignment after `res.json()` cannot supply the response consumed by that earlier call. Those cases should retain `properties.endpoint: null` and preserve uncertainty about the request source. Existing response endpoint tests require null after competing request assignments or a non-request replacement. The shared initializer contract explicitly distinguishes a stable source fact from proof that an initializer executed before a read. The workflow documentation's conditional/read-position discussion concerns callback resolution and is supporting consistency evidence, rather than a separate response-specific promise. ### Impact and scope The observed failure misattributes response provenance for the two parameter fixtures. The CLI completes successfully, but an analyst relying on the exact response endpoint receives an identity the supplied source does not establish. No application code ran, input files changed, or external service was contacted. The executions exercised `json()` through the CLI; other consumer methods, MCP transport, and broader source shapes were not tested. - Impact level: P2 - Assessment status: supported - Impact basis: Bounded semantic evidence integrity failure gives affected response consumers a false exact endpoint, while ordered and explicitly ambiguous controls remain distinguishable. - Workaround status: unverified - Workaround basis: No supported alternative query was demonstrated to restore the correct endpoint attribution for the same unmodified source. Manual source inspection can reveal the ambiguity but does not correct the returned graph. ### REA version Main source commit 1b56237caec20f1f729b4681611581a77bf8b239, freshly built with locked dependencies. Separately installed npm package `rea-agents@6.5.0`, confirmed as the latest published package during this investigation. The source checkout also reports package version 6.5.0; it is not the published package's bytes. ### Runtime and platform Node.js v24.18.0 and npm 11.16.0, matching the source repository's pins; Darwin arm64. Both builds used the same Node runtime. Python 3 ran the reproduction harness. No browser or native analysis provider is required. ### Target and artifact context Local application directories containing only the inline JavaScript modules. The returned application Evidence contains the semantic graph. The `.invalid` URLs are inert static identifiers, and each CLI invocation receives only PATH and an isolated HOME as its environment. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence - Expected source: `AGENTS.md` (Architecture and Cleanup) requires unresolved identity to remain unknown; `src/domain/javascript/javascriptSemanticAnalysis.response_endpoints.test.ts:37-61` requires null endpoints for ambiguous or replaced responses; `src/domain/javascript/javascriptSemanticState.ts:184-205` distinguishes stable initialization from execution at a read position. `docs/javascript-application-workflows.md:226-231` distinguishes resolved static identity from execution. - Failure source: the executed public CLI block above and output below. `src/domain/javascript/javascriptSemanticDataEffects.ts:274-315` selects an endpoint using lexical binding/request uniqueness and initializer count; `src/application/javascript/JavaScriptSemanticGraphDataProjection.ts:146-168` copies it into the public response node. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Main output, identical in both runs; every command exited zero: ```jsonl {"case": "ordered", "endpoint": "https://example.invalid/api", "consumed_by": ["resolved"], "graph_coverage": "unknown"} {"case": "parameter-conditional", "endpoint": "https://example.invalid/api", "consumed_by": ["resolved"], "graph_coverage": "unknown"} {"case": "parameter-later", "endpoint": "https://example.invalid/api", "consumed_by": ["resolved"], "graph_coverage": "unknown"} {"case": "multiple-requests", "endpoint": null, "consumed_by": ["candidate"], "graph_coverage": "unknown"} ``` For the published-package comparison, all four endpoints were null; the `consumed_by` and `graph_coverage` values matched the corresponding main rows. The existing response-endpoint test file passed all three tests on main; it does not cover either parameter fixture above. ### Typed outcome or recovery expected Re-run these fixtures: both parameter cases should return a null response endpoint, the ordered control should retain its exact endpoint, and the two-request control should retain its null endpoint and existing ambiguity. Preserve the response node and its consumer location. These controls distinguish restoring receiver attribution from suppressing all endpoints. ### Analysis or suspected cause Source-derived reasoning: the response collector finds requests syntactically assigned to the same lexical binding, then uses a single initializer as sufficient evidence for copying the unique request endpoint. It does not check whether that assignment can be skipped or occurs after this consumer. Projection copies that endpoint to the response node. The `consumed-by` edge runs from the response node to its method-call site; it is not a request-to-response provenance edge. Its resolved state is included above as observed output, but this report does not require that edge to become a candidate merely because endpoint provenance is unknown. Nor does a resolved static edge prove execution. ### Additional context Closed [#3328](https://github.com/morluto/rea/issues/3328) reports lost endpoints on uniquely identified responses, and merged [#3822](https://github.com/morluto/rea/pull/3822) added endpoint preservation. This report concerns retaining an endpoint when the receiver source at the read is not established. Closed [#4213](https://github.com/morluto/rea/issues/4213) concerns endpoint trace seed selection, rather than incorrect response endpoint attribution. Searches of open and closed issues and pull requests found no report covering these conditional/future assignment fixtures. No last-known-good source revision or post-fix run was established.
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#2391
No description provided.