[GH-ISSUE #2654] fix(javascript): avoid stale return literals after container getter writes #955

Closed
opened 2026-10-15 18:29:52 +00:00 by bsakaguchi · 0 comments
Owner

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

Summary

Analyzing an exported function can infer its old data-property value after a write through a getter that returns its receiver. The failure occurs when the receiver literal is an array element, including an array nested in an object, or is assigned to a previously declared binding. Saving the array element's getter result in a local alias also produces the stale value. Fresh source tests report literal 1 for these four cases and preserve it in the production export-return-shape projection with complete coverage.

Steps to reproduce

In the source checkout described below, save this source-only test as src/domain/javascript/container-getter-receiver-repro.test.ts. It analyzes text and does not execute the supplied application function.

import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";
import { projectJavaScriptModuleSemantics } from "./javascriptModuleSemanticIr.js";
import { projectJavaScriptExportReturnShapes } from "./javascriptExportReturnShapeProjection.js";
import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js";

it("records the stale array receiver return shape", () => {
  const source = `export function result() {
    const arr = [{ x: 1, get self() { return this; } }];
    arr[0].self.x = 2;
    return arr[0].x;
  }`;
  const module = projectJavaScriptModuleSemantics(analyzeJavaScriptSemantics(source));
  const link = module.moduleLinks.find(item => item.exportedName === "result");
  if (link === undefined) throw new Error("Missing result export");
  const projection = projectJavaScriptExportReturnShapes({
    ir: module, link, modulePath: "app.js", baseCoverage: completeApplicationCoverage(),
  });
  console.log(JSON.stringify(projection?.properties));
  expect(projection?.properties.static_return_shapes).toMatchObject([
    { value_status: "literal", fields: [{ path: "", state: "literal", value: 1 }] },
  ]);
  expect(projection?.properties.return_shape_coverage).toMatchObject({
    status: "complete", projection_complete: true,
  });
});

Run the source-only focused test:

npm run test:focused -- src/domain/javascript/container-getter-receiver-repro.test.ts

The equivalent analyzer, module-retention and return-shape projection calls were executed in a bounded focused verifier test. The standalone test above is a compact runnable version of those calls; its exact filename and command were not used in the verifier run. Its assertions record the current wrong output, so a passing assertion reproduces the anomaly.

The supported public route accepts this function in app.js inside an ordinary readable directory through analyze-javascript-application with directory format. Public adapter reachability was traced in source only. CLI, MCP, the isolated source worker and the released package were not run in this verification.

Expected behavior

The return-value lattice must preserve uncertainty when a member write can change the object read by a direct return. The application workflow documentation describes conservative uncertainty for mutable references, shared children and accessors. Here the own getter returns the exact array element used as its receiver. The write changes that object's x property to 2 before the direct return. Reporting unknown is consistent with the existing conservative analyzer contract; an accurately justified value 2 would also satisfy the value contract. Literal 1 does not.

Actual behavior

The executed analyzer reports { "status": "literal", "value": 1 }. Its exported projection contains the following fields:

{
  "static_return_shapes": [
    {
      "value_status": "literal",
      "fields": [
        { "path": "", "presence": "present", "state": "literal", "value": 1, "reason": null }
      ],
      "property_coverage": []
    }
  ],
  "return_shape_coverage": {
    "status": "complete",
    "retained_return_sites": 1,
    "omitted_return_sites": 0,
    "omitted_fields": 0,
    "omitted_property_coverage": 0,
    "projection_complete": true
  }
}

The same stale output was observed for an object containing the array, a receiver literal assigned with let box; box = ..., and a saved alias of arr[0].self. A direct arr[0].x = 2 write, a directly declared receiver getter write and an object-property receiver getter write each returned unknown. Reading the array element without a write retained literal 1. These observations distinguish the receiver-reference failure from unsupported array indexing or blanket loss of literal precision. No application fixture was executed to obtain a runtime return value; the value 2 follows from the explicit getter and assignment semantics.

Evidence

  • Expected source: docs/javascript-application-workflows.md:185-202 specifies direct return evaluation and conservative reference uncertainty; src/domain/javascript/javascriptSemanticMemberMutations.ts:45 defines preservation of property-write uncertainty. src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts:12-99 corroborates getter receiver tracking for directly declared and nested object-property owners.
  • Failure source: The bounded verifier source tests observed the analyzer and production export projection results shown above. Relevant locators are src/domain/javascript/javascriptSemanticObjectReferences.ts:266-343, src/domain/javascript/javascriptSemanticMemberMutations.ts:663-665, and src/domain/javascript/javascriptExportReturnShapeProjection.ts:24-84.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The executed run contained ten observation tests, including the exact supplied top-level binding form, all four exported variants, controls and an editable-source workaround. It passed all assertions in 35ms with exit 0. The assertions establish the wrong output rather than correctness. Production projectJavaScriptModuleSemantics retained each exact export/callable link and the return value; projectJavaScriptExportReturnShapes then emitted the stale literal and complete projection fields.

Public reachability was checked in source only. The supported directory-analysis entry point is src/cli/electronCommands.ts:188, and the application export-return-shape consumer is src/application/javascript/JavaScriptModuleRelationships.ts:144-226. The fixture is valid JavaScript in a readable directory with no source exclusion. The public route requires normal successful worker/resource admission; worker startup, transfer and adapter execution were not tested.

Related: #1494 and #1665 cover earlier getter failures. Their closure comments verify their earlier specific cases, while the independently tested container and assignment forms above still fail at this checkout.

Related: #2567 concerns property-write operation classification. This report concerns stale return-value inference after a direct assignment recognized by the mutation pass.

Restoration check

Analyze the array receiver fixture and the three observed variants. The return shape must no longer publish stale literal 1; preserve unknown unless the post-write value can be justified. Keep the no-write array read at literal 1. The existing direct receiver, nested object-property receiver and direct array-write controls must continue to suppress the old value. The source module/export projection must retain the corrected result so downstream callers receive the same conservative value.

Runtime or environment

Source checkout a2d8b94dd50dab8d966239dd63ab84718aa8b4b4, tested with Node 24.18.0 and npm 11.16.0. Registry metadata identifies rea-agents 6.4.0 as the latest published package; released runtime behavior and byte equivalence with this checkout were not tested. Current upstream source recheck at 3ae826ceba6f25b08bce50bcb51001209bc16b57 found the relevant receiver resolver, member-mutation traversal, value evaluation, and export projection unchanged; no new runtime test was run, and the observed failure remains supported by the earlier execution and current source trace.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Valid local source can produce a wrong literal in the exported return-shape inference. The observed failure is bounded to receiver-reference forms; no severe downstream incident or broad core-workflow failure was demonstrated.
  • Workaround status: available
  • Workaround basis: For editable source, the tested form const box = { x: 1, get self() { return this; } }; const arr = [box]; arr[0].self.x = 2; return arr[0].x; returns unknown. This preserves the fixture's object identity and return behavior. Callers who must analyze unchanged original artifacts cannot use this restructuring without changing their evidence input; an equivalent workaround for that constraint was not verified.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2654 ## Summary Analyzing an exported function can infer its old data-property value after a write through a getter that returns its receiver. The failure occurs when the receiver literal is an array element, including an array nested in an object, or is assigned to a previously declared binding. Saving the array element's getter result in a local alias also produces the stale value. Fresh source tests report literal 1 for these four cases and preserve it in the production export-return-shape projection with complete coverage. ## Steps to reproduce In the source checkout described below, save this source-only test as `src/domain/javascript/container-getter-receiver-repro.test.ts`. It analyzes text and does not execute the supplied application function. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; import { projectJavaScriptModuleSemantics } from "./javascriptModuleSemanticIr.js"; import { projectJavaScriptExportReturnShapes } from "./javascriptExportReturnShapeProjection.js"; import { completeApplicationCoverage } from "./javascriptApplicationEvidenceSchemas.js"; it("records the stale array receiver return shape", () => { const source = `export function result() { const arr = [{ x: 1, get self() { return this; } }]; arr[0].self.x = 2; return arr[0].x; }`; const module = projectJavaScriptModuleSemantics(analyzeJavaScriptSemantics(source)); const link = module.moduleLinks.find(item => item.exportedName === "result"); if (link === undefined) throw new Error("Missing result export"); const projection = projectJavaScriptExportReturnShapes({ ir: module, link, modulePath: "app.js", baseCoverage: completeApplicationCoverage(), }); console.log(JSON.stringify(projection?.properties)); expect(projection?.properties.static_return_shapes).toMatchObject([ { value_status: "literal", fields: [{ path: "", state: "literal", value: 1 }] }, ]); expect(projection?.properties.return_shape_coverage).toMatchObject({ status: "complete", projection_complete: true, }); }); ``` Run the source-only focused test: ```sh npm run test:focused -- src/domain/javascript/container-getter-receiver-repro.test.ts ``` The equivalent analyzer, module-retention and return-shape projection calls were executed in a bounded focused verifier test. The standalone test above is a compact runnable version of those calls; its exact filename and command were not used in the verifier run. Its assertions record the current wrong output, so a passing assertion reproduces the anomaly. The supported public route accepts this function in `app.js` inside an ordinary readable directory through `analyze-javascript-application` with directory format. Public adapter reachability was traced in source only. CLI, MCP, the isolated source worker and the released package were not run in this verification. ## Expected behavior The return-value lattice must preserve uncertainty when a member write can change the object read by a direct return. The application workflow documentation describes conservative uncertainty for mutable references, shared children and accessors. Here the own getter returns the exact array element used as its receiver. The write changes that object's x property to 2 before the direct return. Reporting unknown is consistent with the existing conservative analyzer contract; an accurately justified value 2 would also satisfy the value contract. Literal 1 does not. ## Actual behavior The executed analyzer reports `{ "status": "literal", "value": 1 }`. Its exported projection contains the following fields: ```json { "static_return_shapes": [ { "value_status": "literal", "fields": [ { "path": "", "presence": "present", "state": "literal", "value": 1, "reason": null } ], "property_coverage": [] } ], "return_shape_coverage": { "status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true } } ``` The same stale output was observed for an object containing the array, a receiver literal assigned with `let box; box = ...`, and a saved alias of `arr[0].self`. A direct `arr[0].x = 2` write, a directly declared receiver getter write and an object-property receiver getter write each returned unknown. Reading the array element without a write retained literal 1. These observations distinguish the receiver-reference failure from unsupported array indexing or blanket loss of literal precision. No application fixture was executed to obtain a runtime return value; the value 2 follows from the explicit getter and assignment semantics. ## Evidence - Expected source: `docs/javascript-application-workflows.md:185-202` specifies direct return evaluation and conservative reference uncertainty; `src/domain/javascript/javascriptSemanticMemberMutations.ts:45` defines preservation of property-write uncertainty. `src/domain/javascript/javascriptSemanticAnalysis.getter_receivers.test.ts:12-99` corroborates getter receiver tracking for directly declared and nested object-property owners. - Failure source: The bounded verifier source tests observed the analyzer and production export projection results shown above. Relevant locators are `src/domain/javascript/javascriptSemanticObjectReferences.ts:266-343`, `src/domain/javascript/javascriptSemanticMemberMutations.ts:663-665`, and `src/domain/javascript/javascriptExportReturnShapeProjection.ts:24-84`. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The executed run contained ten observation tests, including the exact supplied top-level binding form, all four exported variants, controls and an editable-source workaround. It passed all assertions in 35ms with exit 0. The assertions establish the wrong output rather than correctness. Production `projectJavaScriptModuleSemantics` retained each exact export/callable link and the return value; `projectJavaScriptExportReturnShapes` then emitted the stale literal and complete projection fields. Public reachability was checked in source only. The supported directory-analysis entry point is `src/cli/electronCommands.ts:188`, and the application export-return-shape consumer is `src/application/javascript/JavaScriptModuleRelationships.ts:144-226`. The fixture is valid JavaScript in a readable directory with no source exclusion. The public route requires normal successful worker/resource admission; worker startup, transfer and adapter execution were not tested. Related: [#1494](https://github.com/morluto/rea/issues/1494) and [#1665](https://github.com/morluto/rea/issues/1665) cover earlier getter failures. Their closure comments verify their earlier specific cases, while the independently tested container and assignment forms above still fail at this checkout. Related: #2567 concerns property-write operation classification. This report concerns stale return-value inference after a direct assignment recognized by the mutation pass. ## Restoration check Analyze the array receiver fixture and the three observed variants. The return shape must no longer publish stale literal 1; preserve unknown unless the post-write value can be justified. Keep the no-write array read at literal 1. The existing direct receiver, nested object-property receiver and direct array-write controls must continue to suppress the old value. The source module/export projection must retain the corrected result so downstream callers receive the same conservative value. ## Runtime or environment Source checkout `a2d8b94dd50dab8d966239dd63ab84718aa8b4b4`, tested with Node 24.18.0 and npm 11.16.0. Registry metadata identifies rea-agents 6.4.0 as the latest published package; released runtime behavior and byte equivalence with this checkout were not tested. Current upstream source recheck at `3ae826ceba6f25b08bce50bcb51001209bc16b57` found the relevant receiver resolver, member-mutation traversal, value evaluation, and export projection unchanged; no new runtime test was run, and the observed failure remains supported by the earlier execution and current source trace. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Valid local source can produce a wrong literal in the exported return-shape inference. The observed failure is bounded to receiver-reference forms; no severe downstream incident or broad core-workflow failure was demonstrated. - Workaround status: available - Workaround basis: For editable source, the tested form `const box = { x: 1, get self() { return this; } }; const arr = [box]; arr[0].self.x = 2; return arr[0].x;` returns unknown. This preserves the fixture's object identity and return behavior. Callers who must analyze unchanged original artifacts cannot use this restructuring without changing their evidence input; an equivalent workaround for that constraint was not verified.
bsakaguchi 2026-10-15 18:29:52 +00:00
  • closed this issue
  • added the
    bug
    label
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#955
No description provided.