[GH-ISSUE #4638] [Bug] Owned command cancellation drops exact caller-reason provenance #2417

Closed
opened 2026-10-17 21:04:22 +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/4638

Before submitting

  • Searched open and closed OwnedCommand cancellation/reason reports; #2760 concerns monotonic deadline expiry, not caller-reason provenance.
  • No credentials or private target data included.

Area

Process capture or runtime observation

Observed behavior

At the production runOwnedCommand boundary, pre-admission cancellation discards the caller's exact abort reason. The resulting OwnedCommandFailure records reason='cancelled' but has no originating reason in its cause or a producer-owned cancellation record. Source inspection shows running/final-cleanup cancellations also construct failures without recording that reason. Preparation failures may retain a raw thrown cause, but that is not an authoritative cancellation record.

Reproduction

On unchanged main71929ed37, abort a controller with null, a primitive string, or a caller-created typed cleanup error. Call runOwnedCommand with that signal and a launcher that must not run. Inspect the thrown OwnedCommandFailure: reason is cancelled, snapshot is null, cleanupFailure is null, resources are empty, and caller reason is absent. A separate controlled launcher aborts during preparation and throws the raw reason. Six focused controls (three reasons across before/preparation) failed to find a producer-owned exact cancellation observation. No subprocess launches in these controls.

Expected behavior

Retain a producer-owned cancellation observation containing the exact opaque caller reason, separate from physical failure causes and cleanup authority. Adapters should consume owner facts rather than resample a signal or reinterpret arbitrary nested causes. Preserve null, primitives and typed error objects without assigning their apparent resource metadata to the command. This follows the repository's boundary-value/ownership rules and existing ArtifactReader/Darwin ownership cancellation representations.

Impact and scope

P2 source-boundary provenance loss; no native-provider incident or downstream user impact established. Preadmission loss reproduced for all three values; preparation record absence reproduced in the controlled launcher. Running/finalization cases are source-derived pending explicit controls. NOT_ENOUGH_INFO: real-provider frequency and public CLI/MCP impact.

REA version

6.5.0, main71929ed37cdc560b6b8aa62b5f7182e417c8a850; only new regression controls changed during reproduction.

Runtime and platform

Node24.18.0, Darwin arm64.

Target and artifact context

No target or launched provider. A controlled launcher rejects during preparation; preadmission controls never invoke it.

Interface

Other: shared production owned-command boundary.

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

OwnedCommandFailure.reason: cancelled
expected exact caller reason: null / 'opaque caller reason' / caller-created ProviderCleanupError
producer cancellation record: undefined

Source: src/process/OwnedCommand.ts preadmission throw and interrupted collection/final cleanup constructors. Existing preparation cause and cleanup-owner fields must remain intact.

Typed outcome or recovery expected

A non-enumerable producer-owned cancellation observation, propagated through cleanup composition; exact reason identity retained. Verify preadmission, preparation, active execution, final cleanup, incomplete cleanup/retry, timeout precedence, and opaque typed reasons. Do not assign caller-metadata cleanup ownership.

Analysis or suspected cause

The helper records only interruption kind and physical/error causes; it lacks a stable parsed fact identifying the caller cancellation reason at the stage that observed it.

Additional context

Found while preparing the owned async Java probe migration for #4561. Fixing this shared prerequisite alone does not fix the synchronous Ghidra discovery/admission path. No target execution or native-engine support claim is involved.

Originally created by @morluto on GitHub (Oct 17, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/4638 ### Before submitting - [x] Searched open and closed OwnedCommand cancellation/reason reports; #2760 concerns monotonic deadline expiry, not caller-reason provenance. - [x] No credentials or private target data included. ### Area Process capture or runtime observation ### Observed behavior At the production runOwnedCommand boundary, pre-admission cancellation discards the caller's exact abort reason. The resulting OwnedCommandFailure records reason='cancelled' but has no originating reason in its cause or a producer-owned cancellation record. Source inspection shows running/final-cleanup cancellations also construct failures without recording that reason. Preparation failures may retain a raw thrown cause, but that is not an authoritative cancellation record. ### Reproduction On unchanged main71929ed37, abort a controller with null, a primitive string, or a caller-created typed cleanup error. Call runOwnedCommand with that signal and a launcher that must not run. Inspect the thrown OwnedCommandFailure: reason is cancelled, snapshot is null, cleanupFailure is null, resources are empty, and caller reason is absent. A separate controlled launcher aborts during preparation and throws the raw reason. Six focused controls (three reasons across before/preparation) failed to find a producer-owned exact cancellation observation. No subprocess launches in these controls. ### Expected behavior Retain a producer-owned cancellation observation containing the exact opaque caller reason, separate from physical failure causes and cleanup authority. Adapters should consume owner facts rather than resample a signal or reinterpret arbitrary nested causes. Preserve null, primitives and typed error objects without assigning their apparent resource metadata to the command. This follows the repository's boundary-value/ownership rules and existing ArtifactReader/Darwin ownership cancellation representations. ### Impact and scope P2 source-boundary provenance loss; no native-provider incident or downstream user impact established. Preadmission loss reproduced for all three values; preparation record absence reproduced in the controlled launcher. Running/finalization cases are source-derived pending explicit controls. NOT_ENOUGH_INFO: real-provider frequency and public CLI/MCP impact. ### REA version 6.5.0, main71929ed37cdc560b6b8aa62b5f7182e417c8a850; only new regression controls changed during reproduction. ### Runtime and platform Node24.18.0, Darwin arm64. ### Target and artifact context No target or launched provider. A controlled launcher rejects during preparation; preadmission controls never invoke it. ### Interface Other: shared production owned-command boundary. ### Earliest failure stage Cleanup, cancellation, or lifecycle ### Relevant output or evidence ```text OwnedCommandFailure.reason: cancelled expected exact caller reason: null / 'opaque caller reason' / caller-created ProviderCleanupError producer cancellation record: undefined ``` Source: src/process/OwnedCommand.ts preadmission throw and interrupted collection/final cleanup constructors. Existing preparation cause and cleanup-owner fields must remain intact. ### Typed outcome or recovery expected A non-enumerable producer-owned cancellation observation, propagated through cleanup composition; exact reason identity retained. Verify preadmission, preparation, active execution, final cleanup, incomplete cleanup/retry, timeout precedence, and opaque typed reasons. Do not assign caller-metadata cleanup ownership. ### Analysis or suspected cause The helper records only interruption kind and physical/error causes; it lacks a stable parsed fact identifying the caller cancellation reason at the stage that observed it. ### Additional context Found while preparing the owned async Java probe migration for #4561. Fixing this shared prerequisite alone does not fix the synchronous Ghidra discovery/admission path. No target execution or native-engine support claim is involved.
Author
Owner

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

Implemented in 408704827057280b240c91963ee5590fd4dcc988.

The command owner now retains an explicit, non-enumerable cancellation observation containing the exact opaque caller reason. Preparation, active collection, failed cleanup wrapping, and cancellation during final cleanup preserve it independently of physical causes and cleanup ownership. Timeouts do not acquire a caller-cancellation observation. The lifecycle guide documents that separation.

Validation performed:

  • 68 focused tests across owned command cancellation, real controlled child-process lifecycle, provider process supervision and deadlines passed. Includes exact null/string/foreign typed reasons, incomplete cleanup and retry, and cancellation during final cleanup.
  • npm run check:fast and npm run docs:check passed; the latter ran 5 README conformance tests.
  • Independent compiled baseline/candidate controls confirm lost versus retained exact reason before admission and during injected preparation. These controls launch no subprocesses. Two alternating warm repeats: admission elapsed 0.05931/0.05943 s, CPU 0.04822/0.04859 s, sampled process-family peak RSS 61.26/60.83 MB; preparation elapsed 0.05927/0.05938 s, CPU 0.04876/0.04985 s, RSS 61.18/60.38 MB. Both return 37 raw cl100k_base reply tokens. Separate first processes were recorded; true cold caches and whole-agent/CLI/MCP/native costs remain unmeasured. No performance improvement claimed.

Scope: controlled local process fixtures and production command-owner boundaries, not real Ghidra/JVM/other provider verification. This does not resolve the synchronous Ghidra probe in #4561. Heavy Qodo/profiling workloads stayed stopped.

<!-- gh-comment-id:6228077395 --> @morluto commented on GitHub (Oct 17, 2026): Implemented in `408704827057280b240c91963ee5590fd4dcc988`. The command owner now retains an explicit, non-enumerable cancellation observation containing the exact opaque caller reason. Preparation, active collection, failed cleanup wrapping, and cancellation during final cleanup preserve it independently of physical causes and cleanup ownership. Timeouts do not acquire a caller-cancellation observation. The lifecycle guide documents that separation. Validation performed: - 68 focused tests across owned command cancellation, real controlled child-process lifecycle, provider process supervision and deadlines passed. Includes exact null/string/foreign typed reasons, incomplete cleanup and retry, and cancellation during final cleanup. - `npm run check:fast` and `npm run docs:check` passed; the latter ran 5 README conformance tests. - Independent compiled baseline/candidate controls confirm lost versus retained exact reason before admission and during injected preparation. These controls launch no subprocesses. Two alternating warm repeats: admission elapsed 0.05931/0.05943 s, CPU 0.04822/0.04859 s, sampled process-family peak RSS 61.26/60.83 MB; preparation elapsed 0.05927/0.05938 s, CPU 0.04876/0.04985 s, RSS 61.18/60.38 MB. Both return 37 raw cl100k_base reply tokens. Separate first processes were recorded; true cold caches and whole-agent/CLI/MCP/native costs remain unmeasured. No performance improvement claimed. Scope: controlled local process fixtures and production command-owner boundaries, not real Ghidra/JVM/other provider verification. This does not resolve the synchronous Ghidra probe in #4561. Heavy Qodo/profiling workloads stayed stopped.
Author
Owner

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

Follow-up shared lifecycle fix pushed to main in e567033a71.

Late cancellation during refused final cleanup is now retained independently of the primary physical command failure and exact cleanup authority. Complete accepted output is observed before awaited final cleanup; physical stream errors retain their original Error, and failed Darwin preparation exposes an exact failed-root retry capability without closing the shared inspector.

Executed: 24 owned-command tests, 6 cancellation controls, 2 targeted Darwin cleanup retry controls, check:fast, normal commit and push checks. Controlled macOS process/file fixtures only; no new real JVM/Ghidra, Linux, or Windows behavior claim. The coordinated Ghidra migration for #4561 remains in progress and uncommitted.

<!-- gh-comment-id:6228887243 --> @morluto commented on GitHub (Oct 17, 2026): Follow-up shared lifecycle fix pushed to main in e567033a7137d5eebf49147ed325cad141cff5c2. Late cancellation during refused final cleanup is now retained independently of the primary physical command failure and exact cleanup authority. Complete accepted output is observed before awaited final cleanup; physical stream errors retain their original Error, and failed Darwin preparation exposes an exact failed-root retry capability without closing the shared inspector. Executed: 24 owned-command tests, 6 cancellation controls, 2 targeted Darwin cleanup retry controls, check:fast, normal commit and push checks. Controlled macOS process/file fixtures only; no new real JVM/Ghidra, Linux, or Windows behavior claim. The coordinated Ghidra migration for #4561 remains in progress and uncommitted.
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#2417
No description provided.