Skip to content

refactor(platform-android): classify pm refusals on typed reasons - #3010

Merged
thymikee merged 1 commit into
mainfrom
refactor/settings-permission-typed-skip-2700
Sep 28, 2026
Merged

thymikee merged 1 commit into
mainfrom
refactor/settings-permission-typed-skip-2700

Conversation

@thymikee

@thymikee thymikee commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Summary

settings permission all decided skip-vs-abort by lowercase-substring matching pm stderr in two independent places, and the photos path re-ran the same sniff over another error's recorded attempts. This refactors both call sites onto a single typed boundary: classifyAndroidPmSkip reads pm stderr once and returns an AndroidPmSkipReason (not-changeable, not-requested, not-runtime-permission, unknown-permission, role-managed); tryPmUnit and setAndroidPhotoPermission both call it, and the photos skip check now reads the typed attempts it already recorded instead of re-matching stderr. The repeated applied/warnings bookkeeping across unit kinds is folded into recordAppliedPermission/finishAllUnit. No behavior change: the same five refusal patterns are still classified, and anything else still aborts the fan-out.

Touched files: 2 (packages/platform-android/src/settings-permission.ts, its test file).

Closes #2700

Validation

Tested commit: f78cc55332216634ee165f4292fbf66e65a12ac6

  • pnpm format: no changes
  • pnpm check:affected --run: all runnable checks passed — 364 test files, 2380 tests passed; unit and provider-integration suites deduped as covered by related tests/CI

Live device check (private emulator Pixel_9_Pro_XL_API_37, serial emulator-5584, never touching the reserved emulator-5554), against the pre-installed org.telegram.messenger.web:

node bin/agent-device.mjs open org.telegram.messenger.web --platform android --serial emulator-5584 --foreground
node bin/agent-device.mjs settings permission grant all --json
node bin/agent-device.mjs settings permission deny all --json

grant all applied 23 permissions and produced 52 skip warnings, exercising both the not-changeable reason (e.g. FOREGROUND_SERVICE_LOCATION ... is not a changeable permission type) and the unknown-permission reason (e.g. Unknown permission android.permission.READ_CLIPBOARD) through classifyAndroidPmSkip. deny all applied 23 and produced 75 warnings mixing skip warnings with the pre-existing "was granted before this revoke" relaunch warnings, confirming recordAppliedPermission/finishAllUnit still fold both warning kinds correctly. Emulator was booted and killed for this check only; adb devices afterward showed only emulator-5554 remaining.

No unresolved risk: refusal-reason classification and applied/warning accounting are exercised end to end on-device, matching the added unit coverage for classifyAndroidPmSkip and the photos skip path.

Review in cubic

)

`settings permission all` decided skip-vs-abort by matching lowercase
substrings of pm stderr, and the photos path re-ran the same sniff over
another error's details.attempts. Classify once at the pm boundary into
an AndroidPmSkipReason instead: tryPmUnit and setAndroidPhotoPermission
both call classifyAndroidPmSkip, and the photos skip check reads the
typed attempts it already recorded rather than re-matching stderr.
Fold the repeated applied/warnings accounting across unit kinds into
recordAppliedPermission/finishAllUnit.
@github-actions

Copy link
Copy Markdown

Size Report

Metric Base Current Diff
Installed (including dependencies) 4.87 MB 4.87 MB +188 B
Package (unpacked) 4.87 MB 4.87 MB +188 B
Package (download) 1.46 MB 1.46 MB +87 B

Startup median (7 runs, lower is better):

Scenario Base Current Diff
CLI --version 22.4 ms 22.4 ms +0.0 ms
CLI --help 65.3 ms 65.2 ms -0.1 ms

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

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

1 issue found across 2 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/platform-android/src/settings-permission.ts">

<violation number="1" location="packages/platform-android/src/settings-permission.ts:405">
P3: `isAndroidPhotosSkipAttempts` makes the whole skip decision on `attempt.reason !== undefined`, but `isAndroidPmSkipAttemptList` validates `permission`/`detail` as strings and never checks `reason`'s type. A truthy non-string `reason` (for example `false`, or a future shape change) would pass the guard and silently collapse an operational photos probe failure into the benign skip warning instead of aborting the fan-out — the exact misclassification this typed boundary is meant to prevent. Validate `reason` in the guard: `typeof reason === 'string' || reason === undefined` (or membership-check against `AndroidPmSkipReason`).</violation>
</file>

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

Fix all with cubic | Re-trigger cubic

return (
isAndroidPmSkipAttemptList(attempts) &&
attempts.length > 0 &&
attempts.every((attempt) => attempt.reason !== undefined)

@cubic-dev-ai cubic-dev-ai Bot Sep 28, 2026 •

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P3: isAndroidPhotosSkipAttempts makes the whole skip decision on attempt.reason !== undefined, but isAndroidPmSkipAttemptList validates permission/detail as strings and never checks reason's type. A truthy non-string reason (for example false, or a future shape change) would pass the guard and silently collapse an operational photos probe failure into the benign skip warning instead of aborting the fan-out — the exact misclassification this typed boundary is meant to prevent. Validate reason in the guard: typeof reason === 'string' || reason === undefined (or membership-check against AndroidPmSkipReason).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/platform-android/src/settings-permission.ts, line 405:

<comment>`isAndroidPhotosSkipAttempts` makes the whole skip decision on `attempt.reason !== undefined`, but `isAndroidPmSkipAttemptList` validates `permission`/`detail` as strings and never checks `reason`'s type. A truthy non-string `reason` (for example `false`, or a future shape change) would pass the guard and silently collapse an operational photos probe failure into the benign skip warning instead of aborting the fan-out — the exact misclassification this typed boundary is meant to prevent. Validate `reason` in the guard: `typeof reason === 'string' || reason === undefined` (or membership-check against `AndroidPmSkipReason`).</comment>

<file context>
@@ -355,20 +386,36 @@ async function tryPhotosUnit(
+  return (
+    isAndroidPmSkipAttemptList(attempts) &&
+    attempts.length > 0 &&
+    attempts.every((attempt) => attempt.reason !== undefined)
+  );
+}
</file context>
Fix with cubic

@thymikee

Copy link
Copy Markdown
Member Author

Reviewed at f78cc55. The pm refusals are now classified on typed reasons, and every refusal class keeps its old outcome. I found no blocking problem.

One question: applyAllNotificationsUnit now goes through recordAppliedPermission, which skips warnIfRevoked on grant. So permission grant --target all no longer prints the "could not be read" warning for POST_NOTIFICATIONS. This looks like a fix. Could you add a test for it and mention it in the PR body?

@thymikee thymikee added the ready-for-human Valid work that needs human implementation, judgment, or maintainer merge label Sep 28, 2026
@thymikee
thymikee merged commit a212d9a into main Sep 28, 2026
18 checks passed
@thymikee
thymikee deleted the refactor/settings-permission-typed-skip-2700 branch September 28, 2026 10:43
@github-actions

Copy link
Copy Markdown
PR Preview Action v1.8.1
Preview removed because the pull request was closed.
2026-09-28 10:48 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.

Android settings permission all: classify pm refusals on typed reasons, not stderr substrings

1 participant