[GH-ISSUE #6783] [Bug] Inherited setter assignments leave stale exported return literals #4137

Closed
opened 2026-10-31 00:47:04 +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/6783

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 public analyze-javascript-application CLI publishes literal 1 after an ordinary assignment invokes an inherited setter that changes the returned field to 2. An own setter performing the same write correctly yields unknown. An inherited ordinary data property and an own data property shadowing the inherited setter both correctly preserve literal 1. The failing application graph reports complete coverage.

Reproduction

From the prepared source checkout identified below, with locked dependencies and its compiled runtime, run this self-contained Python 3 block. The final four-case probe was executed once; this copy uses a generic temporary base directory. It creates disposable fixtures and isolated configuration beneath /tmp. REA statically analyzes the source; a separate Node process executes only the self-authored inert modules.

python3 - <<'PYPROBE'
import json, os, pathlib, subprocess, tempfile
repo = pathlib.Path.cwd()
root = pathlib.Path(tempfile.mkdtemp(prefix='js-prototype-', dir='/tmp'))
for name in ('home', 'config', 'cache', 'temp'):
    (root / name).mkdir()
env = {'PATH': os.environ['PATH'], 'HOME': str(root/'home'),
       'XDG_CONFIG_HOME': str(root/'config'), 'XDG_CACHE_HOME': str(root/'cache'),
       'TMPDIR': str(root/'temp')}
cases = {
    'inherited-setter': 'export function read() { const state = {__proto__: {set update(v) {this.value = v;}}, value: 1}; state.update = 2; return state.value; }',
    'own-setter': 'export function read() { const state = {set update(v) {this.value = v;}, value: 1}; state.update = 2; return state.value; }',
    'ordinary-property': 'export function read() { const state = {__proto__: {update: 0}, value: 1}; state.update = 2; return state.value; }',
    'own-property-shadow': 'export function read() { const state = {__proto__: {set update(v) {this.value = v;}}, update: 0, value: 1}; state.update = 2; return state.value; }',
}

for case, source in cases.items():
    app = root/case
    app.mkdir()
    fixture = app/'main.mjs'
    fixture.write_text(source+'\n')
    run = subprocess.run(['node', str(repo/'scripts/rea.mjs'),
        'analyze-javascript-application', str(app), '--artifact-format', 'directory', '--json'],
        cwd=repo, env=env, capture_output=True, text=True, check=True, timeout=60)
    (root/(case+'.json')).write_text(run.stdout)
    evidence = json.loads(run.stdout)['normalized_result']
    projections = [o['properties'] for n in evidence['graph']['nodes'] for o in n.get('observations', [])
        if o['properties'].get('semantic_role') == 'export-return-shapes'
        and o['properties'].get('exported_name') == 'read']
    runtime = subprocess.run(['node', '--input-type=module', '-e',
        'import {pathToFileURL} from "node:url"; const m=await import(pathToFileURL(process.argv[1])); console.log(JSON.stringify(m.read()));',
        str(fixture)], env=env, capture_output=True, text=True, check=True, timeout=10)
    print(json.dumps({'case': case, 'cli_exit': run.returncode,
        'fields': projections[0]['static_return_shapes'][0]['fields'],
        'return_shape_coverage': projections[0]['return_shape_coverage'],
        'runtime': json.loads(runtime.stdout)}), flush=True)
print('evidence_dir='+str(root))
PYPROBE

The inherited-setter fixture uses one local object with an object-literal prototype; it neither modifies global prototypes nor touches external state.

Expected behavior

An assignment that can invoke an inherited setter must not leave a contradicted sibling value exact. Conservative unknown is sufficient. Preserve the own-setter unknown, ordinary-property literal, and own-property-shadow literal controls.

The existing setter contract explains that a setter may replace any member of its receiver and preserves unrelated literals for ordinary property writes. The workflow contract requires conservative accessor uncertainty. Existing prototype-getter coverage also distinguishes inherited accessors from own-property shadowing. Independent Node execution establishes that this valid inherited setter does run and changes the returned value.

Impact and scope

A bounded valid module produces a wrong exact public return fact in static application Evidence. The compact inherited-setter case occurred once in the final four-case run; earlier separate-prototype and inline-prototype wrapper-return probes also showed the stale literal before drafting. No severe downstream incident was demonstrated. Other inheritance forms, real-application prevalence, other platforms, MCP transport, and the published npm runtime were not tested. No workaround preserving the unchanged analyzed artifact was verified.

REA version

Source build at 98ca6c82705bf8c44f814620636852f938d827ca; package manifest 6.7.0. The independently installed npm package was not executed for this report.

Runtime and platform

Node.js v26.12.1; Linux x64. Locked checkout dependencies and the shared prepared runtime. No native provider is involved.

Target and artifact context

Four self-authored directory artifacts, each containing one complete main.mjs module above. All setters change only a numeric field on their local receiver. No global prototype, device, user configuration, network, or external target is changed.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Expected source: Existing setter and inherited-accessor regression contracts linked above, plus independent Node execution of the same fixtures.
  • Failure source: Executed public CLI application graph, selecting semantic_role: "export-return-shapes" and exported_name: "read"; the Node oracle returns 2 where the retained field says literal 1.
  • Evidence provenance: observed.
  • Local verification: reproduced through the public CLI; no helper-only substitution.
  • Reproduction completeness: complete.

Observed final probe output, excluding the generated temporary-directory location:

{"case": "inherited-setter", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "literal", "value": 1, "reason": null}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 2}
{"case": "own-setter", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "unknown", "value": null, "reason": "Own property value is unknown-coverage."}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 2}
{"case": "ordinary-property", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "literal", "value": 1, "reason": null}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 1}
{"case": "own-property-shadow", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "literal", "value": 1, "reason": null}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 1}

The failing application's graph coverage was {"status":"complete","truncated":false,"omitted_count":0,"limits":[]}. All four analysis and runtime commands exited zero.

Analysis or suspected cause

Source-derived reasoning: localSetterReceiver checks the receiver literal's own setter keys, without following its object-literal prototype. The write collection boundary therefore does not widen this write to the receiver's other fields. Value evaluation marks prototype-derived properties unknown but retains later explicit data fields; that alone does not invalidate value when the inherited setter executes. These are source observations, not a tested repair.

Additional context

No inherited-setter or prototype-setter report was found in the supplied open-issue bodies. Related #4630 uses an own setter on an array-element receiver; this fixture has no array, indexed receiver, getter, returned-reference alias, coercion, or disposal. Traversing arrays in own-setter detection would not by itself handle inherited setter dispatch here. #6669 concerns a getter invoked by a read, while this fixture only invokes a setter through assignment. NOT_ENOUGH_INFO: introducing revision, last known good release, and published-package behavior.

Typed outcome or recovery expected

A future repair should stop emitting stale literal 1 for the inherited-setter case, preserve unknown for the own setter, and preserve literal 1 for ordinary writes and own-property shadowing. These are proposed restoration checks. No repair or post-draft re-verification was executed.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: An ordinary inherited setter produces a wrong exact exported return value through the public CLI, with direct runtime evidence and controls; no severe downstream harm was established.
  • Workaround status: unverified
  • Workaround basis: Making the setter an own property changes the artifact, and no REA-only workflow preserving the original source was verified to recover the correct uncertainty.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6783 ## 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 public `analyze-javascript-application` CLI publishes literal `1` after an ordinary assignment invokes an inherited setter that changes the returned field to `2`. An own setter performing the same write correctly yields unknown. An inherited ordinary data property and an own data property shadowing the inherited setter both correctly preserve literal `1`. The failing application graph reports complete coverage. ## Reproduction From the prepared source checkout identified below, with locked dependencies and its compiled runtime, run this self-contained Python 3 block. The final four-case probe was executed once; this copy uses a generic temporary base directory. It creates disposable fixtures and isolated configuration beneath `/tmp`. REA statically analyzes the source; a separate Node process executes only the self-authored inert modules. ```sh python3 - <<'PYPROBE' import json, os, pathlib, subprocess, tempfile repo = pathlib.Path.cwd() root = pathlib.Path(tempfile.mkdtemp(prefix='js-prototype-', dir='/tmp')) for name in ('home', 'config', 'cache', 'temp'): (root / name).mkdir() env = {'PATH': os.environ['PATH'], 'HOME': str(root/'home'), 'XDG_CONFIG_HOME': str(root/'config'), 'XDG_CACHE_HOME': str(root/'cache'), 'TMPDIR': str(root/'temp')} cases = { 'inherited-setter': 'export function read() { const state = {__proto__: {set update(v) {this.value = v;}}, value: 1}; state.update = 2; return state.value; }', 'own-setter': 'export function read() { const state = {set update(v) {this.value = v;}, value: 1}; state.update = 2; return state.value; }', 'ordinary-property': 'export function read() { const state = {__proto__: {update: 0}, value: 1}; state.update = 2; return state.value; }', 'own-property-shadow': 'export function read() { const state = {__proto__: {set update(v) {this.value = v;}}, update: 0, value: 1}; state.update = 2; return state.value; }', } for case, source in cases.items(): app = root/case app.mkdir() fixture = app/'main.mjs' fixture.write_text(source+'\n') run = subprocess.run(['node', str(repo/'scripts/rea.mjs'), 'analyze-javascript-application', str(app), '--artifact-format', 'directory', '--json'], cwd=repo, env=env, capture_output=True, text=True, check=True, timeout=60) (root/(case+'.json')).write_text(run.stdout) evidence = json.loads(run.stdout)['normalized_result'] projections = [o['properties'] for n in evidence['graph']['nodes'] for o in n.get('observations', []) if o['properties'].get('semantic_role') == 'export-return-shapes' and o['properties'].get('exported_name') == 'read'] runtime = subprocess.run(['node', '--input-type=module', '-e', 'import {pathToFileURL} from "node:url"; const m=await import(pathToFileURL(process.argv[1])); console.log(JSON.stringify(m.read()));', str(fixture)], env=env, capture_output=True, text=True, check=True, timeout=10) print(json.dumps({'case': case, 'cli_exit': run.returncode, 'fields': projections[0]['static_return_shapes'][0]['fields'], 'return_shape_coverage': projections[0]['return_shape_coverage'], 'runtime': json.loads(runtime.stdout)}), flush=True) print('evidence_dir='+str(root)) PYPROBE ``` The inherited-setter fixture uses one local object with an object-literal prototype; it neither modifies global prototypes nor touches external state. ## Expected behavior An assignment that can invoke an inherited setter must not leave a contradicted sibling value exact. Conservative unknown is sufficient. Preserve the own-setter unknown, ordinary-property literal, and own-property-shadow literal controls. The [existing setter contract](https://github.com/morluto/rea/blob/98ca6c82705bf8c44f814620636852f938d827ca/src/domain/javascript/javascriptSemanticAnalysis.accessor_writes.test.ts#L11-L41) explains that a setter may replace any member of its receiver and preserves unrelated literals for ordinary property writes. The [workflow contract](https://github.com/morluto/rea/blob/98ca6c82705bf8c44f814620636852f938d827ca/docs/javascript-application-workflows.md#L378-L391) requires conservative accessor uncertainty. Existing [prototype-getter coverage](https://github.com/morluto/rea/blob/98ca6c82705bf8c44f814620636852f938d827ca/src/domain/javascript/javascriptSemanticAnalysis.member_mutations.test.ts#L228-L256) also distinguishes inherited accessors from own-property shadowing. Independent Node execution establishes that this valid inherited setter does run and changes the returned value. ## Impact and scope A bounded valid module produces a wrong exact public return fact in static application Evidence. The compact inherited-setter case occurred once in the final four-case run; earlier separate-prototype and inline-prototype wrapper-return probes also showed the stale literal before drafting. No severe downstream incident was demonstrated. Other inheritance forms, real-application prevalence, other platforms, MCP transport, and the published npm runtime were not tested. No workaround preserving the unchanged analyzed artifact was verified. ## REA version Source build at `98ca6c82705bf8c44f814620636852f938d827ca`; package manifest 6.7.0. The independently installed npm package was not executed for this report. ## Runtime and platform Node.js v26.12.1; Linux x64. Locked checkout dependencies and the shared prepared runtime. No native provider is involved. ## Target and artifact context Four self-authored directory artifacts, each containing one complete `main.mjs` module above. All setters change only a numeric field on their local receiver. No global prototype, device, user configuration, network, or external target is changed. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence - Expected source: Existing setter and inherited-accessor regression contracts linked above, plus independent Node execution of the same fixtures. - Failure source: Executed public CLI application graph, selecting `semantic_role: "export-return-shapes"` and `exported_name: "read"`; the Node oracle returns `2` where the retained field says literal `1`. - Evidence provenance: observed. - Local verification: reproduced through the public CLI; no helper-only substitution. - Reproduction completeness: complete. Observed final probe output, excluding the generated temporary-directory location: ```jsonl {"case": "inherited-setter", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "literal", "value": 1, "reason": null}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 2} {"case": "own-setter", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "unknown", "value": null, "reason": "Own property value is unknown-coverage."}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 2} {"case": "ordinary-property", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "literal", "value": 1, "reason": null}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 1} {"case": "own-property-shadow", "cli_exit": 0, "fields": [{"path": "", "presence": "present", "state": "literal", "value": 1, "reason": null}], "return_shape_coverage": {"status": "complete", "retained_return_sites": 1, "omitted_return_sites": 0, "omitted_fields": 0, "omitted_property_coverage": 0, "projection_complete": true}, "runtime": 1} ``` The failing application's graph coverage was `{"status":"complete","truncated":false,"omitted_count":0,"limits":[]}`. All four analysis and runtime commands exited zero. ## Analysis or suspected cause Source-derived reasoning: [localSetterReceiver](https://github.com/morluto/rea/blob/98ca6c82705bf8c44f814620636852f938d827ca/src/domain/javascript/javascriptSemanticMemberMutations.ts#L125-L160) checks the receiver literal's own setter keys, without following its object-literal prototype. The [write collection boundary](https://github.com/morluto/rea/blob/98ca6c82705bf8c44f814620636852f938d827ca/src/domain/javascript/javascriptSemanticMemberMutations.ts#L516-L524) therefore does not widen this write to the receiver's other fields. [Value evaluation](https://github.com/morluto/rea/blob/98ca6c82705bf8c44f814620636852f938d827ca/src/domain/javascript/javascriptSemanticValues.ts#L785-L795) marks prototype-derived properties unknown but retains later explicit data fields; that alone does not invalidate `value` when the inherited setter executes. These are source observations, not a tested repair. ## Additional context No inherited-setter or prototype-setter report was found in the supplied open-issue bodies. Related [#4630](https://github.com/morluto/rea/issues/4630) uses an own setter on an array-element receiver; this fixture has no array, indexed receiver, getter, returned-reference alias, coercion, or disposal. Traversing arrays in own-setter detection would not by itself handle inherited setter dispatch here. [#6669](https://github.com/morluto/rea/issues/6669) concerns a getter invoked by a read, while this fixture only invokes a setter through assignment. NOT_ENOUGH_INFO: introducing revision, last known good release, and published-package behavior. ## Typed outcome or recovery expected A future repair should stop emitting stale literal `1` for the inherited-setter case, preserve unknown for the own setter, and preserve literal `1` for ordinary writes and own-property shadowing. These are proposed restoration checks. No repair or post-draft re-verification was executed. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: An ordinary inherited setter produces a wrong exact exported return value through the public CLI, with direct runtime evidence and controls; no severe downstream harm was established. - Workaround status: unverified - Workaround basis: Making the setter an own property changes the artifact, and no REA-only workflow preserving the original source was verified to recover the correct uncertainty.
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#4137
No description provided.