[GH-ISSUE #4609] [Bug] Assignment destructuring drops a known source-property dependency #2392

Closed
opened 2026-10-17 21:04:05 +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/4609

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 retains a resolved destructures relation for const {a: declared} = src, but omits the corresponding relation and destructuring operation for let a; ({a} = src) in the same module. The source is the known local object {a: 1}. The assignment target's binding node survives. Analysis completes with exit status 0.

Reproduction

  1. With a supported Node.js runtime, 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 src = {a: 1};
const {a: declared} = src;
let a;
({a} = src);
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 returned semantic 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']}
print('destructures targets:', sorted(nodes[r['target_node_id']]['label'] for r in g['relations'] if r['relation'] == 'destructures'))
print('a binding present:', any(n['kind'] == 'binding' and n['label'] == 'a' for n in nodes.values()))
print('destructure operations:', sum(n.get('properties', {}).get('operation_kind') == 'destructure' for n in nodes.values()))
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 ran the fixture against an extracted published npm tarball with its production dependencies installed and against a fresh source checkout after npm ci and npm run build:cached. Both CLI runs used an isolated HOME and an environment containing only HOME and PATH. The final source run and both extracted-package runs reproduced the omission with exit status 0. The equivalent local-install commands above reproduced it again with exit status 0.

Expected behavior

Retain the statically known dependency from src.a to the assigned binding a, as the declaration control retains the dependency from that same property to declared. The workflow documentation describes extraction of static property slots and object destructuring. This expectation concerns retained static evidence for a fixed local object and literal key, without requiring proof of execution or exact recovery of arbitrary dynamic patterns.

Impact and scope

Consumers restricted to destructures relations cannot obtain this known dependency from the relation set. Analysis still returns a graph and binding nodes. Overall coverage is unknown and object-flow coverage is partial, so this report does not claim false complete coverage or interpret the missing relation as proof that no dependency exists. Broader assignment patterns and downstream MCP trace behavior were not tested.

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A bounded, reproducible loss of documented static dependency evidence affects a simple local assignment while analysis and lexical bindings remain available.
  • Workaround status: unverified
  • Workaround basis: No public analysis operation was verified to restore the relation for the unchanged input; the working declaration control changes the analyzed source.

REA version

Published npm package rea-agents@6.5.0; fresh main source checkout at commit d5be402fb3dbfc56ac9854c27b5538df20cc2437. Both CLI version commands returned 6.5.0. npm and GitHub identify the 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. No native analysis provider was required.

Target and artifact context

A local directory containing only the synthetic JavaScript module above. Analysis was static; the target JavaScript was not executed.

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 lines 68-76 in the release documentation describe static property slots and object destructuring. src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:10-36 exercises declaration destructuring.
  • Failure source: The public CLI runs above, reduced from normalized_result.semantic_graph; collector admission at src/domain/javascript/javascriptSemanticObjects.ts:65-68 and graph projection at src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:29-45.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

Both builds produced:

destructures targets: ['declared']
a binding present: True
destructure operations: 1
coverage: unknown truncated: False
object-flow: partial
object-flow unknowns: 0

The retained declaration relation is resolved and its source property slot has property_path: ["a"]. There is no destructuring relation targeting a, no destructuring operation for the assignment, and no operation-specific object-flow unknown. Generic partial coverage is present independently of this missing operation.

The focused source test command npm run test:local -- src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts passed all 66 tests in one file. No source changes or proposed fix were tested.

Typed outcome or recovery expected

For the unchanged fixture, restore a destructures relation from the src.a property slot to the a binding while retaining the resolved relation to declared. A surviving lexical binding or the generic partial-coverage flag alone does not restore this property-specific dependency. No default or rest handling is needed to satisfy this check.

Analysis or suspected cause

Source-derived reasoning: the object-operation collector dispatches destructuring only for variable declarations whose IDs are object or array patterns. The assignment expression fails that admission guard and never reaches the pattern walker. Graph projection consumes the collected operations, so it cannot emit the missing relation. The published runtime contains the same declaration-only guard. This is a causal explanation from source inspection, not a tested repair.

Additional context

Related issue #4604 concerns a nested array under a default in a declaration. That case enters the declaration collector and reaches a terminal helper that accepts an AssignmentPattern only when its left side is an identifier. Correcting that recursion need not admit assignment expressions; admitting this plain assignment need not correct nested-default recursion. No patch was made to demonstrate the separation.

As an additional executed control on each build, I appended the following to the fixture:

const nested = {a: [1]};
const {a: [plain]} = nested;
const {a: [fallback] = []} = nested;

Both combined runs returned only ['declared', 'plain'] as destructuring targets, with two destructuring operations. The plain nested relation was resolved with path ["a", "0"]; the assignment and nested-default targets had no destructuring relations. Both runs exited 0 and retained the same unknown overall coverage, partial object-flow coverage, and no truncation. This report's closure signal is the plain assignment relation. No last known good release was established.

Originally created by @coygeek on GitHub (Oct 16, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4609 ### 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` retains a resolved `destructures` relation for `const {a: declared} = src`, but omits the corresponding relation and destructuring operation for `let a; ({a} = src)` in the same module. The source is the known local object `{a: 1}`. The assignment target's binding node survives. Analysis completes with exit status 0. ### Reproduction 1. With a supported Node.js runtime, 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 src = {a: 1}; const {a: declared} = src; let a; ({a} = src); 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 returned semantic 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']} print('destructures targets:', sorted(nodes[r['target_node_id']]['label'] for r in g['relations'] if r['relation'] == 'destructures')) print('a binding present:', any(n['kind'] == 'binding' and n['label'] == 'a' for n in nodes.values())) print('destructure operations:', sum(n.get('properties', {}).get('operation_kind') == 'destructure' for n in nodes.values())) 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 ran the fixture against an extracted published npm tarball with its production dependencies installed and against a fresh source checkout after `npm ci` and `npm run build:cached`. Both CLI runs used an isolated HOME and an environment containing only HOME and PATH. The final source run and both extracted-package runs reproduced the omission with exit status 0. The equivalent local-install commands above reproduced it again with exit status 0. ### Expected behavior Retain the statically known dependency from `src.a` to the assigned binding `a`, as the declaration control retains the dependency from that same property to `declared`. The workflow documentation describes extraction of static property slots and object destructuring. This expectation concerns retained static evidence for a fixed local object and literal key, without requiring proof of execution or exact recovery of arbitrary dynamic patterns. ### Impact and scope Consumers restricted to `destructures` relations cannot obtain this known dependency from the relation set. Analysis still returns a graph and binding nodes. Overall coverage is unknown and object-flow coverage is partial, so this report does not claim false complete coverage or interpret the missing relation as proof that no dependency exists. Broader assignment patterns and downstream MCP trace behavior were not tested. - Impact level: P2 - Assessment status: supported - Impact basis: A bounded, reproducible loss of documented static dependency evidence affects a simple local assignment while analysis and lexical bindings remain available. - Workaround status: unverified - Workaround basis: No public analysis operation was verified to restore the relation for the unchanged input; the working declaration control changes the analyzed source. ### REA version Published npm package `rea-agents@6.5.0`; fresh `main` source checkout at commit `d5be402fb3dbfc56ac9854c27b5538df20cc2437`. Both CLI version commands returned `6.5.0`. npm and GitHub identify the 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. No native analysis provider was required. ### Target and artifact context A local directory containing only the synthetic JavaScript module above. Analysis was static; the target JavaScript was not executed. ### 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 lines 68-76 in the release documentation describe static property slots and object destructuring. `src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts:10-36` exercises declaration destructuring. - Failure source: The public CLI runs above, reduced from `normalized_result.semantic_graph`; collector admission at `src/domain/javascript/javascriptSemanticObjects.ts:65-68` and graph projection at `src/application/javascript/JavaScriptSemanticGraphObjectProjection.ts:29-45`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete Both builds produced: ```text destructures targets: ['declared'] a binding present: True destructure operations: 1 coverage: unknown truncated: False object-flow: partial object-flow unknowns: 0 ``` The retained declaration relation is resolved and its source property slot has `property_path: ["a"]`. There is no destructuring relation targeting `a`, no destructuring operation for the assignment, and no operation-specific object-flow unknown. Generic partial coverage is present independently of this missing operation. The focused source test command `npm run test:local -- src/domain/javascript/javascriptSemanticAnalysis.dataflow.test.ts` passed all 66 tests in one file. No source changes or proposed fix were tested. ### Typed outcome or recovery expected For the unchanged fixture, restore a `destructures` relation from the `src.a` property slot to the `a` binding while retaining the resolved relation to `declared`. A surviving lexical binding or the generic partial-coverage flag alone does not restore this property-specific dependency. No default or rest handling is needed to satisfy this check. ### Analysis or suspected cause Source-derived reasoning: the object-operation collector dispatches destructuring only for variable declarations whose IDs are object or array patterns. The assignment expression fails that admission guard and never reaches the pattern walker. Graph projection consumes the collected operations, so it cannot emit the missing relation. The published runtime contains the same declaration-only guard. This is a causal explanation from source inspection, not a tested repair. ### Additional context Related [issue #4604](https://github.com/morluto/rea/issues/4604) concerns a nested array under a default in a declaration. That case enters the declaration collector and reaches a terminal helper that accepts an `AssignmentPattern` only when its left side is an identifier. Correcting that recursion need not admit assignment expressions; admitting this plain assignment need not correct nested-default recursion. No patch was made to demonstrate the separation. As an additional executed control on each build, I appended the following to the fixture: ```javascript const nested = {a: [1]}; const {a: [plain]} = nested; const {a: [fallback] = []} = nested; ``` Both combined runs returned only `['declared', 'plain']` as destructuring targets, with two destructuring operations. The plain nested relation was resolved with path `["a", "0"]`; the assignment and nested-default targets had no destructuring relations. Both runs exited 0 and retained the same unknown overall coverage, partial object-flow coverage, and no truncation. This report's closure signal is the plain assignment relation. No last known good release was established.
Author
Owner

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

Addressed in bfb7ed0bd: the plain assignment now retains the resolved src.a -> a destructuring relation alongside its declaration control.

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:6227387368 --> @morluto commented on GitHub (Oct 17, 2026): Addressed in `bfb7ed0bd`: the plain assignment now retains the resolved `src.a -> a` destructuring relation alongside its declaration control. 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. Assignment destructuring now uses the same source-identity and pattern walker as declarations, retaining the resolved src.a dependency for the assigned binding. I verified the committed source and regression coverage; I have not rerun the runtime reproduction.

<!-- gh-comment-id:6244118897 --> @coygeek commented on GitHub (Oct 18, 2026): Closing as addressed on `main` by [bfb7ed0bd](https://github.com/morluto/rea/commit/bfb7ed0bd43edc605e00fc52c6e21e735b285904). Assignment destructuring now uses the same source-identity and pattern walker as declarations, retaining the resolved src.a dependency for the assigned binding. 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#2392
No description provided.