[GH-ISSUE #6045] [Bug] ADB install and uninstall invent a target refusal when no verdict was returned #3691

Closed
opened 2026-10-31 00:43:07 +00:00 by bsakaguchi · 1 comment
Owner

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

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

The built CLI reports unsupported_target when the selected ADB executable exits zero without any package-change verdict. Empty stdout becomes details.reason: "the device refused the installation" or "the device refused the removal". Those words were not returned by the executable. Progress-only stdout also becomes a target-refusal reason. A nonzero exit with the same empty stdout instead returns unreadable_output. The command result establishes neither successful mutation nor a device refusal.

Reproduction

Executed both CLI commands with a small executable ADB substitute, no real device or ADB server. The following reduced case uses the same version response and zero-exit empty response:

work_dir="$(mktemp -d)"
cat > "$work_dir/adb" <<'JS'
#!/usr/bin/env node
const args = process.argv.slice(2);
if (args[0] === 'version') {
  console.log('Android Debug Bridge version 1.0.41\nVersion 34.0.5-stub');
}
process.exit(0);
JS
chmod 755 "$work_dir/adb"
printf 'apk fixture' > "$work_dir/sample.apk"
REA_ADB_PATH="$work_dir/adb" node scripts/rea.mjs install-adb-package emulator-5554 "$work_dir/sample.apk" --format json
REA_ADB_PATH="$work_dir/adb" node scripts/rea.mjs uninstall-adb-package emulator-5554 com.example --format json

Both exited 1 with the fabricated refusal reasons above and empty CLI stderr. Also executed three controls for each command: zero-exit progress-only stdout (Performing Streamed Install) becomes unsupported_target with that progress text as its reason; nonzero empty stdout becomes unreadable_output; explicit Failure [TEST_REFUSAL] becomes unsupported_target with TEST_REFUSAL. Each of these eight command/case combinations was executed once.

Expected behavior

Represent an absent or unrecognized verdict as unreadable/unknown command output. Reserve a package-change target refusal for an observed refusal, and preserve its actual reason. The ADB guide promises Success/Failure verdict preservation and verbatim device Failure reasons. The existing nonzero/no-verdict regression already requires unreadable_output for the analogous failed invocation.

Impact and scope

P2: wrong machine-readable diagnosis and recovery guidance can make a caller treat an unknown mutation outcome as an unsupported target or confirmed device refusal. Directly checking package state and raw ADB output is a practical diagnostic workaround before deciding whether to retry a mutation. This affects install/uninstall responses without a recognized verdict; natural frequency is unknown. No actual device mutation, failed installation, or data loss was demonstrated.

REA version

rea-agents 6.7.0; source revision 5241597594.

Runtime and platform

Node.js v26.12.0; macOS 27.0.1 arm64; locked source dependencies and the supplied built CLI.

Target and artifact context

An inert ADB executable substitute; a small APK-named byte fixture used only for host snapshot admission; simulated serial emulator-5554 and package com.example. No Android device was accessed.

Interface

CLI

Earliest failure stage

Transport or result projection

Relevant output or evidence

Selected fields from actual CLI stdout:

{"command":"install-adb-package","code":"unsupported_target","retryable":false,"details":{"operation":"install_adb_package","path":"emulator-5554","reason":"the device refused the installation"}}
{"command":"uninstall-adb-package","code":"unsupported_target","retryable":false,"details":{"operation":"uninstall_adb_package","path":"emulator-5554","reason":"the device refused the removal"}}

command labels identify the two separate outputs. Both include remediation telling the caller to select a supported target. The substitute printed no verdict or refusal text.

Typed outcome or recovery expected

An output/unknown-verdict failure such as unreadable_output, stating that no recognized Success/Failure verdict was observed. Preserve uncertainty about whether the mutation occurred. Do not synthesize a device claim.

Analysis or suspected cause

parsePackageChangeOutput treats all non-Success stdout as failure; empty output yields a null reason. Install and uninstall then turn that result into AnalysisUnsupportedTargetError, filling a null reason with a made-up refusal. This source path explains the executed outputs. A distinct unknown-verdict state at the parser boundary would keep absence separate from refusal.

Additional context

Read merged #2344: it fixes explicit refusal verdicts on nonzero exit and explicitly leaves zero-exit behavior unchanged. #3166, #4252 and #4673 concern replacement attribution, consumed bytes and user scope respectively. #5971, #5978 and #5981 concern different Android/ADB admission paths. Open #5001 changes activity and permission parsing. None repairs this missing-verdict path. Fetched upstream main 6d356b8434; these parser/call-site lines remain unchanged. MCP registration reaches the same provider, but MCP transport was not executed.

Originally created by @coygeek on GitHub (Oct 21, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/6045 ### 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 The built CLI reports `unsupported_target` when the selected ADB executable exits zero without any package-change verdict. Empty stdout becomes `details.reason: "the device refused the installation"` or `"the device refused the removal"`. Those words were not returned by the executable. Progress-only stdout also becomes a target-refusal reason. A nonzero exit with the same empty stdout instead returns `unreadable_output`. The command result establishes neither successful mutation nor a device refusal. ### Reproduction Executed both CLI commands with a small executable ADB substitute, no real device or ADB server. The following reduced case uses the same version response and zero-exit empty response: ```sh work_dir="$(mktemp -d)" cat > "$work_dir/adb" <<'JS' #!/usr/bin/env node const args = process.argv.slice(2); if (args[0] === 'version') { console.log('Android Debug Bridge version 1.0.41\nVersion 34.0.5-stub'); } process.exit(0); JS chmod 755 "$work_dir/adb" printf 'apk fixture' > "$work_dir/sample.apk" REA_ADB_PATH="$work_dir/adb" node scripts/rea.mjs install-adb-package emulator-5554 "$work_dir/sample.apk" --format json REA_ADB_PATH="$work_dir/adb" node scripts/rea.mjs uninstall-adb-package emulator-5554 com.example --format json ``` Both exited 1 with the fabricated refusal reasons above and empty CLI stderr. Also executed three controls for each command: zero-exit progress-only stdout (`Performing Streamed Install`) becomes `unsupported_target` with that progress text as its reason; nonzero empty stdout becomes `unreadable_output`; explicit `Failure [TEST_REFUSAL]` becomes `unsupported_target` with `TEST_REFUSAL`. Each of these eight command/case combinations was executed once. ### Expected behavior Represent an absent or unrecognized verdict as unreadable/unknown command output. Reserve a package-change target refusal for an observed refusal, and preserve its actual reason. [The ADB guide promises Success/Failure verdict preservation](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/adb-device-analysis.md#L100-L101) and [verbatim device Failure reasons](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/docs/adb-device-analysis.md#L167-L175). [The existing nonzero/no-verdict regression](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/adb/AdbProvider.test.ts#L1767-L1780) already requires `unreadable_output` for the analogous failed invocation. ### Impact and scope P2: wrong machine-readable diagnosis and recovery guidance can make a caller treat an unknown mutation outcome as an unsupported target or confirmed device refusal. Directly checking package state and raw ADB output is a practical diagnostic workaround before deciding whether to retry a mutation. This affects install/uninstall responses without a recognized verdict; natural frequency is unknown. No actual device mutation, failed installation, or data loss was demonstrated. ### REA version rea-agents 6.7.0; source revision 52415975942a4b9c53a817915b313ccc404f280e. ### Runtime and platform Node.js v26.12.0; macOS 27.0.1 arm64; locked source dependencies and the supplied built CLI. ### Target and artifact context An inert ADB executable substitute; a small APK-named byte fixture used only for host snapshot admission; simulated serial emulator-5554 and package com.example. No Android device was accessed. ### Interface CLI ### Earliest failure stage Transport or result projection ### Relevant output or evidence Selected fields from actual CLI stdout: ```json {"command":"install-adb-package","code":"unsupported_target","retryable":false,"details":{"operation":"install_adb_package","path":"emulator-5554","reason":"the device refused the installation"}} {"command":"uninstall-adb-package","code":"unsupported_target","retryable":false,"details":{"operation":"uninstall_adb_package","path":"emulator-5554","reason":"the device refused the removal"}} ``` `command` labels identify the two separate outputs. Both include remediation telling the caller to select a supported target. The substitute printed no verdict or refusal text. ### Typed outcome or recovery expected An output/unknown-verdict failure such as `unreadable_output`, stating that no recognized Success/Failure verdict was observed. Preserve uncertainty about whether the mutation occurred. Do not synthesize a device claim. ### Analysis or suspected cause [parsePackageChangeOutput](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/adb/AdbShellOutput.ts#L388-L404) treats all non-Success stdout as failure; empty output yields a null reason. [Install](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/adb/AdbProvider.ts#L2594-L2607) and [uninstall](https://github.com/morluto/rea/blob/52415975942a4b9c53a817915b313ccc404f280e/src/adb/AdbProvider.ts#L2658-L2671) then turn that result into `AnalysisUnsupportedTargetError`, filling a null reason with a made-up refusal. This source path explains the executed outputs. A distinct unknown-verdict state at the parser boundary would keep absence separate from refusal. ### Additional context Read merged #2344: it fixes explicit refusal verdicts on nonzero exit and explicitly leaves zero-exit behavior unchanged. #3166, #4252 and #4673 concern replacement attribution, consumed bytes and user scope respectively. #5971, #5978 and #5981 concern different Android/ADB admission paths. Open #5001 changes activity and permission parsing. None repairs this missing-verdict path. Fetched upstream main 6d356b84344659085f2b912857b5e27fe0160c9b; these parser/call-site lines remain unchanged. MCP registration reaches the same provider, but MCP transport was not executed.
Author
Owner

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

Checked the reported fix on main at d87e73f2db.

Empty and progress-only outputs are explicitly unknown, and both successful-process install and uninstall branches now turn unknown into AnalysisOutputError retaining stdout/stderr, before considering an actual Failure verdict. A production-source probe confirms the three no-verdict parser inputs. Existing parser regression covers them; provider tests retain real Failure reasons and a nonzero/no-verdict control.

Implementation: AdbProvider.ts, AdbProvider.ts.

The relevant regression checks in AdbShellOutput.test.ts, AdbProvider.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:6380728239 --> @coygeek commented on GitHub (Oct 24, 2026): Checked the reported fix on `main` at d87e73f2db50be72d33a4b3c7199af35518b441c. Empty and progress-only outputs are explicitly unknown, and both successful-process install and uninstall branches now turn unknown into AnalysisOutputError retaining stdout/stderr, before considering an actual Failure verdict. A production-source probe confirms the three no-verdict parser inputs. Existing parser regression covers them; provider tests retain real Failure reasons and a nonzero/no-verdict control. Implementation: [AdbProvider.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/adb/AdbProvider.ts#L2715), [AdbProvider.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/adb/AdbProvider.ts#L2793). The relevant regression checks in [AdbShellOutput.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/adb/AdbShellOutput.test.ts), [AdbProvider.test.ts](https://github.com/morluto/rea/blob/d87e73f2db50be72d33a4b3c7199af35518b441c/src/adb/AdbProvider.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#3691
No description provided.