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).
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) seescom.google.android.permissioncontrollerforegrounded and throwsCOMMAND_FAILED "The tap likely escaped the app". The harness then never reaches itsalert get/alert acceptsteps — which are designed to consume exactly that prompt (alert-detection already classifies the permission-controller packages as a legitimatepermissionalert 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
alertis 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.tsexportsisAndroidPermissionPackage()so a single authority enumerates the permission packages. The escape guard previously hardcoded onlycom.google.android.permissioncontroller, so the AOSPcom.android.permissioncontroller/ packageinstaller packages weren't detected at all.interaction-android-escape.ts:assertAndroidPressStayedInAppreturns a warning for permission surfaces instead of throwing; the stale "retry the smoke assertion" hint becameUse "alert get" to inspect it, then "alert accept" or "alert dismiss" to respond.The selector-runtime foreground-blocker path (is/getfailing while a dialog blocks) is unchanged.interaction-touch.ts:afterRunmay 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 aWarning:line — previouslydata.warningwas JSON-only, so plain-CLI agents (the live harness included) never saw response warnings at all.Verification
Warning:text line. Typecheck, lint, and the full unit suites over the touched areas (2390 tests) pass.settings permission reset microphone→scroll bottom→click id="automation-request-microphone"→alert get→alert accept): the click that previously threw now returnsTapped … Warning: press … opened an Android permission dialog … Use "alert get" …;alert getreportssource: permissionwith the real buttons (While using the app/Only this time/Don't allow); accept lands anddumpsysconfirmsRECORD_AUDIO: granted=true. The deny leg (alert dismiss→denied) was verified live too.Notes for review