[GH-ISSUE #6669] [Bug] Getter side effects leave stale exported return literals #4049

Closed
opened 2026-10-31 00:46:21 +00:00 by bsakaguchi · 0 comments
Owner

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

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

The production JavaScript artifact analyzer and export-return projection publish state: "literal", value: 1 after an ordinary property read invokes a getter that writes this.x = 2. Native execution of the same benign function returns 2. The equivalent explicit method call correctly yields an unknown static return. This is an executed source-boundary finding, not a released CLI/provider reproduction.

Reproduction

From the source checkout with the pinned Node runtime and locked dependencies available, run the following shell block. Its body was executed once in the final three-case run and exited zero. Earlier bounded production-boundary probes established the same failure and the variants described below; no fixture was called a minimized reproduction.

node --import ./node_modules/tsx/dist/loader.mjs --input-type=module <<'JS'
import { createHash } from 'node:crypto';
import { analyzeJavaScriptArtifactFiles } from './src/application/javascript/JavaScriptArtifactAnalysis.ts';
import { projectJavaScriptModuleSemantics } from './src/domain/javascript/javascriptModuleSemanticIr.ts';
import { projectJavaScriptExportReturnShapes } from './src/domain/javascript/javascriptExportReturnShapeProjection.ts';
import { completeApplicationCoverage } from './src/domain/javascript/javascriptApplicationEvidenceSchemas.ts';
const cases = {
  getter: `export function read(){const state={x:1,get trigger(){this.x=2;return 0;}};state.trigger;return state.x;}`,
  no_read: `export function read(){const state={x:1,get trigger(){this.x=2;return 0;}};return state.x;}`,
  explicit_method: `export function read(){const state={x:1,trigger(){this.x=2;return 0;}};state.trigger();return state.x;}`,
};
for (const [name, source] of Object.entries(cases)) {
  const sha = createHash('sha256').update(source).digest('hex');
  const analysis = analyzeJavaScriptArtifactFiles({
    files: [{path:'app.mjs', container_sha256:sha, sha256:sha,
      bytes:Buffer.byteLength(source), inventory_artifact_id:`art_${sha}`,
      kind:'javascript', unpacked:false, text:{included:true, value:source}}],
    containers:[], unavailable_relevant_files:[],
    text_bytes_read:Buffer.byteLength(source), invalid_utf8_files:0,
  });
  const ir = analysis.files[0]?.semantic?.ir;
  if (!ir) throw new Error('Missing semantic IR');
  const module = projectJavaScriptModuleSemantics(ir);
  const link = module.moduleLinks.find(l => l.exportedName === 'read');
  if (!link) throw new Error('Missing read export');
  const projection = projectJavaScriptExportReturnShapes({
    ir:module, link, modulePath:'app.mjs', baseCoverage:completeApplicationCoverage(),
  });
  if (!projection) throw new Error('Missing return projection');
  const shape = projection.properties.static_return_shapes[0];
  const native = Function(source.replace('export ', '') + '; return read();')();
  console.log(JSON.stringify({case:name, parse_failures:analysis.parse_failures,
    semantic_coverage:ir.coverage.status, field:shape.fields[0], native}));
}
JS

The Function call is an independent native-language reference control. REA analyzes the source string without executing it.

Expected behavior

Return unknown, or an accurately justified post-write value, instead of the obsolete literal 1. The documented value contract preserves conservative uncertainty for accessors and mutable method receivers, and the mutation contract avoids assuming exposed objects remain unchanged. No interpretation of the getter body to recover the exact value 2 is necessary; an unmodeled getter invocation must not justify certainty about its receiver's prior field value.

Impact and scope

The bounded source pattern produces a wrong fact in a retained exported return shape. Direct getter reads, object spread and object destructuring were observed to retain the stale literal. The no-read control retains the correct 1, while an explicit method call and an own-setter assignment produce conservative unknowns. Real-application prevalence, downstream incidents and other platforms were not measured.

REA version

Source checkout 5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26, whose package version is 6.7.0. The parent investigation identified rea-agents 6.7.0 as the latest published package; this newer source checkout was tested, not the published package.

Runtime and platform

Node.js v24.22.1; Linux x86_64; repository lockfile dependencies. No external analysis provider was started or mocked.

Target and artifact context

A self-authored JavaScript module supplied as decoded app.mjs text to the production artifact-analysis boundary. A separate native-JavaScript reference control executes only the small self-contained fixture.

Interface

Other

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

Selected final-run output, with the unchanged field keys retained:

{"case":"getter","parse_failures":0,"semantic_coverage":"complete","field":{"path":"","presence":"present","state":"literal","value":1,"reason":null},"native":2}
{"case":"no_read","parse_failures":0,"semantic_coverage":"complete","field":{"path":"","presence":"present","state":"literal","value":1,"reason":null},"native":1}
{"case":"explicit_method","parse_failures":0,"semantic_coverage":"complete","field":{"path":"","presence":"present","state":"unknown","value":null,"reason":"Own property x is unknown-coverage."},"native":2}

Replacing state.trigger; with either const copy = {...state}; or const {trigger} = state; also produced static literal 1 and native 2 in the earlier executed probe. These are invocation variants of the same primary behavior, not separate reports. Complete coverage here describes retained syntax, not proof of runtime execution.

  • Expected source: The documented conservative accessor and receiver contracts linked above, corroborated by the executed explicit-method control.
  • Failure source: Executed analyzeJavaScriptArtifactFiles, module retention and projectJavaScriptExportReturnShapes calls shown inline; the projected field is the actual observed contrary result.
  • Evidence provenance: observed.
  • Local verification: reproduced.
  • Reproduction completeness: complete at the production analysis/projection boundary.

Public-path reachability was checked in source: the CLI registration accepts an ordinary readable directory; the shared service calls reconstruction; its source worker uses the same parser/semantic pass; and module relationships consume the same export-return projection. The inline file has a supported extension, valid UTF-8/module syntax and an exact exported-callable link, with no imports or source selection exclusions. This establishes applicability under normal successful worker/resource admission. CLI/MCP transport, the isolated worker and the released package were not executed.

Typed outcome or recovery expected

Using the same fixtures, the getter, spread and destructuring cases should stop publishing literal 1; unknown is sufficient. Preserve the no-read case's known 1 and the explicit-method case's uncertainty. This is a proposed restoration check; no fix was implemented.

Analysis or suspected cause

Source-derived: receiver exposure is recorded for explicit calls in the effect traversal, which has no corresponding ordinary getter-read branch. The getter's this write is not translated into exposure of the local receiver in this case. Return projection then preserves the stale value. This identifies a likely boundary without prescribing an implementation.

Additional context

The full 1,043-entry open-issue snapshot was reread before drafting. Fresh open/closed searches covered getter titles, getter side effects, this.x with getter syntax, accessors and related returned-reference reports. #1494, #1665, #2653, #2654 and #2655 concern writes through a getter's returned reference. Here the getter returns a primitive and mutates its receiver internally. #4630 concerns an array-element setter invoked by assignment. The documentation's statement that a getter read without a write preserves fields appears in the returned-reference discussion; this fixture contains an explicit receiver-field write inside the getter. NOT_ENOUGH_INFO: introducing revision, last known good release or released-package behavior.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Valid bounded source yields an obsolete literal in exported return evidence; production parsing and projection were observed, with no severe downstream incident demonstrated.
  • Workaround status: unverified
  • Workaround basis: Rewriting the getter invocation as an explicit method call produces uncertainty in the control, but changes the analyzed artifact. No practical route preserving an unchanged original artifact was verified.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6669 ## 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 The production JavaScript artifact analyzer and export-return projection publish `state: "literal", value: 1` after an ordinary property read invokes a getter that writes `this.x = 2`. Native execution of the same benign function returns `2`. The equivalent explicit method call correctly yields an unknown static return. This is an executed source-boundary finding, not a released CLI/provider reproduction. ## Reproduction From the source checkout with the pinned Node runtime and locked dependencies available, run the following shell block. Its body was executed once in the final three-case run and exited zero. Earlier bounded production-boundary probes established the same failure and the variants described below; no fixture was called a minimized reproduction. ```sh node --import ./node_modules/tsx/dist/loader.mjs --input-type=module <<'JS' import { createHash } from 'node:crypto'; import { analyzeJavaScriptArtifactFiles } from './src/application/javascript/JavaScriptArtifactAnalysis.ts'; import { projectJavaScriptModuleSemantics } from './src/domain/javascript/javascriptModuleSemanticIr.ts'; import { projectJavaScriptExportReturnShapes } from './src/domain/javascript/javascriptExportReturnShapeProjection.ts'; import { completeApplicationCoverage } from './src/domain/javascript/javascriptApplicationEvidenceSchemas.ts'; const cases = { getter: `export function read(){const state={x:1,get trigger(){this.x=2;return 0;}};state.trigger;return state.x;}`, no_read: `export function read(){const state={x:1,get trigger(){this.x=2;return 0;}};return state.x;}`, explicit_method: `export function read(){const state={x:1,trigger(){this.x=2;return 0;}};state.trigger();return state.x;}`, }; for (const [name, source] of Object.entries(cases)) { const sha = createHash('sha256').update(source).digest('hex'); const analysis = analyzeJavaScriptArtifactFiles({ files: [{path:'app.mjs', container_sha256:sha, sha256:sha, bytes:Buffer.byteLength(source), inventory_artifact_id:`art_${sha}`, kind:'javascript', unpacked:false, text:{included:true, value:source}}], containers:[], unavailable_relevant_files:[], text_bytes_read:Buffer.byteLength(source), invalid_utf8_files:0, }); const ir = analysis.files[0]?.semantic?.ir; if (!ir) throw new Error('Missing semantic IR'); const module = projectJavaScriptModuleSemantics(ir); const link = module.moduleLinks.find(l => l.exportedName === 'read'); if (!link) throw new Error('Missing read export'); const projection = projectJavaScriptExportReturnShapes({ ir:module, link, modulePath:'app.mjs', baseCoverage:completeApplicationCoverage(), }); if (!projection) throw new Error('Missing return projection'); const shape = projection.properties.static_return_shapes[0]; const native = Function(source.replace('export ', '') + '; return read();')(); console.log(JSON.stringify({case:name, parse_failures:analysis.parse_failures, semantic_coverage:ir.coverage.status, field:shape.fields[0], native})); } JS ``` The `Function` call is an independent native-language reference control. REA analyzes the source string without executing it. ## Expected behavior Return unknown, or an accurately justified post-write value, instead of the obsolete literal `1`. [The documented value contract](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/docs/javascript-application-workflows.md#L368-L391) preserves conservative uncertainty for accessors and mutable method receivers, and the [mutation contract](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/docs/javascript-application-workflows.md#L214-L221) avoids assuming exposed objects remain unchanged. No interpretation of the getter body to recover the exact value `2` is necessary; an unmodeled getter invocation must not justify certainty about its receiver's prior field value. ## Impact and scope The bounded source pattern produces a wrong fact in a retained exported return shape. Direct getter reads, object spread and object destructuring were observed to retain the stale literal. The no-read control retains the correct `1`, while an explicit method call and an own-setter assignment produce conservative unknowns. Real-application prevalence, downstream incidents and other platforms were not measured. ## REA version Source checkout `5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26`, whose package version is 6.7.0. The parent investigation identified rea-agents 6.7.0 as the latest published package; this newer source checkout was tested, not the published package. ## Runtime and platform Node.js v24.22.1; Linux x86_64; repository lockfile dependencies. No external analysis provider was started or mocked. ## Target and artifact context A self-authored JavaScript module supplied as decoded `app.mjs` text to the production artifact-analysis boundary. A separate native-JavaScript reference control executes only the small self-contained fixture. ## Interface Other ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence Selected final-run output, with the unchanged field keys retained: ```json {"case":"getter","parse_failures":0,"semantic_coverage":"complete","field":{"path":"","presence":"present","state":"literal","value":1,"reason":null},"native":2} {"case":"no_read","parse_failures":0,"semantic_coverage":"complete","field":{"path":"","presence":"present","state":"literal","value":1,"reason":null},"native":1} {"case":"explicit_method","parse_failures":0,"semantic_coverage":"complete","field":{"path":"","presence":"present","state":"unknown","value":null,"reason":"Own property x is unknown-coverage."},"native":2} ``` Replacing `state.trigger;` with either `const copy = {...state};` or `const {trigger} = state;` also produced static literal `1` and native `2` in the earlier executed probe. These are invocation variants of the same primary behavior, not separate reports. Complete coverage here describes retained syntax, not proof of runtime execution. - Expected source: The documented conservative accessor and receiver contracts linked above, corroborated by the executed explicit-method control. - Failure source: Executed `analyzeJavaScriptArtifactFiles`, module retention and `projectJavaScriptExportReturnShapes` calls shown inline; the projected field is the actual observed contrary result. - Evidence provenance: observed. - Local verification: reproduced. - Reproduction completeness: complete at the production analysis/projection boundary. Public-path reachability was checked in source: the [CLI registration](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/cli/electronCommands.ts#L302-L338) accepts an ordinary readable directory; the [shared service](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/application/javascript/JavaScriptApplicationService.ts#L41-L86) calls reconstruction; its [source worker](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/composition/javascriptAnalysisWorkerProcess.ts#L25-L54) uses the same parser/semantic pass; and [module relationships](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/application/javascript/JavaScriptModuleRelationships.ts#L156-L193) consume the same export-return projection. The inline file has a supported extension, valid UTF-8/module syntax and an exact exported-callable link, with no imports or source selection exclusions. This establishes applicability under normal successful worker/resource admission. CLI/MCP transport, the isolated worker and the released package were not executed. ## Typed outcome or recovery expected Using the same fixtures, the getter, spread and destructuring cases should stop publishing literal `1`; unknown is sufficient. Preserve the no-read case's known `1` and the explicit-method case's uncertainty. This is a proposed restoration check; no fix was implemented. ## Analysis or suspected cause Source-derived: [receiver exposure](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/domain/javascript/javascriptSemanticMemberMutations.ts#L841-L847) is recorded for explicit calls in [the effect traversal](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/domain/javascript/javascriptSemanticMemberMutations.ts#L879-L923), which has no corresponding ordinary getter-read branch. The getter's `this` write is not translated into exposure of the local receiver in this case. [Return projection](https://github.com/morluto/rea/blob/5a5bcb6b549cfdfba6dd3f02dd681e505ffd2a26/src/domain/javascript/javascriptExportReturnShapeProjection.ts#L90-L103) then preserves the stale value. This identifies a likely boundary without prescribing an implementation. ## Additional context The full 1,043-entry open-issue snapshot was reread before drafting. Fresh open/closed searches covered getter titles, getter side effects, `this.x` with getter syntax, accessors and related returned-reference reports. [#1494](https://github.com/morluto/rea/issues/1494), [#1665](https://github.com/morluto/rea/issues/1665), [#2653](https://github.com/morluto/rea/issues/2653), [#2654](https://github.com/morluto/rea/issues/2654) and [#2655](https://github.com/morluto/rea/issues/2655) concern writes through a getter's returned reference. Here the getter returns a primitive and mutates its receiver internally. [#4630](https://github.com/morluto/rea/issues/4630) concerns an array-element setter invoked by assignment. The documentation's statement that a getter read without a write preserves fields appears in the returned-reference discussion; this fixture contains an explicit receiver-field write inside the getter. NOT_ENOUGH_INFO: introducing revision, last known good release or released-package behavior. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Valid bounded source yields an obsolete literal in exported return evidence; production parsing and projection were observed, with no severe downstream incident demonstrated. - Workaround status: unverified - Workaround basis: Rewriting the getter invocation as an explicit method call produces uncertainty in the control, but changes the analyzed artifact. No practical route preserving an unchanged original artifact was verified.
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#4049
No description provided.