[GH-ISSUE #8369] [Bug] Call graph workflows continue and return success after incomplete cleanup #6569

Closed
opened 2026-11-01 21:12:19 +00:00 by bsakaguchi · 1 comment
Owner

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

Originally assigned to: @kaoru0822-kitauji on GitHub.

Before submitting

  • I searched the open and closed issues for a related report.
  • I removed credentials, tokens, private paths, and sensitive target data.

Area

MCP or agent integration

Observed behavior

A provider relationship-read error with cleanupIncomplete: true is embedded in a successful get_call_graph or trace_call_path result. Both workflows then dispatch another queued relationship read. The embedded node/failure correctly has code: "cleanup_incomplete"; the problem is the outer success and continuing work despite that incomplete cleanup.

Reproduction

At REA d54a4661da20382ad8456564a08569fdd24131c5, I ran four public MCP tests through the production server and linked in-memory TypeScript SDK transports with an injected, fixed analysis provider:

  1. procedure_address returns its requested selector. The first procedure_callees("0x1") returns ["0x2", "0x3"] with complete observed relationship coverage.
  2. procedure_callees("0x2") returns AnalysisCapabilityUnavailableError("fixture", "procedure_callees", "child failed", { cleanup: { reason: "worker cleanup failed", resources: ["owned worker"] } }).
  3. procedure_callees("0x3") returns ["0x4"], and the test records every read.
  4. Call get_call_graph({address:"0x1", direction:"forward"}) and trace_call_path({start:"0x1", goal:"0x4"}) separately. Both replies have isError: false and an embedded cleanup_incomplete; both dispatch 0x3 after 0x2. The trace reports goal_status: "reached".
  5. Replace the 0x2 error with the same unavailable error without cleanup metadata. Both calls still return successful partial results and continue to 0x3, as expected for an ordinary recoverable expansion failure.

This is a controlled failure injection, not a real-provider cleanup failure or a claim that an actual process leaked. The isolated worktree contained an unrelated experimental Kuna coverage edit; this test injects the fixed provider and does not call that adapter. Four MCP tests passed in the observed-behavior sense above.

Expected behavior

Do not treat unresolved owned cleanup as an ordinary per-node read failure. Stop dispatching new graph expansions, retain the incomplete-cleanup classification and resource owner/retry capability through the outer outcome, and preserve any already admitted graph observations as partial Evidence where supported. Ordinary read errors without incomplete cleanup should remain usable partial graph results. Also check cancellation arriving with the cleanup failure and cleanup retry/ownership; these combinations were not exercised in this probe.

This follows REA's lifecycle guidance, which requires incomplete cleanup and its owner to remain represented in typed outcomes.

Impact and scope

An MCP caller receives a successful composed result while a child explicitly reports cleanup remains unconfirmed, and unrelated queued work is dispatched. The error is present inside the result; this report does not claim it is absent. Effects on a real engine's process lifetime, session cleanup and retry are NOT_ENOUGH_INFO: only a simulated provider was exercised. The same behavior was observed once per tool with this fixture; other workflows and platforms are unverified.

REA version

d54a4661da20382ad8456564a08569fdd24131c5

Runtime and platform

Node 24.22.1; macOS arm64; TypeScript MCP SDK 2.3.1; injected fixed provider.

Target and artifact context

Synthetic in-memory provider; no binary or real engine.

Interface

MCP

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

Four focused MCP tests: 2 incomplete-cleanup reproductions and 2 ordinary-error controls. For the former, isError was false, the structured result contained cleanup_incomplete, and recorded reads showed 0x2 before 0x3. trace_call_path also reported reached.

Source-derived: EnhancedTools.ts propagates cancellation but projects other errors into graph nodes and continues; CallPathTracing.ts similarly records non-cancellation failures and continues. KunaQueryWork.ts documents that unproven native-command cleanup retains leases, but a real Kuna cleanup failure was not induced here.

Additional context

Related to the closed cancellation-specific #3198 and the broader lifecycle audit #4227. Proposed verification: both directions and workflows, mixed completed/failed children, simultaneous cancellation, cleanup retry and retained partial Evidence through public MCP/CLI. Real-provider injection or a production-boundary lifecycle harness is needed to establish actual ownership behavior.

cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0

Originally created by @morluto on GitHub (Oct 31, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/8369 Originally assigned to: @kaoru0822-kitauji on GitHub. ### 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 MCP or agent integration ### Observed behavior A provider relationship-read error with `cleanupIncomplete: true` is embedded in a **successful** `get_call_graph` or `trace_call_path` result. Both workflows then dispatch another queued relationship read. The embedded node/failure correctly has `code: "cleanup_incomplete"`; the problem is the outer success and continuing work despite that incomplete cleanup. ### Reproduction At REA `d54a4661da20382ad8456564a08569fdd24131c5`, I ran four public MCP tests through the production server and linked in-memory TypeScript SDK transports with an injected, fixed analysis provider: 1. `procedure_address` returns its requested selector. The first `procedure_callees("0x1")` returns `["0x2", "0x3"]` with complete observed relationship coverage. 2. `procedure_callees("0x2")` returns `AnalysisCapabilityUnavailableError("fixture", "procedure_callees", "child failed", { cleanup: { reason: "worker cleanup failed", resources: ["owned worker"] } })`. 3. `procedure_callees("0x3")` returns `["0x4"]`, and the test records every read. 4. Call `get_call_graph({address:"0x1", direction:"forward"})` and `trace_call_path({start:"0x1", goal:"0x4"})` separately. Both replies have `isError: false` and an embedded `cleanup_incomplete`; both dispatch `0x3` after `0x2`. The trace reports `goal_status: "reached"`. 5. Replace the `0x2` error with the same unavailable error **without** cleanup metadata. Both calls still return successful partial results and continue to `0x3`, as expected for an ordinary recoverable expansion failure. This is a controlled failure injection, not a real-provider cleanup failure or a claim that an actual process leaked. The isolated worktree contained an unrelated experimental Kuna coverage edit; this test injects the fixed provider and does not call that adapter. Four MCP tests passed in the observed-behavior sense above. ### Expected behavior Do not treat unresolved owned cleanup as an ordinary per-node read failure. Stop dispatching new graph expansions, retain the incomplete-cleanup classification and resource owner/retry capability through the outer outcome, and preserve any already admitted graph observations as partial Evidence where supported. Ordinary read errors without incomplete cleanup should remain usable partial graph results. Also check cancellation arriving with the cleanup failure and cleanup retry/ownership; these combinations were not exercised in this probe. This follows [REA's lifecycle guidance](https://github.com/morluto/rea/blob/d54a4661da20382ad8456564a08569fdd24131c5/docs/tool-design.md#own-the-lifecycle), which requires incomplete cleanup and its owner to remain represented in typed outcomes. ### Impact and scope An MCP caller receives a successful composed result while a child explicitly reports cleanup remains unconfirmed, and unrelated queued work is dispatched. The error is present inside the result; this report does not claim it is absent. Effects on a real engine's process lifetime, session cleanup and retry are `NOT_ENOUGH_INFO`: only a simulated provider was exercised. The same behavior was observed once per tool with this fixture; other workflows and platforms are unverified. ### REA version `d54a4661da20382ad8456564a08569fdd24131c5` ### Runtime and platform Node 24.22.1; macOS arm64; TypeScript MCP SDK 2.3.1; injected fixed provider. ### Target and artifact context Synthetic in-memory provider; no binary or real engine. ### Interface MCP ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence Four focused MCP tests: 2 incomplete-cleanup reproductions and 2 ordinary-error controls. For the former, `isError` was false, the structured result contained `cleanup_incomplete`, and recorded reads showed `0x2` before `0x3`. `trace_call_path` also reported `reached`. Source-derived: [`EnhancedTools.ts`](https://github.com/morluto/rea/blob/d54a4661da20382ad8456564a08569fdd24131c5/src/application/EnhancedTools.ts#L645-L662) propagates cancellation but projects other errors into graph nodes and continues; [`CallPathTracing.ts`](https://github.com/morluto/rea/blob/d54a4661da20382ad8456564a08569fdd24131c5/src/application/CallPathTracing.ts#L153-L181) similarly records non-cancellation failures and continues. [`KunaQueryWork.ts`](https://github.com/morluto/rea/blob/d54a4661da20382ad8456564a08569fdd24131c5/src/kuna/KunaQueryWork.ts) documents that unproven native-command cleanup retains leases, but a real Kuna cleanup failure was not induced here. ### Additional context Related to the closed cancellation-specific #3198 and the broader lifecycle audit #4227. Proposed verification: both directions and workflows, mixed completed/failed children, simultaneous cancellation, cleanup retry and retained partial Evidence through public MCP/CLI. Real-provider injection or a production-boundary lifecycle harness is needed to establish actual ownership behavior. cc @gensokyo-dev @kaoru0822-kitauji @N0zoM1z0
bsakaguchi 2026-11-01 21:12:19 +00:00
  • closed this issue
  • added the
    bug
    triage
    labels
Author
Owner

@morluto commented on GitHub (Oct 31, 2026):

A production-adapter/session control narrows the ownership question, using a fake Kuna executable and a real spawned subprocess. The process launcher was instrumented to perform its normal stop/cleanup but report that stop as unproven until the test released the injected gate. decompile-graph was made to hang and hit a 750 ms project deadline. This is a deliberately injected proof failure, not evidence that real Kuna 1.986 leaks a process.

Through public MCP with a valid synthetic Mach-O and the production Kuna adapter/BinarySession:

  • get_call_graph and trace_call_path each returned success with an embedded cleanup_incomplete from the root graph read. A separate direct procedure_callees control returned a top-level MCP error with cleanup_incomplete.
  • In all three cases, before attempting close, binary_session({detail:"summary"}) reported open:true, cleanup_pending:false; provider operation health reported an unavailable adapter. The distinction between active-client command cleanup and the status field's intended scope needs clarification.
  • The first close_binary returned cleanup_incomplete; status then reported open:false, cleanup_pending:true. After the test allowed the injected proof, a second close_binary succeeded and status became open:false, cleanup_pending:false. Thus the production adapter retained its cleanup owner and close retry worked in this controlled case; it does not support a claim that the owner was lost.

All three cases passed in focused tests at d54a4661 on macOS arm64 / Node 24.22.1. The fixed-provider branching test in the issue above, not this root-failure control, establishes that a later queued expansion is dispatched. Real-provider natural cleanup failures and other providers remain unverified. The worktree's unrelated experimental coverage edit did not affect this graph-failure path.

<!-- gh-comment-id:6548501698 --> @morluto commented on GitHub (Oct 31, 2026): A production-adapter/session control narrows the ownership question, using a **fake Kuna executable** and a real spawned subprocess. The process launcher was instrumented to perform its normal stop/cleanup but report that stop as unproven until the test released the injected gate. `decompile-graph` was made to hang and hit a 750 ms project deadline. This is a deliberately injected proof failure, not evidence that real Kuna 1.986 leaks a process. Through public MCP with a valid synthetic Mach-O and the production Kuna adapter/BinarySession: - `get_call_graph` and `trace_call_path` each returned success with an embedded `cleanup_incomplete` from the root graph read. A separate direct `procedure_callees` control returned a top-level MCP error with `cleanup_incomplete`. - In all three cases, before attempting close, `binary_session({detail:"summary"})` reported `open:true, cleanup_pending:false`; provider operation health reported an unavailable adapter. The distinction between active-client command cleanup and the status field's intended scope needs clarification. - The first `close_binary` returned `cleanup_incomplete`; status then reported `open:false, cleanup_pending:true`. After the test allowed the injected proof, a second `close_binary` succeeded and status became `open:false, cleanup_pending:false`. Thus the production adapter retained its cleanup owner and close retry worked in this controlled case; it does **not** support a claim that the owner was lost. All three cases passed in focused tests at `d54a4661` on macOS arm64 / Node 24.22.1. The fixed-provider branching test in the issue above, not this root-failure control, establishes that a later queued expansion is dispatched. Real-provider natural cleanup failures and other providers remain unverified. The worktree's unrelated experimental coverage edit did not affect this graph-failure path.
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#6569
No description provided.