[GH-ISSUE #5585] [Bug] Backward JavaScript provenance loses the value of a directly returned property #3393

Closed
opened 2026-10-31 00:39:51 +00:00 by bsakaguchi · 1 comment
Owner

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

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

analyze-javascript-application followed by trace-javascript-semantics with direction: "backward-provenance" loses the known string "products" when a local function directly returns settings.index. Returning the same property through a local variable, or assigning it directly to a binding, retains the literal. Enabling include_ambiguous_dynamic_edges does not recover it. Analysis and all six trace commands exit 0.

Reproduction

From the source checkout with dependencies installed and the CLI built, run the following. The independent verifier ran these commands and then replayed the embedded reproduction successfully. The fixture needs no external provider or target execution.

checkout=$PWD
probe=$(mktemp -d)
mkdir -p "$probe"/home "$probe"/config "$probe"/data "$probe"/cache "$probe"/state "$probe"/fixture
rea_probe() {
  env -i PATH="$PATH" HOME="$probe/home" \
    XDG_CONFIG_HOME="$probe/config" XDG_DATA_HOME="$probe/data" \
    XDG_CACHE_HOME="$probe/cache" XDG_STATE_HOME="$probe/state" \
    node "$checkout/scripts/rea.mjs" "$@"
}
cat > "$probe/fixture/package.json" <<'JSON'
{"name":"return-probe","version":"1.0.0"}
JSON
cat > "$probe/fixture/index.js" <<'JS'
const settings = { index: "products" };
function viaLocal() {
  const value = settings.index;
  return value;
}
function viaMember() {
  return settings.index;
}
const a = viaLocal();
const b = viaMember();
const direct = settings.index;
console.log(a, b, direct);
JS
rea_probe analyze-javascript-application "$probe/fixture" --format json \
  > "$probe/application.json"
for binding in a b direct; do
  for candidates in false true; do
    jq --arg binding "$binding" --argjson candidates "$candidates" '
      {application: ., query: {
        seed: {kind: "semantic-node", node_id: (
          .normalized_result.semantic_graph.nodes[] |
          select(.kind == "binding" and .label == $binding) | .node_id
        )},
        direction: "backward-provenance",
        include_ambiguous_dynamic_edges: $candidates
      }}' "$probe/application.json" > "$probe/query-$binding-$candidates.json"
    rea_probe trace-javascript-semantics \
      "$probe/query-$binding-$candidates.json" --format json \
      > "$probe/trace-$binding-$candidates.json"
    jq --arg binding "$binding" --argjson candidates "$candidates" '
      .normalized_result | {
        binding: $binding, candidates: $candidates, status,
        nodes: .summary.traversed_nodes,
        products: any(.nodes[];
          .kind == "literal" and .properties.value == "products")
      }' "$probe/trace-$binding-$candidates.json"
  done
done

Expected behavior

Backward provenance from b should retain the returned literal "products" through resolved value flow with candidates disabled, as the two controls do, and keep it when candidates are enabled. docs/javascript-application-workflows.md:165-180 documents complete reachable subgraphs, static property slots and reads, uniquely resolved local calls, and argument/return flow. This fixture uses a fixed local object and static property name. An equivalent resolved connection is sufficient; no particular internal graph topology is required.

Impact and scope

Bounded loss of a known returned configuration value in the supported semantic trace. The result explicitly reports ambiguity; this is not a claim of false certainty or complete analysis. For editable inputs, the reproduced viaLocal rewrite restores the literal. That workaround is unavailable to callers required to analyze unchanged artifacts. No security boundary or target mutation is involved; broader syntax, MCP execution, and other platforms were not tested.

REA version

6.7.0; source commit 074768901f3cdc8205b61773fb1faf6f6c7b3ca8.

Runtime and platform

Node.js v26.12.0; Darwin arm64. Built source CLI with the checkout's supplied dependencies and artifacts; no native provider required.

Target and artifact context

Synthetic local JavaScript application containing only the package manifest and module above; authenticated application Evidence and semantic trace results. Static analysis only.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Reduced from the six CLI results by the jq expression above:

binding  candidates  status     nodes  products
 a       false       found      8      true
 a       true        found      8      true
 b       false       ambiguous  4      false
 b       true        ambiguous  7      false
 direct  false       found      3      true
 direct  true        found      3      true

The retained application graph has a resolved reads-property relation for the read inside viaMember, but neither b trace contains the property slot or literal. The only incoming relation to that return site is a candidate aliases relation from the receiver settings. Candidate traversal reaches the receiver without recovering the property's value. The affected binding also retains an explicit unsupported-call-expression unknown.

Typed outcome or recovery expected

The same b query should reach "products" with candidates disabled and enabled while preserving the working a and direct controls. Preserve unrelated explicit uncertainty: changing only the status or adding receiver nodes does not restore value provenance.

Analysis or suspected cause

Source-derived reasoning: src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts:193-216 connects return sites to retained references, while src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:21-44 projects property reads separately. The observed read is disconnected from the returned value. This corroborates a missing value-flow connection; no repair was tested.

Additional context

Prepared with AI assistance and independently reproduced through the built public CLI. Before submission, origin/main was refreshed to c2858d2aa0916f0dd2d073d848b8c2195d9bf6b3; the cited documentation and both projection files are unchanged from the tested baseline. This is a source recheck, not a runtime replay at that newer revision. Open and closed issues and PRs, including merged PRs, were searched; related destructuring, stale-return-literal, and alias-expansion reports concern different behavior.

Originally created by @coygeek on GitHub (Oct 20, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5585 ### 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 `analyze-javascript-application` followed by `trace-javascript-semantics` with `direction: "backward-provenance"` loses the known string `"products"` when a local function directly returns `settings.index`. Returning the same property through a local variable, or assigning it directly to a binding, retains the literal. Enabling `include_ambiguous_dynamic_edges` does not recover it. Analysis and all six trace commands exit 0. ### Reproduction From the source checkout with dependencies installed and the CLI built, run the following. The independent verifier ran these commands and then replayed the embedded reproduction successfully. The fixture needs no external provider or target execution. ```sh checkout=$PWD probe=$(mktemp -d) mkdir -p "$probe"/home "$probe"/config "$probe"/data "$probe"/cache "$probe"/state "$probe"/fixture rea_probe() { env -i PATH="$PATH" HOME="$probe/home" \ XDG_CONFIG_HOME="$probe/config" XDG_DATA_HOME="$probe/data" \ XDG_CACHE_HOME="$probe/cache" XDG_STATE_HOME="$probe/state" \ node "$checkout/scripts/rea.mjs" "$@" } cat > "$probe/fixture/package.json" <<'JSON' {"name":"return-probe","version":"1.0.0"} JSON cat > "$probe/fixture/index.js" <<'JS' const settings = { index: "products" }; function viaLocal() { const value = settings.index; return value; } function viaMember() { return settings.index; } const a = viaLocal(); const b = viaMember(); const direct = settings.index; console.log(a, b, direct); JS rea_probe analyze-javascript-application "$probe/fixture" --format json \ > "$probe/application.json" for binding in a b direct; do for candidates in false true; do jq --arg binding "$binding" --argjson candidates "$candidates" ' {application: ., query: { seed: {kind: "semantic-node", node_id: ( .normalized_result.semantic_graph.nodes[] | select(.kind == "binding" and .label == $binding) | .node_id )}, direction: "backward-provenance", include_ambiguous_dynamic_edges: $candidates }}' "$probe/application.json" > "$probe/query-$binding-$candidates.json" rea_probe trace-javascript-semantics \ "$probe/query-$binding-$candidates.json" --format json \ > "$probe/trace-$binding-$candidates.json" jq --arg binding "$binding" --argjson candidates "$candidates" ' .normalized_result | { binding: $binding, candidates: $candidates, status, nodes: .summary.traversed_nodes, products: any(.nodes[]; .kind == "literal" and .properties.value == "products") }' "$probe/trace-$binding-$candidates.json" done done ``` ### Expected behavior Backward provenance from `b` should retain the returned literal `"products"` through resolved value flow with candidates disabled, as the two controls do, and keep it when candidates are enabled. `docs/javascript-application-workflows.md:165-180` documents complete reachable subgraphs, static property slots and reads, uniquely resolved local calls, and argument/return flow. This fixture uses a fixed local object and static property name. An equivalent resolved connection is sufficient; no particular internal graph topology is required. ### Impact and scope Bounded loss of a known returned configuration value in the supported semantic trace. The result explicitly reports ambiguity; this is not a claim of false certainty or complete analysis. For editable inputs, the reproduced `viaLocal` rewrite restores the literal. That workaround is unavailable to callers required to analyze unchanged artifacts. No security boundary or target mutation is involved; broader syntax, MCP execution, and other platforms were not tested. ### REA version 6.7.0; source commit `074768901f3cdc8205b61773fb1faf6f6c7b3ca8`. ### Runtime and platform Node.js v26.12.0; Darwin arm64. Built source CLI with the checkout's supplied dependencies and artifacts; no native provider required. ### Target and artifact context Synthetic local JavaScript application containing only the package manifest and module above; authenticated application Evidence and semantic trace results. Static analysis only. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence Reduced from the six CLI results by the jq expression above: ```text binding candidates status nodes products a false found 8 true a true found 8 true b false ambiguous 4 false b true ambiguous 7 false direct false found 3 true direct true found 3 true ``` The retained application graph has a resolved `reads-property` relation for the read inside `viaMember`, but neither `b` trace contains the property slot or literal. The only incoming relation to that return site is a candidate `aliases` relation from the receiver `settings`. Candidate traversal reaches the receiver without recovering the property's value. The affected binding also retains an explicit unsupported-call-expression unknown. ### Typed outcome or recovery expected The same `b` query should reach `"products"` with candidates disabled and enabled while preserving the working `a` and `direct` controls. Preserve unrelated explicit uncertainty: changing only the status or adding receiver nodes does not restore value provenance. ### Analysis or suspected cause Source-derived reasoning: `src/application/javascript/JavaScriptSemanticGraphFlowProjection.ts:193-216` connects return sites to retained references, while `src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:21-44` projects property reads separately. The observed read is disconnected from the returned value. This corroborates a missing value-flow connection; no repair was tested. ### Additional context Prepared with AI assistance and independently reproduced through the built public CLI. Before submission, `origin/main` was refreshed to `c2858d2aa0916f0dd2d073d848b8c2195d9bf6b3`; the cited documentation and both projection files are unchanged from the tested baseline. This is a source recheck, not a runtime replay at that newer revision. Open and closed issues and PRs, including merged PRs, were searched; related destructuring, stale-return-literal, and alias-expansion reports concern different behavior.
Author
Owner

@morluto commented on GitHub (Oct 21, 2026):

Closing as fixed on main by github.com/morluto/rea@53e0943223, retained at 67a6477f01.

Semantic flow projection now connects directly returned member reads to their object-read occurrences. The graph-builder regression reaches the products literal through backward provenance with candidate edges disabled and enabled while preserving explicit unknown controls.

This closure follows current-main producer and regression source inspection and the existing issue evidence. Tests and runtime reproductions were not rerun in this audit; no additional platform/provider coverage is implied.

<!-- gh-comment-id:6324271277 --> @morluto commented on GitHub (Oct 21, 2026): Closing as fixed on main by https://github.com/morluto/rea/commit/53e0943223b016c5e1782aa76f3ec2297dc4313b, retained at 67a6477f011ae358fc333e5a6e26c86d932654ed. Semantic flow projection now connects directly returned member reads to their object-read occurrences. The graph-builder regression reaches the products literal through backward provenance with candidate edges disabled and enabled while preserving explicit unknown controls. This closure follows current-main producer and regression source inspection and the existing issue evidence. Tests and runtime reproductions were not rerun in this audit; no additional platform/provider coverage is implied.
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#3393
No description provided.