[GH-ISSUE #6615] [Bug] avoid resolved generator-body returns at iterator call sites #4002

Closed
opened 2026-10-31 00:45:56 +00:00 by bsakaguchi · 1 comment
Owner

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

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

Public analyze-javascript-application followed by trace-javascript-semantics reports a generator body's explicit return as a resolved source for the immediate produce() call. Backward provenance from that call returns status: "found", one returns-to-call edge and zero relevant unknowns even though the program never advances the iterator. A synchronous ordinary function, generator function and async generator function receive the same relation. All six commands exit 0. This was independently observed on both the supplied source checkout's compiled CLI and the published package's bundled CLI.

Reproduction

From a built REA source checkout with locked dependencies available, run the following with its supported Node runtime on PATH. It creates isolated synthetic fixtures and configuration, invokes the public launcher, and retains the exact application Evidence, trace requests, trace Evidence and stderr. The block was replayed successfully; it needs no native provider, external service or target execution.

python3 - <<'PY'
import json, os, pathlib, subprocess, tempfile
root = pathlib.Path.cwd()
work = pathlib.Path(tempfile.mkdtemp(prefix="return-flow-"))
for leaf in ("home", "config", "cache", "temp"):
    (work / leaf).mkdir()
env = {"PATH": os.environ["PATH"], "HOME": str(work / "home"),
       "XDG_CONFIG_HOME": str(work / "config"),
       "XDG_CACHE_HOME": str(work / "cache"), "TMPDIR": str(work / "temp")}
def rea(arguments, output):
    run = subprocess.run(["node", str(root / "scripts/rea.mjs"), *arguments,
                          "--format", "json"], env=env, capture_output=True,
                         text=True, timeout=100)
    (work / (output + ".json")).write_text(run.stdout)
    (work / (output + ".stderr")).write_text(run.stderr)
    if run.returncode:
        raise RuntimeError(f"{arguments[0]} exited {run.returncode}: {run.stderr}")
    return json.loads(run.stdout)
for name, declaration in (("ordinary", "function"),
                          ("generator", "function*"),
                          ("async-generator", "async function*")):
    fixture = work / name
    fixture.mkdir()
    (fixture / "package.json").write_text(
        '{"name":"return-flow-fixture","version":"1.0.0","main":"index.js"}\n')
    (fixture / "index.js").write_text(
        declaration + ' produce() {\n  return "TOKEN";\n}\n'
        'const result = produce();\nexports.result = result;\n')
    application = rea(["analyze-javascript-application", str(fixture)], name + "-analysis")
    graph = application["normalized_result"]["semantic_graph"]
    call = next(n for n in graph["nodes"] if n["kind"] == "call-site")
    request = work / (name + "-request.json")
    request.write_text(json.dumps({"application": application, "query": {
        "seed": {"kind": "semantic-node", "node_id": call["node_id"]},
        "direction": "backward-provenance"}}))
    trace = rea(["trace-javascript-semantics", str(request)], name + "-trace")["normalized_result"]
    print(name, "exits=0,0", "status=" + trace["status"],
          "edges=" + str([(r["relation"], r["resolution"]) for r in trace["relations"]]),
          "unknowns=" + str(trace["summary"]["relevant_unknowns"]))
print("Retained evidence:", work)
PY

Both tested runtimes printed the same reduced results:

ordinary exits=0,0 status=found edges=[('returns-to-call', 'resolved')] unknowns=0
generator exits=0,0 status=found edges=[('returns-to-call', 'resolved')] unknowns=0
async-generator exits=0,0 status=found edges=[('returns-to-call', 'resolved')] unknowns=0

Expected behavior

A generator or async generator call returns an iterator object; its body's return "TOKEN" cannot supply that call expression's value. The call-flow contract describes a direct return that may supply the call expression's value, and the workflow documents argument/return flow for uniquely resolved local calls. The generator documentation also distinguishes iterator creation from body evaluation and leaves iterator advancement unresolved. This static graph need not claim that anything executed, but a resolved value-provenance relation must still be semantically possible for the expression it targets. A body return available only through later iterator advancement should not appear as a resolved source of the immediate iterator call result. The ordinary-function control should retain its valid return flow.

Impact and scope

Observed for the two direct local generator declaration forms above. Their semantic graph exposes a false resolved value-flow connection, and default backward provenance traverses it as resolved with zero relevant unknowns. Trace coverage still says partial and retains general static-analysis limitations; the defect is the specific impossible value-flow edge, not a claim that the whole graph is complete or that execution occurred. The graph also aliases the call result to its assigned binding, so the edge belongs to value flow rather than a separate iterator-consumption relation. This is a bounded evidence-correctness defect; no target execution, data loss or security boundary crossing was observed. Arbitrary generator syntax, MCP execution and other platforms were not tested. The callers direction is outside this claim because it traverses only calls relations.

REA version

Source baseline bcd1f95e688a2ac020a9d13f9c85e0ce321cb6b6 with prepared compiled CLI; independently reproduced using the integrity-checked published rea-agents@6.7.0 tarball's bundled CLI with the checkout's locked dependencies. npm and the latest GitHub release both identified 6.7.0 as current.

Runtime and platform

Node.js v24.22.1; macOS 27.0.1 arm64. No native analysis provider required.

Target and artifact context

Each application is a local CommonJS directory containing only the inline synthetic module and package manifest. Analysis and trace use authenticated application Evidence. REA does not execute these modules.

Interface

CLI

Earliest failure stage

Native analysis or reconstruction

Relevant output or evidence

  • Expected source: src/domain/javascript/javascriptSemanticIr.ts:151 defines the call-return flow as a direct local return that may supply the call expression's value; docs/javascript-application-workflows.md:165 documents semantic argument/return flow and default uncertainty handling; docs/javascript-artifact-reconstruction.md:427 documents iterator identity and unevaluated generator bodies in factory evaluation, as corroboration rather than the sole graph contract; ECMAScript generator body evaluation and GeneratorStart distinguish creation from resumption.
  • Failure source: Public analysis and backward trace of the inline fixtures emit and traverse returns-to-call: resolved; src/domain/javascript/javascriptSemanticCalls.ts:170 admits ordinary call sites and src/domain/javascript/javascriptSemanticCalls.ts:287 collects each resolved callee's return sites without distinguishing generators; src/application/javascript/JavaScriptSemanticGraphBuilder.ts:935 projects those flows with call-target resolution.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The generator trace contains only the call-site and return-site nodes, one resolved edge, and summary.relevant_unknowns: 0. The return node's source span identifies the body's explicit return. Separate synthetic Node controls observed zero body executions immediately after generator and async-generator calls; their first next() returned or fulfilled with {value:"TOKEN",done:true}. The ordinary call immediately returned "TOKEN". These controls establish JavaScript behavior separately from REA's static Evidence.

Typed outcome or recovery expected

Preserve the exact local calls relation while avoiding a resolved body-return value source for the immediate generator call. If the graph represents unsupported iterator-consumption flow, retain its uncertainty and distinction from the call's iterator object. Keep ordinary-function return provenance intact. No particular internal topology or implementation is required.

Analysis or suspected cause

Source-derived explanation: return-flow collection accepts every retained callee return site after identifying an ordinary call expression, and graph projection reuses the callee-resolution status. Resolving which function is called does not establish that the function's body return supplies the immediate expression value. The reproduced edge contains no iterator-consumption qualifier. General static-analysis limitations do not make an impossible value source a conservative candidate. No fix was implemented or tested.

Additional context

Fresh open and closed issue searches covered generator, iterator, return-flow and the exact relation spelling. #1449 is related but distinct: its closure addresses stale static literals after writes through returned objects, including explicit iterator consumption; this reproduction has no object mutation or iterator advancement. #2569 concerns fingerprint collisions, #2576 inconsistent unknown-coverage validation, and #5585 and #5648 missing property-value provenance. Their closure signals do not remove the current false generator edge. The ordinary async-function control also produced the same raw edge; that observation does not establish a separate reported defect here. No source edits or publication were performed.

Restoration check

Proposed, not executed against a fix: rerun the block and confirm that generator and async-generator calls no longer present their body returns as resolved sources of their immediate iterator values, while the ordinary control keeps its resolved return flow. Confirm that the generator's exact local calls relation remains and that unresolved iterator advancement does not become falsely resolved. A status-only change that retains the false resolved edge does not restore this contract.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: Two supported local generator forms reproducibly add a false resolved source to value-provenance results. This is consequential but bounded static Evidence corruption; no wider workflow failure or security impact was established.
  • Workaround status: available
  • Workaround basis: For these reduced fixtures, the returned function and return nodes carry exact source spans; an analyst can inspect the inline callee declaration, identify the generator marker and disregard the impossible immediate-return attribution. This manual check does not repair the graph or provide an automatic filter for larger artifacts.
Originally created by @coygeek on GitHub (Oct 23, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6615 ## 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 Public `analyze-javascript-application` followed by `trace-javascript-semantics` reports a generator body's explicit return as a resolved source for the immediate `produce()` call. Backward provenance from that call returns `status: "found"`, one `returns-to-call` edge and zero relevant unknowns even though the program never advances the iterator. A synchronous ordinary function, generator function and async generator function receive the same relation. All six commands exit 0. This was independently observed on both the supplied source checkout's compiled CLI and the published package's bundled CLI. ## Reproduction From a built REA source checkout with locked dependencies available, run the following with its supported Node runtime on PATH. It creates isolated synthetic fixtures and configuration, invokes the public launcher, and retains the exact application Evidence, trace requests, trace Evidence and stderr. The block was replayed successfully; it needs no native provider, external service or target execution. ```sh python3 - <<'PY' import json, os, pathlib, subprocess, tempfile root = pathlib.Path.cwd() work = pathlib.Path(tempfile.mkdtemp(prefix="return-flow-")) for leaf in ("home", "config", "cache", "temp"): (work / leaf).mkdir() env = {"PATH": os.environ["PATH"], "HOME": str(work / "home"), "XDG_CONFIG_HOME": str(work / "config"), "XDG_CACHE_HOME": str(work / "cache"), "TMPDIR": str(work / "temp")} def rea(arguments, output): run = subprocess.run(["node", str(root / "scripts/rea.mjs"), *arguments, "--format", "json"], env=env, capture_output=True, text=True, timeout=100) (work / (output + ".json")).write_text(run.stdout) (work / (output + ".stderr")).write_text(run.stderr) if run.returncode: raise RuntimeError(f"{arguments[0]} exited {run.returncode}: {run.stderr}") return json.loads(run.stdout) for name, declaration in (("ordinary", "function"), ("generator", "function*"), ("async-generator", "async function*")): fixture = work / name fixture.mkdir() (fixture / "package.json").write_text( '{"name":"return-flow-fixture","version":"1.0.0","main":"index.js"}\n') (fixture / "index.js").write_text( declaration + ' produce() {\n return "TOKEN";\n}\n' 'const result = produce();\nexports.result = result;\n') application = rea(["analyze-javascript-application", str(fixture)], name + "-analysis") graph = application["normalized_result"]["semantic_graph"] call = next(n for n in graph["nodes"] if n["kind"] == "call-site") request = work / (name + "-request.json") request.write_text(json.dumps({"application": application, "query": { "seed": {"kind": "semantic-node", "node_id": call["node_id"]}, "direction": "backward-provenance"}})) trace = rea(["trace-javascript-semantics", str(request)], name + "-trace")["normalized_result"] print(name, "exits=0,0", "status=" + trace["status"], "edges=" + str([(r["relation"], r["resolution"]) for r in trace["relations"]]), "unknowns=" + str(trace["summary"]["relevant_unknowns"])) print("Retained evidence:", work) PY ``` Both tested runtimes printed the same reduced results: ```text ordinary exits=0,0 status=found edges=[('returns-to-call', 'resolved')] unknowns=0 generator exits=0,0 status=found edges=[('returns-to-call', 'resolved')] unknowns=0 async-generator exits=0,0 status=found edges=[('returns-to-call', 'resolved')] unknowns=0 ``` ## Expected behavior A generator or async generator call returns an iterator object; its body's `return "TOKEN"` cannot supply that call expression's value. The call-flow contract describes a direct return that may supply the call expression's value, and the workflow documents argument/return flow for uniquely resolved local calls. The generator documentation also distinguishes iterator creation from body evaluation and leaves iterator advancement unresolved. This static graph need not claim that anything executed, but a resolved value-provenance relation must still be semantically possible for the expression it targets. A body return available only through later iterator advancement should not appear as a resolved source of the immediate iterator call result. The ordinary-function control should retain its valid return flow. ## Impact and scope Observed for the two direct local generator declaration forms above. Their semantic graph exposes a false resolved value-flow connection, and default backward provenance traverses it as resolved with zero relevant unknowns. Trace coverage still says `partial` and retains general static-analysis limitations; the defect is the specific impossible value-flow edge, not a claim that the whole graph is complete or that execution occurred. The graph also aliases the call result to its assigned binding, so the edge belongs to value flow rather than a separate iterator-consumption relation. This is a bounded evidence-correctness defect; no target execution, data loss or security boundary crossing was observed. Arbitrary generator syntax, MCP execution and other platforms were not tested. The `callers` direction is outside this claim because it traverses only `calls` relations. ## REA version Source baseline `bcd1f95e688a2ac020a9d13f9c85e0ce321cb6b6` with prepared compiled CLI; independently reproduced using the integrity-checked published `rea-agents@6.7.0` tarball's bundled CLI with the checkout's locked dependencies. npm and the latest GitHub release both identified 6.7.0 as current. ## Runtime and platform Node.js v24.22.1; macOS 27.0.1 arm64. No native analysis provider required. ## Target and artifact context Each application is a local CommonJS directory containing only the inline synthetic module and package manifest. Analysis and trace use authenticated application Evidence. REA does not execute these modules. ## Interface CLI ## Earliest failure stage Native analysis or reconstruction ## Relevant output or evidence - Expected source: `src/domain/javascript/javascriptSemanticIr.ts:151` defines the call-return flow as a direct local return that may supply the call expression's value; `docs/javascript-application-workflows.md:165` documents semantic argument/return flow and default uncertainty handling; `docs/javascript-artifact-reconstruction.md:427` documents iterator identity and unevaluated generator bodies in factory evaluation, as corroboration rather than the sole graph contract; ECMAScript [generator body evaluation](https://tc39.es/ecma262/multipage/ecmascript-language-functions-and-classes.html#sec-runtime-semantics-evaluatebody) and [GeneratorStart](https://tc39.es/ecma262/multipage/control-abstraction-objects.html#sec-generatorstart) distinguish creation from resumption. - Failure source: Public analysis and backward trace of the inline fixtures emit and traverse `returns-to-call: resolved`; `src/domain/javascript/javascriptSemanticCalls.ts:170` admits ordinary call sites and `src/domain/javascript/javascriptSemanticCalls.ts:287` collects each resolved callee's return sites without distinguishing generators; `src/application/javascript/JavaScriptSemanticGraphBuilder.ts:935` projects those flows with call-target resolution. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The generator trace contains only the call-site and return-site nodes, one resolved edge, and `summary.relevant_unknowns: 0`. The return node's source span identifies the body's explicit return. Separate synthetic Node controls observed zero body executions immediately after generator and async-generator calls; their first `next()` returned or fulfilled with `{value:"TOKEN",done:true}`. The ordinary call immediately returned `"TOKEN"`. These controls establish JavaScript behavior separately from REA's static Evidence. ## Typed outcome or recovery expected Preserve the exact local `calls` relation while avoiding a resolved body-return value source for the immediate generator call. If the graph represents unsupported iterator-consumption flow, retain its uncertainty and distinction from the call's iterator object. Keep ordinary-function return provenance intact. No particular internal topology or implementation is required. ## Analysis or suspected cause Source-derived explanation: return-flow collection accepts every retained callee return site after identifying an ordinary call expression, and graph projection reuses the callee-resolution status. Resolving which function is called does not establish that the function's body return supplies the immediate expression value. The reproduced edge contains no iterator-consumption qualifier. General static-analysis limitations do not make an impossible value source a conservative candidate. No fix was implemented or tested. ## Additional context Fresh open and closed issue searches covered generator, iterator, return-flow and the exact relation spelling. [#1449](https://github.com/morluto/rea/issues/1449) is related but distinct: its closure addresses stale static literals after writes through returned objects, including explicit iterator consumption; this reproduction has no object mutation or iterator advancement. [#2569](https://github.com/morluto/rea/issues/2569) concerns fingerprint collisions, [#2576](https://github.com/morluto/rea/issues/2576) inconsistent unknown-coverage validation, and [#5585](https://github.com/morluto/rea/issues/5585) and [#5648](https://github.com/morluto/rea/issues/5648) missing property-value provenance. Their closure signals do not remove the current false generator edge. The ordinary async-function control also produced the same raw edge; that observation does not establish a separate reported defect here. No source edits or publication were performed. ## Restoration check Proposed, not executed against a fix: rerun the block and confirm that generator and async-generator calls no longer present their body returns as resolved sources of their immediate iterator values, while the ordinary control keeps its resolved return flow. Confirm that the generator's exact local `calls` relation remains and that unresolved iterator advancement does not become falsely resolved. A status-only change that retains the false resolved edge does not restore this contract. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: Two supported local generator forms reproducibly add a false resolved source to value-provenance results. This is consequential but bounded static Evidence corruption; no wider workflow failure or security impact was established. - Workaround status: available - Workaround basis: For these reduced fixtures, the returned function and return nodes carry exact source spans; an analyst can inspect the inline callee declaration, identify the generator marker and disregard the impossible immediate-return attribution. This manual check does not repair the graph or provide an automatic filter for larger artifacts.
Author
Owner

@coygeek commented on GitHub (Oct 24, 2026):

Checked the reported fix on main at d87e73f2db.

The callable-result owner distinguishes iterator, async-iterator, promise and direct values. collectReturnFlows now adds a dynamic-call frontier and skips body return edges for non-value direct results. Graph construction still emits the exact calls relation independently and projects returns-to-call only from retained callReturnFlows.

Implementation: javascriptSemanticCallableResults.ts, javascriptSemanticCalls.ts.

The relevant regression checks in javascriptSemanticAnalysis.identity_boundaries.test.ts passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.

<!-- gh-comment-id:6380763044 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. The callable-result owner distinguishes iterator, async-iterator, promise and direct values. collectReturnFlows now adds a dynamic-call frontier and skips body return edges for non-value direct results. Graph construction still emits the exact calls relation independently and projects returns-to-call only from retained callReturnFlows. Implementation: [javascriptSemanticCallableResults.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticCallableResults.ts#L56), [javascriptSemanticCalls.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticCalls.ts#L298). The relevant regression checks in [javascriptSemanticAnalysis.identity_boundaries.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/domain/javascript/javascriptSemanticAnalysis.identity_boundaries.test.ts) passed in the focused test run on this revision. This closure is based on the current source and those checks; provider fixtures do not establish a real-engine run. Closing as completed on main.
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#4002
No description provided.