[GH-ISSUE #2562] fix(javascript): skipped initialization produces a definite binding and return value #878

Closed
opened 2026-10-15 18:29:14 +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/2562

Summary

When a JavaScript variable can remain uninitialized because a branch or loop does not run, static semantic analysis still reports its assigned number as a definite literal. The incorrect value also appears in a function returning that variable. This affects conditionally initialized hoisted variables and a declared variable with a conditional assignment; callers lose the distinction between a possible value and a certain one.

Steps to reproduce

Use the source checkout and installed dependencies described under Runtime or environment. Save the following test as src/domain/javascript/conditional-initialization.test.ts, then run npm run test:focused -- src/domain/javascript/conditional-initialization.test.ts.

import { expect, it } from "vitest";
import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js";

it.each([
  "if (condition) { var result = 1; }",
  "while (condition) { var result = 1; break; }",
  "switch (condition) { case true: var result = 1; }",
  "try { if (!condition) throw 0; var result = 1; } catch (error) {}",
  "let result; if (condition) { result = 1; }",
])("retains uncertainty for %s", (declarations) => {
  const runtime = new Function("condition", `${declarations} return result;`);
  expect(runtime(false)).toBeUndefined();
  expect(runtime(true)).toBe(1);
  const analysis = analyzeJavaScriptSemantics(
    `${declarations} function read() { return result; } read();`,
  );
  const binding = analysis.bindings.find((entry) => entry.name === "result");
  const callable = analysis.callables.find((entry) => entry.name === "read");
  expect(binding).toBeDefined();
  expect(callable).toBeDefined();
  expect(binding?.value).not.toEqual({ status: "literal", value: 1 });
  expect(callable?.returnSites[0]?.value).not.toEqual({ status: "literal", value: 1 });
});

The runtime comparisons execute only the explicit, inert fixtures above; they are not needed to execute the analyzed target application. Each parameterized fixture independently reaches the binding assertion.

Expected behavior

For an unknown condition, the variable can contain either undefined or 1 when it is read after the conditional statement. Analysis must retain that uncertainty in both the binding value and the direct return value instead of claiming that 1 is certain. It may conservatively report an unresolved value; it does not need to simulate the condition to preserve this contract.

Actual behavior

All five independently tested fixtures produced {"status":"literal","value":1} for the variable and its direct return site. Their false-condition runtime checks returned undefined, and their true-condition checks returned 1. The five binding assertions failed, while four separate controls passed: an unconditional const, an unconditional assignment, multiple possible assignments, and a plain try block whose literal assignment cannot normally be skipped. The focused runner exited with status 1.

Evidence

  • Expected source: AGENTS.md:19 requires preserving uncertainty; src/domain/javascript/javascriptSemanticAnalysis.ts:723 explicitly records that a hoisted variable can remain undefined if its initializer is skipped, and src/domain/javascript/javascriptSemanticState.ts:93 rejects those initializers for stable-value consumers.
  • Failure source: Independent focused observations from analyzeJavaScriptSemantics; src/domain/javascript/javascriptSemanticValues.ts:277 evaluates a sole initializer without rejecting its conditional execution, and src/domain/javascript/javascriptSemanticProjection.ts:218 publishes that value in the semantic result.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The declaration index records conditional var initializers, but the conditional plain-assignment path does not record the same fact. The reproduced cases therefore share an incorrect certainty result without establishing that one consumer-only change fixes all forms. Direct return projections were inspected separately; CLI and MCP end-to-end output was not tested.

Related reports #1428 and #1449 concern false literals after object-reference mutations. They are closed other-author reports with merged fixes for those different inputs; the skipped-initialization cases still fail on the tested baseline.

Runtime or environment

Failure was verified at a2d8b94dd5 and is still present at 8b181716ac by source recheck. Node v26.10.0 and npm 11.19.1, with dependencies already installed from the repository lockfile. The focused verifier used a 1536 MiB JavaScript heap limit and completed in 1.08 seconds.

Restoration check

Reanalyze the five fixtures and verify that both the binding and direct return values retain the possibility of skipped initialization rather than publishing a definite 1. Keep const result = 1; and let result; result = 1; as definite literal controls. Keep let result = 0; if (condition) result = 1; uncertain, and do not require try { var result = 1; } catch (error) {} to be uncertain merely to satisfy the original report. These controls distinguish restoring uncertainty from discarding every known value.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Five concrete conditional-initialization inputs produce incorrect definite binding and direct return facts. This is consequential but bounded semantic incorrectness; no severe downstream incident or complete workflow failure was observed.
  • Workaround status: unverified
  • Workaround basis: No safe, practical workaround preserving semantic results for arbitrary analyzed targets was established; manual source review or execution of real target code was not assessed as a substitute.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2562 ## Summary When a JavaScript variable can remain uninitialized because a branch or loop does not run, static semantic analysis still reports its assigned number as a definite literal. The incorrect value also appears in a function returning that variable. This affects conditionally initialized hoisted variables and a declared variable with a conditional assignment; callers lose the distinction between a possible value and a certain one. ## Steps to reproduce Use the source checkout and installed dependencies described under Runtime or environment. Save the following test as `src/domain/javascript/conditional-initialization.test.ts`, then run `npm run test:focused -- src/domain/javascript/conditional-initialization.test.ts`. ```ts import { expect, it } from "vitest"; import { analyzeJavaScriptSemantics } from "./javascriptSemanticAnalysis.js"; it.each([ "if (condition) { var result = 1; }", "while (condition) { var result = 1; break; }", "switch (condition) { case true: var result = 1; }", "try { if (!condition) throw 0; var result = 1; } catch (error) {}", "let result; if (condition) { result = 1; }", ])("retains uncertainty for %s", (declarations) => { const runtime = new Function("condition", `${declarations} return result;`); expect(runtime(false)).toBeUndefined(); expect(runtime(true)).toBe(1); const analysis = analyzeJavaScriptSemantics( `${declarations} function read() { return result; } read();`, ); const binding = analysis.bindings.find((entry) => entry.name === "result"); const callable = analysis.callables.find((entry) => entry.name === "read"); expect(binding).toBeDefined(); expect(callable).toBeDefined(); expect(binding?.value).not.toEqual({ status: "literal", value: 1 }); expect(callable?.returnSites[0]?.value).not.toEqual({ status: "literal", value: 1 }); }); ``` The runtime comparisons execute only the explicit, inert fixtures above; they are not needed to execute the analyzed target application. Each parameterized fixture independently reaches the binding assertion. ## Expected behavior For an unknown condition, the variable can contain either undefined or 1 when it is read after the conditional statement. Analysis must retain that uncertainty in both the binding value and the direct return value instead of claiming that 1 is certain. It may conservatively report an unresolved value; it does not need to simulate the condition to preserve this contract. ## Actual behavior All five independently tested fixtures produced `{"status":"literal","value":1}` for the variable and its direct return site. Their false-condition runtime checks returned undefined, and their true-condition checks returned 1. The five binding assertions failed, while four separate controls passed: an unconditional const, an unconditional assignment, multiple possible assignments, and a plain try block whose literal assignment cannot normally be skipped. The focused runner exited with status 1. ## Evidence - Expected source: `AGENTS.md:19` requires preserving uncertainty; `src/domain/javascript/javascriptSemanticAnalysis.ts:723` explicitly records that a hoisted variable can remain undefined if its initializer is skipped, and `src/domain/javascript/javascriptSemanticState.ts:93` rejects those initializers for stable-value consumers. - Failure source: Independent focused observations from `analyzeJavaScriptSemantics`; `src/domain/javascript/javascriptSemanticValues.ts:277` evaluates a sole initializer without rejecting its conditional execution, and `src/domain/javascript/javascriptSemanticProjection.ts:218` publishes that value in the semantic result. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The declaration index records conditional var initializers, but the conditional plain-assignment path does not record the same fact. The reproduced cases therefore share an incorrect certainty result without establishing that one consumer-only change fixes all forms. Direct return projections were inspected separately; CLI and MCP end-to-end output was not tested. Related reports [#1428](https://github.com/morluto/rea/issues/1428) and [#1449](https://github.com/morluto/rea/issues/1449) concern false literals after object-reference mutations. They are closed other-author reports with merged fixes for those different inputs; the skipped-initialization cases still fail on the tested baseline. ## Runtime or environment Failure was verified at a2d8b94dd50dab8d966239dd63ab84718aa8b4b4 and is still present at 8b181716aca2cb467be499cba226abfb5ae19141 by source recheck. Node v26.10.0 and npm 11.19.1, with dependencies already installed from the repository lockfile. The focused verifier used a 1536 MiB JavaScript heap limit and completed in 1.08 seconds. ## Restoration check Reanalyze the five fixtures and verify that both the binding and direct return values retain the possibility of skipped initialization rather than publishing a definite 1. Keep `const result = 1;` and `let result; result = 1;` as definite literal controls. Keep `let result = 0; if (condition) result = 1;` uncertain, and do not require `try { var result = 1; } catch (error) {}` to be uncertain merely to satisfy the original report. These controls distinguish restoring uncertainty from discarding every known value. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Five concrete conditional-initialization inputs produce incorrect definite binding and direct return facts. This is consequential but bounded semantic incorrectness; no severe downstream incident or complete workflow failure was observed. - Workaround status: unverified - Workaround basis: No safe, practical workaround preserving semantic results for arbitrary analyzed targets was established; manual source review or execution of real target code was not assessed as a substitute.
bsakaguchi 2026-10-15 18:29:14 +00:00
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#878
No description provided.