[GH-ISSUE #4604] [Bug] JavaScript graph drops a known nested-default destructuring dependency #2389

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

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

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

rea analyze-javascript-application <directory> --json completes with exit status 0, but adding an unused default to a nested array pattern removes its destructuring operation and destructures relation. In the fixture below, plain retains the obj.a[0] dependency while fallback loses it. Both bindings remain in the graph. The same fixture also has no destructuring operations or relations for the object-rest target rest or array-rest target ys.

Reproduction

  1. Use a supported Node.js runtime and create the fixture and an isolated HOME:
case_root="$(mktemp -d)"
mkdir "$case_root/fixture" "$case_root/home"
cat > "$case_root/fixture/main.js" <<'JS'
const obj = {a: [1], b: 2};
const arr = [1, 2, 3];
const {a: [plain]} = obj;
const {a: [fallback] = []} = obj;
const {a, ...rest} = obj;
const [x, ...ys] = arr;
const {b: scalar = 0} = obj;
JS
  1. Install the published package locally and analyze without inherited client configuration:
env -i HOME="$case_root/home" PATH="$PATH" npm install \
  --prefix "$case_root/runner" --no-audit --no-fund rea-agents@6.5.0
env -i HOME="$case_root/home" PATH="$PATH" node \
  "$case_root/runner/node_modules/rea-agents/scripts/rea.mjs" \
  analyze-javascript-application "$case_root/fixture" --json \
  > "$case_root/result.json" 2> "$case_root/progress.ndjson"
  1. Inspect the graph:
python3 - "$case_root/result.json" <<'PY'
import json, sys
g = json.load(open(sys.argv[1]))['normalized_result']['semantic_graph']
nodes = {n['node_id']: n for n in g['nodes']}
wanted = {'a', 'plain', 'fallback', 'rest', 'x', 'ys', 'scalar'}
print('operations:', sum(n.get('properties', {}).get('operation_kind') == 'destructure' for n in nodes.values()))
print('relation targets:', sorted(nodes[r['target_node_id']]['label'] for r in g['relations'] if r['relation'] == 'destructures'))
print('binding targets:', sorted(n['label'] for n in nodes.values() if n['kind'] == 'binding' and n['label'] in wanted))
print('coverage:', g['coverage']['status'], 'truncated:', g['coverage']['truncated'])
print('object-flow:', next(f['status'] for f in g['coverage']['families'] if f['family'] == 'object-flow'))
print('object-flow unknowns:', sum(u['family'] == 'object-flow' for u in g['unknowns']))
PY

I also ran the same fixture and inspection against a fresh source checkout after npm ci and npm run build:cached, using node scripts/rea.mjs as the entry point. Both runs used isolated HOME directories with only HOME and PATH inherited by analysis. The omission reproduced once on each tested build, two runs total, with exit status 0 on both.

Expected behavior

The documented extractor covers object destructuring. For this fixed input, the nested default does not run because obj.a is present. The known obj.a[0] dependency should remain represented for fallback, as it is for the plain nested control. The expectation concerns retained static evidence, not proof of execution or exact evaluation of arbitrary default expressions.

The overall unknown coverage and partial object-flow coverage are conservative signals, and I do not interpret missing relations as proven absence. They do not identify this particular loss of a statically known dependency. No documentation or test I found designates nested-default destructuring as an intentional omission. The rest omissions are related observations from the same collector; I am not assuming that every unprojectable pattern must receive an exact rest path or its own object-flow unknown.

Impact and scope

The graph loses a known nested destructuring dependency in this small valid module on both tested builds. Callers using destructures relations cannot recover that dependency from the relation set. Lexical binding nodes survive, analysis succeeds, and the graph reports unknown overall coverage and partial object-flow coverage. No false complete-coverage result, broader application impact, or downstream MCP trace behavior was observed.

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded, reproducible loss of documented static destructuring evidence affects a known local projection while the analysis and lexical bindings remain available.
  • Workaround status: unverified
  • Workaround basis: No alternative public analysis operation was verified to restore the missing relation for the unchanged input. Rewriting away defaults or rest was not verified as a practical equivalent workaround.

REA version

Published npm package rea-agents@6.5.0, with CLI version output 6.5.0; fresh main source checkout at commit 1b56237caec20f1f729b4681611581a77bf8b239, also reporting CLI version 6.5.0. npm and GitHub identify that package as the latest release.

Runtime and platform

Node.js v24.18.0; npm 11.16.0; Darwin arm64. The source build used the repository's pinned toolchain and locked dependencies. Static JavaScript analysis required no native provider.

Target and artifact context

A local directory containing the synthetic main.js above. No Electron, Webpack, external service, or execution of the target JavaScript is involved.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Expected source: docs/javascript-application-workflows.md:177-185 on the tested source revision, and the release documentation at lines 68-76, describe object destructuring coverage. src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts:760-803 checks retained nested destructuring relations.
  • Failure source: The two public CLI runs above, reduced from normalized_result.semantic_graph; collector branches at src/domain/javascript/javascriptSemanticObjects.ts:128-168 and :244-248.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Both tested builds produced exactly:

operations: 4
relation targets: ['a', 'plain', 'scalar', 'x']
binding targets: ['a', 'fallback', 'plain', 'rest', 'scalar', 'x', 'ys']
coverage: unknown truncated: False
object-flow: partial
object-flow unknowns: 0

The plain nested control retains property_path: ["a", "0"]; the scalar-default control retains ["b"]. The returned omission counters are zero, with no truncation limits. These counters describe retained graph output rather than syntax completeness. Separate data-flow unknowns exist for the rest bindings; the result has no object-flow unknown for these omitted operations.

Typed outcome or recovery expected

With this fixture, retain the nested-default dependency to fallback while preserving the existing relations to a, plain, scalar, and x. A conservative candidate is acceptable if the default prevents exact resolution. Keeping only the lexical binding or the broad partial-coverage flag does not restore the missing dependency. Do not invent exact rest projections or change intentionally conservative dynamic-key behavior.

Analysis or suspected cause

Source-derived reasoning: collectDestructuring visits plain object and array patterns. Its terminal helper accepts an AssignmentPattern only when its left side is an identifier, so the nested array target under a default produces no operation. The same walker skips object RestElement properties and array rest elements. src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:29-45 projects retained operations, so it cannot recover those omitted by the collector.

src/domain/javascript/javascriptSemanticIr.ts:377-387 permits partial destructuring candidates and nullable paths. However, src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:39-63 deliberately omits operations for dynamic computed keys. This report therefore does not assume a universal per-pattern operation or unknown guarantee. src/application/javascript/JavaScriptSemanticGraphProjection.ts:170-192 marks supported families partial independently of these syntax cases.

Additional context

Related issue #4490 concerns Electron member provenance through a Webpack external and missing windows/IPC output. This fixture uses ordinary local values and tests missing object-flow operation records. No last known good release was established, and no fix was attempted.

Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4604 ### 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 `rea analyze-javascript-application <directory> --json` completes with exit status 0, but adding an unused default to a nested array pattern removes its destructuring operation and `destructures` relation. In the fixture below, `plain` retains the `obj.a[0]` dependency while `fallback` loses it. Both bindings remain in the graph. The same fixture also has no destructuring operations or relations for the object-rest target `rest` or array-rest target `ys`. ### Reproduction 1. Use a supported Node.js runtime and create the fixture and an isolated HOME: ```sh case_root="$(mktemp -d)" mkdir "$case_root/fixture" "$case_root/home" cat > "$case_root/fixture/main.js" <<'JS' const obj = {a: [1], b: 2}; const arr = [1, 2, 3]; const {a: [plain]} = obj; const {a: [fallback] = []} = obj; const {a, ...rest} = obj; const [x, ...ys] = arr; const {b: scalar = 0} = obj; JS ``` 2. Install the published package locally and analyze without inherited client configuration: ```sh env -i HOME="$case_root/home" PATH="$PATH" npm install \ --prefix "$case_root/runner" --no-audit --no-fund rea-agents@6.5.0 env -i HOME="$case_root/home" PATH="$PATH" node \ "$case_root/runner/node_modules/rea-agents/scripts/rea.mjs" \ analyze-javascript-application "$case_root/fixture" --json \ > "$case_root/result.json" 2> "$case_root/progress.ndjson" ``` 3. Inspect the graph: ```sh python3 - "$case_root/result.json" <<'PY' import json, sys g = json.load(open(sys.argv[1]))['normalized_result']['semantic_graph'] nodes = {n['node_id']: n for n in g['nodes']} wanted = {'a', 'plain', 'fallback', 'rest', 'x', 'ys', 'scalar'} print('operations:', sum(n.get('properties', {}).get('operation_kind') == 'destructure' for n in nodes.values())) print('relation targets:', sorted(nodes[r['target_node_id']]['label'] for r in g['relations'] if r['relation'] == 'destructures')) print('binding targets:', sorted(n['label'] for n in nodes.values() if n['kind'] == 'binding' and n['label'] in wanted)) print('coverage:', g['coverage']['status'], 'truncated:', g['coverage']['truncated']) print('object-flow:', next(f['status'] for f in g['coverage']['families'] if f['family'] == 'object-flow')) print('object-flow unknowns:', sum(u['family'] == 'object-flow' for u in g['unknowns'])) PY ``` I also ran the same fixture and inspection against a fresh source checkout after `npm ci` and `npm run build:cached`, using `node scripts/rea.mjs` as the entry point. Both runs used isolated HOME directories with only HOME and PATH inherited by analysis. The omission reproduced once on each tested build, two runs total, with exit status 0 on both. ### Expected behavior The documented extractor covers object destructuring. For this fixed input, the nested default does not run because `obj.a` is present. The known `obj.a[0]` dependency should remain represented for `fallback`, as it is for the plain nested control. The expectation concerns retained static evidence, not proof of execution or exact evaluation of arbitrary default expressions. The overall unknown coverage and partial object-flow coverage are conservative signals, and I do not interpret missing relations as proven absence. They do not identify this particular loss of a statically known dependency. No documentation or test I found designates nested-default destructuring as an intentional omission. The rest omissions are related observations from the same collector; I am not assuming that every unprojectable pattern must receive an exact rest path or its own object-flow unknown. ### Impact and scope The graph loses a known nested destructuring dependency in this small valid module on both tested builds. Callers using `destructures` relations cannot recover that dependency from the relation set. Lexical binding nodes survive, analysis succeeds, and the graph reports unknown overall coverage and partial object-flow coverage. No false complete-coverage result, broader application impact, or downstream MCP trace behavior was observed. - Impact level: P2 - Assessment status: supported - Impact basis: A bounded, reproducible loss of documented static destructuring evidence affects a known local projection while the analysis and lexical bindings remain available. - Workaround status: unverified - Workaround basis: No alternative public analysis operation was verified to restore the missing relation for the unchanged input. Rewriting away defaults or rest was not verified as a practical equivalent workaround. ### REA version Published npm package `rea-agents@6.5.0`, with CLI version output `6.5.0`; fresh `main` source checkout at commit `1b56237caec20f1f729b4681611581a77bf8b239`, also reporting CLI version `6.5.0`. npm and GitHub identify that package as the latest release. ### Runtime and platform Node.js `v24.18.0`; npm `11.16.0`; Darwin arm64. The source build used the repository's pinned toolchain and locked dependencies. Static JavaScript analysis required no native provider. ### Target and artifact context A local directory containing the synthetic `main.js` above. No Electron, Webpack, external service, or execution of the target JavaScript is involved. ### Interface CLI ### Earliest failure stage Native analysis or reconstruction ### Relevant output or evidence - Expected source: `docs/javascript-application-workflows.md:177-185` on the tested source revision, and the release documentation at lines 68-76, describe object destructuring coverage. `src/application/javascript/JavaScriptSemanticGraphBuilder.test.ts:760-803` checks retained nested destructuring relations. - Failure source: The two public CLI runs above, reduced from `normalized_result.semantic_graph`; collector branches at `src/domain/javascript/javascriptSemanticObjects.ts:128-168` and `:244-248`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Both tested builds produced exactly: ```text operations: 4 relation targets: ['a', 'plain', 'scalar', 'x'] binding targets: ['a', 'fallback', 'plain', 'rest', 'scalar', 'x', 'ys'] coverage: unknown truncated: False object-flow: partial object-flow unknowns: 0 ``` The plain nested control retains `property_path: ["a", "0"]`; the scalar-default control retains `["b"]`. The returned omission counters are zero, with no truncation limits. These counters describe retained graph output rather than syntax completeness. Separate data-flow unknowns exist for the rest bindings; the result has no object-flow unknown for these omitted operations. ### Typed outcome or recovery expected With this fixture, retain the nested-default dependency to `fallback` while preserving the existing relations to `a`, `plain`, `scalar`, and `x`. A conservative candidate is acceptable if the default prevents exact resolution. Keeping only the lexical binding or the broad partial-coverage flag does not restore the missing dependency. Do not invent exact rest projections or change intentionally conservative dynamic-key behavior. ### Analysis or suspected cause Source-derived reasoning: `collectDestructuring` visits plain object and array patterns. Its terminal helper accepts an `AssignmentPattern` only when its left side is an identifier, so the nested array target under a default produces no operation. The same walker skips object `RestElement` properties and array rest elements. `src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:29-45` projects retained operations, so it cannot recover those omitted by the collector. `src/domain/javascript/javascriptSemanticIr.ts:377-387` permits partial destructuring candidates and nullable paths. However, `src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:39-63` deliberately omits operations for dynamic computed keys. This report therefore does not assume a universal per-pattern operation or unknown guarantee. `src/application/javascript/JavaScriptSemanticGraphProjection.ts:170-192` marks supported families partial independently of these syntax cases. ### Additional context Related [issue #4490](https://github.com/morluto/rea/issues/4490) concerns Electron member provenance through a Webpack external and missing windows/IPC output. This fixture uses ordinary local values and tests missing object-flow operation records. No last known good release was established, and no fix was attempted.
Author
Owner

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

Addressed the nested-default dependency in bfb7ed0bd: the public CLI retains resolved nested.a[0] -> fallback alongside plain and scalar-default controls. Rest observations remain outside this fix; no exact rest projection is claimed.

The collector now shares source identity and one iterative pattern walker across declaration and assignment syntax. Nested default targets retain their source paths. Existing scalar-default occurrence identities are preserved; absent/unknown source slots remain candidate relations with object-flow uncertainty. Dynamic keys, rest projections, and member targets remain conservative rather than gaining invented exact paths.

Validation on Node 24.18.0: new graph controls before the fix were 10 failed/2 passed; final 405 focused tests across five files passed, including public CLI relations and property paths, dataflow, member mutation, and reference lifetime controls. Synthetic source was parsed statically; no target code or native provider was executed.

Bounded public CLI comparison against immediate parent c3e42385e: two alternating warm repeats per build plus separate first processes. Warm medians baseline/candidate: elapsed 1.2622/1.2263 s, CPU 1.2687/1.2731 s, sampled process-family RSS 522698752/525770752 bytes, raw reply tokens 19965/20903 (cl100k_base). The candidate restores two missing relations, so the replies intentionally differ. These repeats do not establish a speed or token improvement; true cold, whole-agent/schema/request/retry costs and native-provider costs are unmeasured. Heavy Qodo tests remained stopped.

<!-- gh-comment-id:6227388457 --> @morluto commented on GitHub (Oct 17, 2026): Addressed the nested-default dependency in `bfb7ed0bd`: the public CLI retains resolved `nested.a[0] -> fallback` alongside `plain` and scalar-default controls. Rest observations remain outside this fix; no exact rest projection is claimed. The collector now shares source identity and one iterative pattern walker across declaration and assignment syntax. Nested default targets retain their source paths. Existing scalar-default occurrence identities are preserved; absent/unknown source slots remain candidate relations with object-flow uncertainty. Dynamic keys, rest projections, and member targets remain conservative rather than gaining invented exact paths. Validation on Node 24.18.0: new graph controls before the fix were 10 failed/2 passed; final 405 focused tests across five files passed, including public CLI relations and property paths, dataflow, member mutation, and reference lifetime controls. Synthetic source was parsed statically; no target code or native provider was executed. Bounded public CLI comparison against immediate parent c3e42385e: two alternating warm repeats per build plus separate first processes. Warm medians baseline/candidate: elapsed 1.2622/1.2263 s, CPU 1.2687/1.2731 s, sampled process-family RSS 522698752/525770752 bytes, raw reply tokens 19965/20903 (cl100k_base). The candidate restores two missing relations, so the replies intentionally differ. These repeats do not establish a speed or token improvement; true cold, whole-agent/schema/request/retry costs and native-provider costs are unmeasured. Heavy Qodo tests remained stopped.
Author
Owner

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

Closing as addressed on main by bfb7ed0bd. The shared destructuring walker now traverses nested default targets and retains the nested.a[0] dependency to fallback alongside the plain and scalar-default controls, without inventing exact rest projections. I verified the committed source and regression coverage; I have not rerun the runtime reproduction.

<!-- gh-comment-id:6244121238 --> @coygeek commented on GitHub (Oct 18, 2026): Closing as addressed on `main` by [bfb7ed0bd](https://github.com/morluto/rea/commit/bfb7ed0bd43edc605e00fc52c6e21e735b285904). The shared destructuring walker now traverses nested default targets and retains the nested.a[0] dependency to fallback alongside the plain and scalar-default controls, without inventing exact rest projections. I verified the committed source and regression coverage; I have not rerun the runtime reproduction.
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#2389
No description provided.