[GH-ISSUE #4633] [Bug] IDA cancellation loses live startup cleanup ownership #2415

Closed
opened 2026-10-17 21:04:20 +00:00 by bsakaguchi · 2 comments
Owner

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

Before submitting

  • Searched open and closed IDA cancellation/cleanup and IdaSessionClient cancellation reports. #1710 concerns reaped workers; #4628 concerns composite primary projection, rather than this cancellation wrapper's lost owner binding.
  • No credentials or private target data included.

Area

Native analysis or decompilation

Observed behavior

At the compiled production IDA session boundary on main 3e8236639, cancellation during a failed startup copies cleanup history into a new cancellation error but does not bind that error to the exact live cleanup owner. The cancellation remains cleanup_incomplete even after that owner confirms release. The same false incomplete result occurs if release completed before startup failure reached the cancellation handler.

Observed with a controlled connection and actual AnalysisCleanupLease; not a native IDA or public transport reproduction.

Reproduction

Construct an AnalysisTimeoutError('ida_mcp_connect', 123) with cleanup observation {reason:'release denied',resources:['owned transport']}. Bind it to an AnalysisCleanupLease whose release callback can first reject, then succeed. Give IdaSessionClient a controlled connection whose connect() aborts the request's controller and throws that error. Execute procedure_pseudo_code with {procedure:'main'} and the controller signal. Project the returned error, release the lease successfully, and project again. Separately release the lease before aborting/throwing from connect(). No provider tool call occurs.

Both cases were executed once against compiled main; two matching source regression controls also failed before repair. Source tests ran during the independent, uncommitted #4614 startup attribution work, which leaves the cancellation branch unchanged.

Expected behavior

Remain cleanup-incomplete until the exact owner releases, then report cancellation without live resource attribution. Historical cleanup is not evidence of current ownership. This follows AnalysisError.bindCleanupDependencies/resolveCleanup, AnalysisCleanupLease, and docs/tool-design.md lifecycle ownership rules.

Impact and scope

P2: a machine-readable cancellation outcome falsely reports unresolved cleanup and cannot recover through the original owner's successful retry. No actual leak, data loss, real-provider frequency or downstream incident established. NOT_ENOUGH_INFO: native IDA incidence and public transport reachability in a real cancellation race.

REA version

6.5.0, main 3e8236639a.

Runtime and platform

Node 24.18.0, Darwin arm64; no IDA engine.

Target and artifact context

Synthetic 64-byte ELF header and a controlled connection that fails during startup, before workspace or provider tool dispatch. Target is not executed.

Interface

Other: production provider session boundary.

Earliest failure stage

Cleanup, cancellation, or lifecycle.

Relevant output or evidence

[{"state":"pending","before":"cleanup_incomplete","after":"cleanup_incomplete","primary_cleanup_incomplete":false,"cancelled_cleanup_incomplete":true},{"state":"released","before":"cleanup_incomplete","after":"cleanup_incomplete","primary_cleanup_incomplete":false,"cancelled_cleanup_incomplete":true}]

Source-derived explanation: src/ida/IdaSessionClient.ts #failure cancellation branch constructs AnalysisCancelledError with cleanup: cause.cleanup, but does not bind dependencies or the originating resource owner. The historical observation also initializes a fresh incomplete flag after the source error already resolved.

Typed outcome or recovery expected

Preserve typed cancellation and captured output; bind live cleanup to its exact source owner. Cover denied retry, release after cancellation projection, and release before cancellation projection. Do not infer cleanup success from resource names.

Analysis or suspected cause

The cancellation wrapper copies historical facts while dropping the live completion dependency. This is separate from #4614's operation attribution but touches the same failure boundary.

Additional context

Found while auditing the affected consumers for #4614. No native IDA integration, target mutation, attached-GUI close/save, or real-engine support claim is involved.

Originally created by @morluto on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4633 ### Before submitting - [x] Searched open and closed IDA cancellation/cleanup and IdaSessionClient cancellation reports. #1710 concerns reaped workers; #4628 concerns composite primary projection, rather than this cancellation wrapper's lost owner binding. - [x] No credentials or private target data included. ### Area Native analysis or decompilation ### Observed behavior At the compiled production IDA session boundary on main 3e8236639, cancellation during a failed startup copies cleanup history into a new cancellation error but does not bind that error to the exact live cleanup owner. The cancellation remains `cleanup_incomplete` even after that owner confirms release. The same false incomplete result occurs if release completed before startup failure reached the cancellation handler. Observed with a controlled connection and actual AnalysisCleanupLease; not a native IDA or public transport reproduction. ### Reproduction Construct an AnalysisTimeoutError('ida_mcp_connect', 123) with cleanup observation `{reason:'release denied',resources:['owned transport']}`. Bind it to an AnalysisCleanupLease whose release callback can first reject, then succeed. Give IdaSessionClient a controlled connection whose connect() aborts the request's controller and throws that error. Execute `procedure_pseudo_code` with `{procedure:'main'}` and the controller signal. Project the returned error, release the lease successfully, and project again. Separately release the lease before aborting/throwing from connect(). No provider tool call occurs. Both cases were executed once against compiled main; two matching source regression controls also failed before repair. Source tests ran during the independent, uncommitted #4614 startup attribution work, which leaves the cancellation branch unchanged. ### Expected behavior Remain cleanup-incomplete until the exact owner releases, then report cancellation without live resource attribution. Historical cleanup is not evidence of current ownership. This follows AnalysisError.bindCleanupDependencies/resolveCleanup, AnalysisCleanupLease, and docs/tool-design.md lifecycle ownership rules. ### Impact and scope P2: a machine-readable cancellation outcome falsely reports unresolved cleanup and cannot recover through the original owner's successful retry. No actual leak, data loss, real-provider frequency or downstream incident established. NOT_ENOUGH_INFO: native IDA incidence and public transport reachability in a real cancellation race. ### REA version 6.5.0, main 3e8236639abffa39deb5d5e939ec8baf6aaf3ca3. ### Runtime and platform Node 24.18.0, Darwin arm64; no IDA engine. ### Target and artifact context Synthetic 64-byte ELF header and a controlled connection that fails during startup, before workspace or provider tool dispatch. Target is not executed. ### Interface Other: production provider session boundary. ### Earliest failure stage Cleanup, cancellation, or lifecycle. ### Relevant output or evidence ```json [{"state":"pending","before":"cleanup_incomplete","after":"cleanup_incomplete","primary_cleanup_incomplete":false,"cancelled_cleanup_incomplete":true},{"state":"released","before":"cleanup_incomplete","after":"cleanup_incomplete","primary_cleanup_incomplete":false,"cancelled_cleanup_incomplete":true}] ``` Source-derived explanation: `src/ida/IdaSessionClient.ts` #failure cancellation branch constructs AnalysisCancelledError with `cleanup: cause.cleanup`, but does not bind dependencies or the originating resource owner. The historical observation also initializes a fresh incomplete flag after the source error already resolved. ### Typed outcome or recovery expected Preserve typed cancellation and captured output; bind live cleanup to its exact source owner. Cover denied retry, release after cancellation projection, and release before cancellation projection. Do not infer cleanup success from resource names. ### Analysis or suspected cause The cancellation wrapper copies historical facts while dropping the live completion dependency. This is separate from #4614's operation attribution but touches the same failure boundary. ### Additional context Found while auditing the affected consumers for #4614. No native IDA integration, target mutation, attached-GUI close/save, or real-engine support claim is involved.
Author
Owner

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

Implemented in 8f65d7f256, pushed to main.

Startup stays cached, while each accepted request receives its own typed failure with the caller operation. details.diagnostics.startup_operation retains the original initialization/catalog/connection/database-open stage; existing profile diagnostics, timeout budgets, resource limits, output and recovery remain available. The original cached cause is unchanged. The redundant typed-error branch was removed. Documented busy-health operation text and post-operation partial Evidence remain covered.

The same boundary audit found #4633: cancellation copied historical cleanup without a live completion dependency. Cancellation now binds to the exact source owner, so denied release remains incomplete and confirmed release clears the cancellation outcome, including release before wrapping.

Executed validation:

  • 130 tests across nine focused files passed, including five public CLI startup/refusal/success cases, shared startup attribution for distinct callers, resource limits, failed cleanup retry, release before/after cancellation, retained one-shot cleanup, stderr diagnostics, busy health, and partial operation Evidence.
  • Four source controls reran after correcting the fixture's explicit HTTP headers type.
  • Typecheck/lint, docs checks, five documentation conformance controls, normal commit hooks and normal pre-push checks passed. Read-only source review found no blocker.
  • Independent compiled main reproduction for #4633 returned cleanup_incomplete after release in both cases. The repaired build returns cancelled after release and immediately when release preceded wrapping.

Bounded cost comparison: same completed public CLI missing-profile refusal through a loopback SDK supervisor; two alternating warm repeats per build plus separately recorded first processes. Baseline/candidate medians: elapsed 0.48933/0.47599 seconds, CPU 0.70581/0.70182 seconds, sampled peak process-family RSS 423,223,296/441,040,896 bytes, raw reply tokens 247/254 (cl100k_base). A full duplicate startup projection was rejected after measurement; the compact stage adds seven estimated tokens. No speed or memory improvement claimed. True cold, whole-agent, MCP/catalog/request/retry and native-engine costs remain unmeasured.

Scope: public CLI tests use a controlled loopback supervisor; cancellation/ownership controls use a controlled connection and actual cleanup lease. No native IDA engine or attached GUI was used, and no real-engine support claim is added. Heavy Qodo/profiler jobs were not restarted.

<!-- gh-comment-id:6227814430 --> @morluto commented on GitHub (Oct 17, 2026): Implemented in 8f65d7f2569c36707da26ad04b64488fcaa7664d, pushed to main. Startup stays cached, while each accepted request receives its own typed failure with the caller operation. `details.diagnostics.startup_operation` retains the original initialization/catalog/connection/database-open stage; existing profile diagnostics, timeout budgets, resource limits, output and recovery remain available. The original cached cause is unchanged. The redundant typed-error branch was removed. Documented busy-health operation text and post-operation partial Evidence remain covered. The same boundary audit found #4633: cancellation copied historical cleanup without a live completion dependency. Cancellation now binds to the exact source owner, so denied release remains incomplete and confirmed release clears the cancellation outcome, including release before wrapping. Executed validation: - 130 tests across nine focused files passed, including five public CLI startup/refusal/success cases, shared startup attribution for distinct callers, resource limits, failed cleanup retry, release before/after cancellation, retained one-shot cleanup, stderr diagnostics, busy health, and partial operation Evidence. - Four source controls reran after correcting the fixture's explicit HTTP headers type. - Typecheck/lint, docs checks, five documentation conformance controls, normal commit hooks and normal pre-push checks passed. Read-only source review found no blocker. - Independent compiled main reproduction for #4633 returned cleanup_incomplete after release in both cases. The repaired build returns cancelled after release and immediately when release preceded wrapping. Bounded cost comparison: same completed public CLI missing-profile refusal through a loopback SDK supervisor; two alternating warm repeats per build plus separately recorded first processes. Baseline/candidate medians: elapsed 0.48933/0.47599 seconds, CPU 0.70581/0.70182 seconds, sampled peak process-family RSS 423,223,296/441,040,896 bytes, raw reply tokens 247/254 (cl100k_base). A full duplicate startup projection was rejected after measurement; the compact stage adds seven estimated tokens. No speed or memory improvement claimed. True cold, whole-agent, MCP/catalog/request/retry and native-engine costs remain unmeasured. Scope: public CLI tests use a controlled loopback supervisor; cancellation/ownership controls use a controlled connection and actual cleanup lease. No native IDA engine or attached GUI was used, and no real-engine support claim is added. Heavy Qodo/profiler jobs were not restarted.
Author
Owner

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

Closing as fixed on main after reconciling this report with the landed owner/consumer changes.

  • 8f65d7f25: fix(ida): attribute startup failures and retain cleanup ownership. Link cancellation wrappers to the originating live cleanup owner, including release before and after wrapping. Remove the redundant typed-error branch.

Evidence scope: this closure uses the landed source, regression coverage and validation already recorded in the linked commits/thread. No new local tests or native-provider reproduction were run during this backlog pass; previously documented platform and real-provider gaps still apply.

<!-- gh-comment-id:6255997047 --> @morluto commented on GitHub (Oct 18, 2026): <!-- rea-backlog-reconciliation-2026-10-19-4633 --> Closing as fixed on `main` after reconciling this report with the landed owner/consumer changes. - [8f65d7f25](https://github.com/morluto/rea/commit/8f65d7f2569c36707da26ad04b64488fcaa7664d): fix(ida): attribute startup failures and retain cleanup ownership. Link cancellation wrappers to the originating live cleanup owner, including release before and after wrapping. Remove the redundant typed-error branch. Evidence scope: this closure uses the landed source, regression coverage and validation already recorded in the linked commits/thread. No new local tests or native-provider reproduction were run during this backlog pass; previously documented platform and real-provider gaps still apply.
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#2415
No description provided.