Skip to content

fix: treat Android permission prompts as pending alerts, not tap escapes - #1530

Merged
thymikee merged 3 commits into
mainfrom
claude/recursing-wescoff-1c495e
Jul 31, 2026
Merged

fix: treat Android permission prompts as pending alerts, not tap escapes#1530
thymikee merged 3 commits into
mainfrom
claude/recursing-wescoff-1c495e

Conversation

@thymikee

Copy link
Copy Markdown
Member

Problem

The Android full-tier live scenario full:lifecycle-system (resetAndRequestMicrophonePermission) fails deterministically: click id="automation-request-microphone" intentionally raises the system microphone permission dialog, but the post-press escape guard (assertAndroidPressStayedInApp) sees com.google.android.permissioncontroller foregrounded and throws COMMAND_FAILED "The tap likely escaped the app". The harness then never reaches its alert get / alert accept steps — which are designed to consume exactly that prompt (alert-detection already classifies the permission-controller packages as a legitimate permission alert source).

Reproduced 3/3 locally on a Pixel 9 Pro XL API 36 emulator. The full tier has never executed in CI (both nightlies since #1482/#1484 died on Android infra before the suite ran), so this is the first exercise of that path.

Product decision

A press that foregrounds an Android permission-prompt package is a successful press — raising the dialog is the intended outcome, and alert is the designed consumer. The press now succeeds and carries a response warning telling the agent what to do next. Genuine escapes (settings, systemui, launcher) still throw exactly as before.

Changes

  • alert-detection.ts exports isAndroidPermissionPackage() so a single authority enumerates the permission packages. The escape guard previously hardcoded only com.google.android.permissioncontroller, so the AOSP com.android.permissioncontroller / packageinstaller packages weren't detected at all.
  • interaction-android-escape.ts: assertAndroidPressStayedInApp returns a warning for permission surfaces instead of throwing; the stale "retry the smoke assertion" hint became Use "alert get" to inspect it, then "alert accept" or "alert dismiss" to respond. The selector-runtime foreground-blocker path (is/get failing while a dialog blocks) is unchanged.
  • interaction-touch.ts: afterRun may return a warning, appended to the successful response alongside the existing dialog-recovery readiness warning (append, never clobber).
  • output.ts (interaction formatters): tap/press/fill/longpress plain-text output now prints a Warning: line — previously data.warning was JSON-only, so plain-CLI agents (the live harness included) never saw response warnings at all.

Verification

  • New unit tests: press succeeds with the pending-alert warning for both permission-controller packages and the action is recorded; existing launcher/settings escape tests still throw; a formatter test locks the Warning: text line. Typecheck, lint, and the full unit suites over the touched areas (2390 tests) pass.
  • Live on the Pixel 9 Pro XL emulator, exact harness sequence (settings permission reset microphonescroll bottomclick id="automation-request-microphone"alert getalert accept): the click that previously threw now returns Tapped … Warning: press … opened an Android permission dialog … Use "alert get" …; alert get reports source: permission with the real buttons (While using the app / Only this time / Don't allow); accept lands and dumpsys confirms RECORD_AUDIO: granted=true. The deny leg (alert dismissdenied) was verified live too.

Notes for review

A click that raises a system permission dialog (e.g. the lifecycle
scenario's automation-request-microphone) previously failed the
assertAndroidPressStayedInApp escape guard with COMMAND_FAILED, so the
harness never reached its alert get / alert accept steps. But raising
the dialog is the intended press outcome, and alert-detection already
treats the permission-controller packages as a legitimate alert source.

The guard now returns a response warning for permission-prompt packages
(shared isAndroidPermissionPackage authority, which also covers the AOSP
permissioncontroller/packageinstaller packages the guard previously
missed) and keeps throwing for genuine escapes (settings, systemui,
launcher). The warning tells the agent to consume the dialog with
alert get / alert accept / alert dismiss, and tap/press/fill/longpress
plain-text CLI output now prints response warnings so plain-CLI agents
see it too (previously data.warning was JSON-only).
@github-actions

github-actions Bot commented Jul 31, 2026

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
JS raw 1.91 MB 1.91 MB -156 B
JS gzip 613.4 kB 613.4 kB -39 B
npm tarball 731.5 kB 731.4 kB -45 B
npm unpacked 2.57 MB 2.57 MB -156 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 31.1 ms 31.3 ms +0.2 ms
CLI --help 68.5 ms 68.5 ms -0.1 ms

Top changed chunks:

Chunk Raw diff Gzip diff
dist/src/interaction.js +56 B +36 B
dist/src/selector-runtime.js +74 B +30 B
dist/src/ime-lifecycle.js +46 B +22 B
dist/src/registry.js +52 B +15 B
dist/src/internal/daemon.js 0 B +2 B

@thymikee

thymikee commented Jul 31, 2026

Copy link
Copy Markdown
Member Author

Reviewed exact head e0a458e2a1ec02b8c46514a7f03710a87b098ec1. The production route is coherent and the live Pixel evidence is credible, but one regression-coverage gap remains:

P2 — cover the two package-installer variants that this PR newly classifies as non-escapes. ANDROID_PERMISSION_PACKAGES contains four packages, while the new end-to-end guard test exercises only com.google.android.permissioncontroller and com.android.permissioncontroller. Deleting either com.google.android.packageinstaller or com.android.packageinstaller would leave the suite green even though the PR explicitly claims those older/AOSP surfaces now succeed with the pending-alert warning. Add both variants to the table and retain the successful response, warning, and action-recording assertions.

All exact-head CI is green; no readiness label until the claimed widening is pinned.

@thymikee

Copy link
Copy Markdown
Member Author

Addressed in 9846b16. The end-to-end pending-alert warning case now table-covers all four package owners accepted by production: both permission-controller variants and both package-installer variants. The final diff is intentionally limited to the test table.

Validation: pnpm check:affected --base origin/main --run passed (including 2,103 related tests, 5,214 coverage tests, and 141 provider-integration tests).

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Jul 31, 2026
@thymikee
thymikee merged commit 9fea8ff into main Jul 31, 2026
30 checks passed
@thymikee
thymikee deleted the claude/recursing-wescoff-1c495e branch July 31, 2026 19:22
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-07-31 19:22 UTC

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.

1 participant