Skip to content

fix(android): report dispatched no for adb host refusals - #3079

Merged
thymikee merged 4 commits into
feat/dispatch-disclosurefrom
fix/adb-host-refusal-dispatch-no
Oct 1, 2026
Merged

thymikee merged 4 commits into
feat/dispatch-disclosurefrom
fix/adb-host-refusal-dispatch-no

Conversation

@thymikee

@thymikee thymikee commented Sep 30, 2026 •

Copy link
Copy Markdown
Member

Summary

An adb failure that adb itself prints before any device-side work now reports dispatched: "no". The classifier marks a host refusal (device_unauthorized, device_offline, multiple_devices, no_devices, device_not_found) only when the whole adb output is the refusal: empty stdout, a first stderr line that is exactly the refusal, and only adb's fixed trailer lines after it. Mixed output, server_version_mismatch, install_*, connection_dropped and timeouts stay unknown. attachAdbFailureHint stamps details.adbHostRefusal. discloseAdbInputDispatch and the one-shot touch helper read that stamp, so there is no string matching at the producers.

Closes #3075. Part of #3069. Stacked on #3071. Touches 6 files.

Validation

Tested commit: df909a1. Not device-facing, so no live evidence.

Every lane passed except vitest-related. There, 5 of 1324 test files failed, each with a 5000ms timeout, and each passed when rerun alone. The passing lanes were format, lint, typecheck, layering, di-seams, fallow, mcp-metadata, build, package, integration-node and macos-coverage. The 5 files are:

  • runner-recovery-wiring
  • runner-session-lifecycle
  • runner-session
  • apple-ci-impact
  • screenshot-density

screenshot-density passed alone, but took 4.95s against the 5s timeout. The same file is untouched by the branch diff against 6165953, so this is a slow test under host load rather than a regression. No code changes or commits were made.

Contention reruns passed alone:

  • packages/platform-apple/src/runner/tests/runner-recovery-wiring.test.ts
  • packages/platform-apple/src/runner/tests/runner-session-lifecycle.test.ts
  • packages/platform-apple/src/runner/tests/runner-session.test.ts
  • scripts/tests/apple-ci-impact.test.ts
  • packages/platform-apple/src/core/tests/screenshot-density.test.ts

Unresolved risk: fill at zero dispatched steps (text-input.ts) is unchanged. The one-shot helper failure now also carries the classifier's adbFailure, hint and retriable.

Review in cubic

@thymikee
thymikee added this pull request to stack #3080 September 30, 2026 18:58

@cubic-dev-ai cubic-dev-ai Bot left a comment •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All reported issues were addressed across 96 files

Reply with feedback, questions, or to request a fix.

Fix all with cubic | Re-trigger cubic

Comment thread contracts/fixtures/dispatch-disclosure.json Outdated
Comment thread packages/platform-android/src/touch-helper.ts
Comment thread packages/platform-android/src/adb-failure.test.ts
@thymikee

Copy link
Copy Markdown
Member Author

[claude-fable-5-1] responding on behalf of @thymikee

All three findings addressed in 1c7f3cd.

  1. Mixed-refusal fixture row (P2) -> 1c7f3cd. The driver now feeds error: device offline\nKilled through the real tap executor and asserts unknown; trigger text updated. Mutation: trailer.every(...) -> true in classifyHostRefusal makes the row report no.
  2. touch-helper parsed-final/nonzero branch (P2) -> 1c7f3cd. The error now goes through attachAdbFailureHint before disclosure (still unknown). New test: nonzero exit with adb: device offline keeps adbFailure, hint, retriable. Mutation: removing attachAdbFailureHint from that branch fails the test (verified).
  3. adb-failure.test hint (P3) -> 1c7f3cd. The expected hint is a hard-coded literal. Mutation: changing the hint text in adb-failure.ts fails the assertion.

@github-actions

github-actions Bot commented Sep 30, 2026 •

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.91 MB 4.91 MB +919 B
Package (unpacked) 4.91 MB 4.91 MB +919 B
Package (download) 1.47 MB 1.47 MB +382 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 21.6 ms 23.1 ms +1.5 ms
CLI --help 63.2 ms 66.8 ms +3.6 ms

@thymikee

Copy link
Copy Markdown
Member Author

This PR is ready at 1c7f3cd. The code looks correct to me, and all 19 checks pass. The PR body reports five Apple/screenshot vitest timeouts under host load. Those files do not overlap the Android adb-failure and touch-helper paths this diff touches. There are no conflicts.

Not blocking, and you can take or leave these: (1) HOST_REFUSAL_SHAPES re-spells the five patterns that ANDROID_ADB_FAILURE_MATCHERS already holds. If someone edits a matcher regex, the anchored shape can fall behind. The result stays unknown, which is safe. Declaring the anchored head pattern on the matcher entry would leave one table. (2) HOST_REFUSAL_STDERR in dispatch-disclosure.test.ts and HOST_REFUSALS in adb-failure.test.ts are near-identical. One exported fixture table would serve both. (3) The PR body could say that server_version_mismatch is excluded because adb retries the command after it restarts the server. The "Unresolved risk" line about fill at zero dispatched steps looks moot, since fillAndroid counts the focus tap before any IME broadcast.

I did not run the tests or the mutation (trailer.every -> true). I relied on green CI and the diff. I did not check against a real unauthorized or offline device, so I cannot confirm that adb emits exactly the asserted strings. Different wording falls to unknown, which is the safe side. I could not prove one race from code. A device that drops after the shell stream opens would have to print a bare error: device offline or device not found with empty stdout to be misclassified. The adb client only prints those at transport selection, before the command is sent. I could not confirm this for remote or cloud ADB providers that relay stderr. I also did not check readers of adbFailure or retriable outside packages/platform-android.

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 30, 2026
@thymikee
thymikee force-pushed the fix/adb-host-refusal-dispatch-no branch from 1c7f3cd to cf00361 Compare October 1, 2026 06:45
@thymikee

thymikee commented Oct 1, 2026

Copy link
Copy Markdown
Member Author

[claude-fable-5-1] responding on behalf of @thymikee

Replayed onto #3071's d7b442d040; the rebase applied cleanly with no conflicts. Added refactor(android): one adb failure table carries the host-refusal shapes: HOST_REFUSAL_SHAPES and ANDROID_ADB_FAILURES_BY_REASON are folded into ANDROID_ADB_FAILURE_MATCHERS, where a row may carry an anchored hostRefusal pattern and the classifier reads it from the row it matched. The existing adb-failure, dispatch-disclosure and adb-provider-scope tests pass unchanged (none imported the removed constants).

@thymikee

thymikee commented Oct 1, 2026

Copy link
Copy Markdown
Member Author

The latest changes at cf00361 look good. The rebase left only one logical change, the table fold in the adb failure classifier, and it matches the earlier review of 1c7f3cd. The existing adb-failure and dispatch-disclosure tests cover that route. I did not run the platform-android tests locally, and I did not run a live adb device. I compared the two table versions side by side. I also did not re-review the base changes from #3071 that came in through the rebase. All 19 checks pass. There are no conflicts. Nothing more is needed from review. Merge can go ahead once #3071 lands.

@thymikee
thymikee force-pushed the fix/adb-host-refusal-dispatch-no branch 2 times, most recently from 7486840 to 5717c00 Compare October 1, 2026 09:15
…for it

device_unauthorized, multiple_devices, no_devices, device_not_found and
server_version_mismatch now carry hostRefusal like device_offline, so
attachAdbFailureHint stamps details.adbHostRefusal for all six. The adb input
producers (discloseAdbInputDispatch) and the one-shot and session helper
gesture producers read that stamp instead of only TOOL_MISSING; a later step
still overrides to unknown through discloseDispatchAfterSteps. The one-shot
helper launch failure now runs through attachAdbFailureHint so the classifier
stays the single source.

Tests and the mutation that makes each fail:
- adb-failure.test.ts "every host-refusal reason ...": drop hostRefusal from
  the no_devices matcher.
- adb-failure.test.ts "reasons a device-side command ...": add hostRefusal to
  connection_dropped or any install_* matcher.
- dispatch-disclosure.test.ts android-adb.input-tap.host-refused.* (six rows,
  real executor via withFakeAdb): make discloseAdbInputDispatch ignore
  adbHostRefusal (6 failures).
- dispatch-disclosure.test.ts android-helper.gesture.host-refused: drop
  attachAdbFailureHint from the one-shot no-parseable-output branch.
- android-adb.input-tap.failed now scripts a non-refusal stderr and stays
  unknown.
… a nonzero exit

- dispatch-disclosure fixture: the mixed-refusal row now feeds `error: device offline\nKilled`
  through the real tap executor and expects unknown. Mutation: ignoring non-trailer lines in
  classifyHostRefusal (trailer.every -> true) makes the row report no.
- touch-helper: the parsed-final/nonzero error goes through attachAdbFailureHint before
  disclosure. Test: a nonzero exit with `adb: device offline` keeps adbFailure, hint, retriable.
  Mutation: dropping attachAdbFailureHint from that branch fails the new test (verified).
- adb-failure.test: the device_offline hint is a literal string instead of a value derived from
  the classifier. Mutation: editing the hint text in adb-failure.ts fails the assertion.
@thymikee
thymikee force-pushed the fix/adb-host-refusal-dispatch-no branch from 5717c00 to a8bb438 Compare October 1, 2026 09:19
@thymikee
thymikee merged commit dc6073d into main Oct 1, 2026
17 of 18 checks passed
@thymikee
thymikee deleted the fix/adb-host-refusal-dispatch-no branch October 1, 2026 09:20
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ready-for-human Valid work that needs human implementation, judgment, or maintainer merge

Projects

None yet

Development

Successfully merging this pull request may close these issues.

adb: mark every host refusal as hostRefusal and derive dispatched: "no" from it

1 participant