[GH-ISSUE #5106] [Bug] Cancelling doctor does not interrupt its Kuna version probe #3144

Closed
opened 2026-10-31 00:37:08 +00:00 by bsakaguchi · 1 comment
Owner

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

Before submitting

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

Area

Provider or capability discovery

Observed behavior

Controlled production doctor-host boundary reproduction with an authored executable fixture (fake provider), not a real Kuna run or a public CLI invocation. After the fixture announced that its version probe had started, aborting the signal supplied to createSystemDoctorHost left providerInspections() pending. At a check after awaiting 250 ms it was still pending. Releasing the fixture then yielded a successful Kuna installation observation despite the aborted outer signal.

A separate direct inspectKunaInstallation control using its signal option rejected on abort and preserved the exact caller reason. This isolates the disconnected signal at doctor composition.

Reproduction

Run from a source checkout of the revision below with its locked dependencies present, on macOS or Linux. This standalone reduced probe was executed successfully. It uses only a small authored executable and private HOME/cache paths, disables Java/Ghidra discovery through absent fixture locations, and awaits the fixture and doctor-host closure. It has a four-second fixture watchdog.

Create a fresh scratch directory with WORK="$(mktemp -d)". Save the following as $WORK/probe.mjs:

import assert from "node:assert/strict";
import { mkdir, writeFile, access } from "node:fs/promises";
import { join, resolve } from "node:path";
import { pathToFileURL } from "node:url";
import { setTimeout as delay } from "node:timers/promises";
const [repo, work] = process.argv.slice(2).map(value => resolve(value));
const { createSystemDoctorHost } = await import(
  pathToFileURL(join(repo, "src/doctorRuntime.ts")).href);
const ready = join(work, "ready"), release = join(work, "release");
const command = join(work, "kuna-fixture");
await mkdir(join(work, "home"), { recursive: true });
await writeFile(command, `#!${process.execPath}
const fs = require("node:fs");
fs.writeFileSync(${JSON.stringify(ready)}, "ready");
const watchdog = setTimeout(() => process.exit(9), 4000);
const gate = setInterval(() => {
  if (!fs.existsSync(${JSON.stringify(release)})) return;
  clearInterval(gate); clearTimeout(watchdog);
  process.stdout.write(${JSON.stringify("kuna 1.833\n")});
}, 10);
`, { mode: 0o700 });
const controller = new AbortController();
const host = createSystemDoctorHost({
  HOME: join(work, "home"), PATH: "", JAVA_HOME: join(work, "missing-jdk"),
  GHIDRA_INSTALL_DIR: join(work, "missing-ghidra"),
  REA_KUNA_COMMAND: command, REA_KUNA_CACHE_DIR: join(work, "cache"),
}, controller.signal);
let settled = false;
const pending = host.providerInspections().finally(() => { settled = true; });
try {
  const deadline = performance.now() + 15000;
  while (!(await access(ready).then(() => true, () => false))) {
    assert(performance.now() < deadline, "fixture not reached");
    await delay(10);
  }
  controller.abort("cancel doctor inspection");
  await delay(250);
  console.log(JSON.stringify({ signalAborted: controller.signal.aborted,
    inspectionSettledBeforeRelease: settled }));
  assert.equal(settled, false);
  await writeFile(release, "release");
  const kuna = (await pending).find(value => value.id === "kuna");
  console.log(JSON.stringify({ kunaAvailableAfterRelease: kuna.available }));
  assert.equal(kuna.available, true);
} finally {
  await writeFile(release, "release");
  await pending;
  await host.close();
}

Execute from the checkout root with a supported Node runtime:

HOME="$WORK/home" node --import ./node_modules/tsx/dist/loader.mjs   "$WORK/probe.mjs" "$PWD" "$WORK"

The assertions describe the current defective behavior. Output from the executed reduced probe:

{"signalAborted":true,"inspectionSettledBeforeRelease":false}
{"kunaAvailableAfterRelease":true}

Expected behavior

The doctor cancellation signal should reach Kuna's version probe so cancellation can stop that owned prerequisite and await cleanup. The lifecycle contract requires cancellation through awaited observations and cleanup; the installation inspector already accepts that signal.

Impact and scope

Doctor continues unnecessary local readiness work after cancellation and waits for the probe to finish or reach its independent deadline. The controlled fixture reproduced the pending inspection and eventual completion after release. The configured version deadline is 10,000 ms; no ten-second latency measurement or indefinite hang is claimed. No data loss or escaped process was observed.

SOURCE-ONLY caller reachability: the CLI forwards its interrupt signal to this factory, and doctor diagnostics awaits these provider inspections. Actual public CLI timing, real Kuna behavior and Linux runtime coverage are NOT_ENOUGH_INFO; they were not exercised.

REA version

Source revision 3537e58c1ab29309f06eabf50bc52adba9817a0c, package version 6.7.0. This source checkout was tested; no installed package runtime was tested.

Runtime and platform

Node v24.22.0; macOS 27.0.1; arm64. Authored executable fixture; real Kuna not invoked.

Target and artifact context

Local prerequisite version inspection only. No reverse-engineering target, GUI provider, external service or real user configuration was used.

Interface

CLI

Earliest failure stage

Cleanup, cancellation, or lifecycle

Relevant output or evidence

The JSON above came from the executed production-host probe, which exited 0 after observing the defect. The additional direct-inspector control returned rejectedOnAbort=true and exactReason=true.

Doctor composition forwards inspectionSignal to Ghidra/JADX but omits it from Kuna. Kuna doctor inspection accepts file-close ownership only and calls the signal-aware installation inspector without a signal.

Typed outcome or recovery expected

Cancellation should reach the in-flight prerequisite, with joined owned cleanup and the caller's cancellation reason preserved. Eventual public CLI output and interrupt exit status are NOT_ENOUGH_INFO; this probe did not measure them.

Analysis or suspected cause

Source-derived cause: the doctor adapter drops the signal before invoking Kuna's installation inspector. The same omission also disconnects its signal-aware fingerprint work; fingerprint delay was not runtime-tested.

Additional context

Related #4698 concerns shared session installation acquisition and session closure. This report exercises the independent doctor caller; correcting the session caller alone would leave this signal disconnected.

Compared with stable tag rea-agents-6.7.0 at 27dc42d10249f0410904a75c0997842eb03b931c, the relevant Kuna files and doctor call are source-equivalent. The main revision has unrelated Hopper runtime changes. Stable package execution and the first affected release are NOT_ENOUGH_INFO; source equality is not a stable runtime reproduction.

Originally created by @coygeek on GitHub (Oct 18, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/5106 ## 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 Provider or capability discovery ## Observed behavior Controlled production doctor-host boundary reproduction with an authored executable fixture (fake provider), not a real Kuna run or a public CLI invocation. After the fixture announced that its version probe had started, aborting the signal supplied to `createSystemDoctorHost` left `providerInspections()` pending. At a check after awaiting 250 ms it was still pending. Releasing the fixture then yielded a successful Kuna installation observation despite the aborted outer signal. A separate direct `inspectKunaInstallation` control using its signal option rejected on abort and preserved the exact caller reason. This isolates the disconnected signal at doctor composition. ## Reproduction Run from a source checkout of the revision below with its locked dependencies present, on macOS or Linux. This standalone reduced probe was executed successfully. It uses only a small authored executable and private HOME/cache paths, disables Java/Ghidra discovery through absent fixture locations, and awaits the fixture and doctor-host closure. It has a four-second fixture watchdog. Create a fresh scratch directory with `WORK="$(mktemp -d)"`. Save the following as `$WORK/probe.mjs`: ```js import assert from "node:assert/strict"; import { mkdir, writeFile, access } from "node:fs/promises"; import { join, resolve } from "node:path"; import { pathToFileURL } from "node:url"; import { setTimeout as delay } from "node:timers/promises"; const [repo, work] = process.argv.slice(2).map(value => resolve(value)); const { createSystemDoctorHost } = await import( pathToFileURL(join(repo, "src/doctorRuntime.ts")).href); const ready = join(work, "ready"), release = join(work, "release"); const command = join(work, "kuna-fixture"); await mkdir(join(work, "home"), { recursive: true }); await writeFile(command, `#!${process.execPath} const fs = require("node:fs"); fs.writeFileSync(${JSON.stringify(ready)}, "ready"); const watchdog = setTimeout(() => process.exit(9), 4000); const gate = setInterval(() => { if (!fs.existsSync(${JSON.stringify(release)})) return; clearInterval(gate); clearTimeout(watchdog); process.stdout.write(${JSON.stringify("kuna 1.833\n")}); }, 10); `, { mode: 0o700 }); const controller = new AbortController(); const host = createSystemDoctorHost({ HOME: join(work, "home"), PATH: "", JAVA_HOME: join(work, "missing-jdk"), GHIDRA_INSTALL_DIR: join(work, "missing-ghidra"), REA_KUNA_COMMAND: command, REA_KUNA_CACHE_DIR: join(work, "cache"), }, controller.signal); let settled = false; const pending = host.providerInspections().finally(() => { settled = true; }); try { const deadline = performance.now() + 15000; while (!(await access(ready).then(() => true, () => false))) { assert(performance.now() < deadline, "fixture not reached"); await delay(10); } controller.abort("cancel doctor inspection"); await delay(250); console.log(JSON.stringify({ signalAborted: controller.signal.aborted, inspectionSettledBeforeRelease: settled })); assert.equal(settled, false); await writeFile(release, "release"); const kuna = (await pending).find(value => value.id === "kuna"); console.log(JSON.stringify({ kunaAvailableAfterRelease: kuna.available })); assert.equal(kuna.available, true); } finally { await writeFile(release, "release"); await pending; await host.close(); } ``` Execute from the checkout root with a supported Node runtime: ```sh HOME="$WORK/home" node --import ./node_modules/tsx/dist/loader.mjs "$WORK/probe.mjs" "$PWD" "$WORK" ``` The assertions describe the current defective behavior. Output from the executed reduced probe: ```json {"signalAborted":true,"inspectionSettledBeforeRelease":false} {"kunaAvailableAfterRelease":true} ``` ## Expected behavior The doctor cancellation signal should reach Kuna's version probe so cancellation can stop that owned prerequisite and await cleanup. The [lifecycle contract](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/docs/tool-design.md#L101-L107) requires cancellation through awaited observations and cleanup; the [installation inspector already accepts that signal](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/src/kuna/KunaInstallation.ts#L173-L185). ## Impact and scope Doctor continues unnecessary local readiness work after cancellation and waits for the probe to finish or reach its independent deadline. The controlled fixture reproduced the pending inspection and eventual completion after release. The configured version deadline is 10,000 ms; no ten-second latency measurement or indefinite hang is claimed. No data loss or escaped process was observed. SOURCE-ONLY caller reachability: [the CLI forwards its interrupt signal to this factory](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/src/cli/setupCommands.ts#L123-L135), and [doctor diagnostics awaits these provider inspections](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/src/application/DoctorDiagnostics.ts#L28-L42). Actual public CLI timing, real Kuna behavior and Linux runtime coverage are NOT_ENOUGH_INFO; they were not exercised. ## REA version Source revision `3537e58c1ab29309f06eabf50bc52adba9817a0c`, package version 6.7.0. This source checkout was tested; no installed package runtime was tested. ## Runtime and platform Node v24.22.0; macOS 27.0.1; arm64. Authored executable fixture; real Kuna not invoked. ## Target and artifact context Local prerequisite version inspection only. No reverse-engineering target, GUI provider, external service or real user configuration was used. ## Interface CLI ## Earliest failure stage Cleanup, cancellation, or lifecycle ## Relevant output or evidence The JSON above came from the executed production-host probe, which exited 0 after observing the defect. The additional direct-inspector control returned `rejectedOnAbort=true` and `exactReason=true`. [Doctor composition](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/src/doctorRuntime.ts#L52-L66) forwards `inspectionSignal` to Ghidra/JADX but omits it from Kuna. [Kuna doctor inspection](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/src/kuna/KunaDoctor.ts#L35-L47) accepts file-close ownership only and calls the signal-aware installation inspector without a signal. ## Typed outcome or recovery expected Cancellation should reach the in-flight prerequisite, with joined owned cleanup and the caller's cancellation reason preserved. Eventual public CLI output and interrupt exit status are NOT_ENOUGH_INFO; this probe did not measure them. ## Analysis or suspected cause Source-derived cause: the doctor adapter drops the signal before invoking Kuna's installation inspector. The same omission also disconnects its [signal-aware fingerprint work](https://github.com/morluto/rea/blob/3537e58c1ab29309f06eabf50bc52adba9817a0c/src/kuna/KunaInstallation.ts#L212-L224); fingerprint delay was not runtime-tested. ## Additional context Related [#4698](https://github.com/morluto/rea/issues/4698) concerns shared session installation acquisition and session closure. This report exercises the independent doctor caller; correcting the session caller alone would leave this signal disconnected. Compared with stable tag `rea-agents-6.7.0` at `27dc42d10249f0410904a75c0997842eb03b931c`, the relevant Kuna files and doctor call are source-equivalent. The main revision has unrelated Hopper runtime changes. Stable package execution and the first affected release are NOT_ENOUGH_INFO; source equality is not a stable runtime reproduction.
Author
Owner

@XinwangTao commented on GitHub (Oct 19, 2026):

Confirmed — the disconnect is in doctor composition: the CLI forwards its interrupt signal to the host factory, but providerInspections() doesn't thread it down to the Kuna version probe, so Ctrl-C leaves the probe pending while the direct inspectKunaInstallation control aborts correctly. That's a lifecycle-contract inconsistency, not a probe bug.

I'd route the outer signal through every provider inspection in the composition (matching the installation inspector's existing signal support) and add a regression test asserting providerInspections() rejects with the caller's reason when the host signal is aborted.

<!-- gh-comment-id:6260198779 --> @XinwangTao commented on GitHub (Oct 19, 2026): Confirmed — the disconnect is in doctor composition: the CLI forwards its interrupt signal to the host factory, but providerInspections() doesn't thread it down to the Kuna version probe, so Ctrl-C leaves the probe pending while the direct inspectKunaInstallation control aborts correctly. That's a lifecycle-contract inconsistency, not a probe bug. I'd route the outer signal through every provider inspection in the composition (matching the installation inspector's existing signal support) and add a regression test asserting providerInspections() rejects with the caller's reason when the host signal is aborted.
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#3144
No description provided.