8.0 KiB
Mobile web QA — 2026-11-02
This change is limited to the web client and its tests/documentation, with no backend contract or native app release changes. This report records the focused mobile QA and remaining device verification limits.
Reproduction and fixes
The production bundle was exercised with local API/socket fixtures in touch-enabled Chromium at 390×844. Before/after measurements use identical messages and viewport dimensions.
| Area | Before | After |
|---|---|---|
| Installed display | Manifest preferred fullscreen, including display_override |
standalone, no fullscreen override; normal Apple status-bar style |
| Horizontal table pan | touch-action: pan-y on the workspace; table stayed at scrollLeft=0, swipe created a new chat |
Native table scrolled to scrollLeft=160, current chat retained |
| Regular assistant width | 304.30px of a 358px transcript at 390px viewport | 358px; desktop width rule retained |
| Agent message width | 287.52px | 287.52px; bubble/avatar styling retained |
| Compact model | Hidden | Provider and active model beneath title; updates after changing model in settings |
| Context menu | Assistant context-menu event canceled for a Fork-only menu | Native event allowed for text, links and composer; explicit message-actions button opens Fork |
| Zoom | Viewport disabled user scaling | Fixed-scale viewport retained (maximum-scale=1.0, user-scalable=no) |
| Notification failures | Browser exception names and server statuses replaced by a generic error; background refresh erased errors | Stage-specific, redacted recovery guidance, retained until explicit retry/refresh |
Text owns gestures across regular chat, search markdown and Agent. Dynamic horizontal scrollers and code/table/quote elements are protected even at both ends. Selections, multi-touch, long-press starts and touch cancellation abandon navigation tracking. Blank workspace chrome still supports sidebar/new-chat swipes. Notification help is bounded and scrollable; sidebar content remains reachable on shorter screens.
Notification diagnosis and official sources
A read-only authenticated request to the running server's GET /v1/agent/push/config returned HTTP 200 with enabled=true, webPushAvailable=true, a known source, quarantined=false, and a public key present. Credentials/key values were not printed. This rules out disabled transport configuration at the time of inspection; it does not prove delivery or identify the actual phone's failed step.
Chromium's Android GCM driver uses GoogleCloudMessagingV2, which checks for com.google.android.gms and rejects registration when Google Play services is absent. Vanadium is GrapheneOS's Chromium browser, as documented in the official browsing guide. Missing/unreachable Play services is therefore a plausible cause on GrapheneOS; the old generic message cannot establish that cause.
The official sandboxed Google Play guide requires dependent apps and Play in the same profile and recommends a battery optimization exception for background push. Check network access, Sybil's site notification permission and Vanadium's Android notification permission, then reload and explicitly enable again. Users choosing not to install Play need a browser with an independent push service. Sybil cannot replace that browser service or inspect installed Android packages, so it does not disable Vanadium based on user-agent detection.
Permission and subscription are separate: PushManager.subscribe() can fail after permission was granted. Diagnostics identify the failed layer and known exception name without echoing arbitrary browser/provider messages. No permissions are requested in the background; opt-in still requests permission synchronously from the user's click. Registration failures retain rollback and revocation behavior, including storage failure after browser subscription.
standalone can retain the device status bar, unlike fullscreen (manifest display reference). Existing installations may retain older manifest metadata until their browser updates it. Chrome documents manifest update scheduling; Vanadium installation/update behavior still needs device verification. The manifest URL, app ID, start URL and service-worker scope remain stable.
Automated verification
npm run buildandnpm testinweb.mobile-workspace.browser.mjs: trusted CDP touch pans on tables, nested quotation code and code blocks; both scroll edges; selection/clipboard copy and text paste; uncanceled context-menu events; explicit Fork menu; synthetic multi-touch/long-press/cancel scenarios; both navigation directions; model change; widths 320, 390, 430 and 767; 1280px desktop check; 450px-height composer checks; unchanged Agent width; manifest and zoom settings. Screenshots were inspected.agent-push-errors.browser.mjs: mobile permission dismissal/denial, worker failure,AbortError,NotAllowedError,NotSupportedError,InvalidStateError, subscription timeout, HTTP 400/401/403/409/429/503, rollback, preserved error after background refresh, successful retry, missing APIs/insecure context, iOS Home Screen guidance, and reachable notification controls at 320×568 and 390×450. API/push-provider outcomes are mocked; worker registration is real except the deliberate failure case.- Existing
agent-push.browser.mjs: real worker activation/notification display/IndexedDB and read-close, explicit consent, source changes, multiple sessions, credential rotation, revocation and offline logout with mocked push provider/backend. - Existing
pwa.browser.mjs: production service-worker activation/reload and authentication flows.
Run browser checks after building, using an existing Playwright/Chromium installation. These fixtures intentionally reject non-loopback targets and mock backend/provider traffic.
cd web
npm run build
npm test
npm run preview -- --host 127.0.0.1 --port 5197 --strictPort
In another shell, with PLAYWRIGHT_MODULE=/absolute/path/to/playwright-core/index.mjs and CHROME_EXECUTABLE=/absolute/path/to/chrome exported:
cd web
node tests/mobile-workspace.browser.mjs
node tests/agent-push-errors.browser.mjs
node tests/agent-push.browser.mjs
node tests/pwa.browser.mjs
MOBILE_ARTIFACT_DIR saves layout screenshots. PUSH_ERROR_SCREENSHOT saves notification settings. MOBILE_BASELINE=1 records the original layout/gesture failures against a separately served original build.
Device verification limits
No physical GrapheneOS/Vanadium, Android Chrome, or iPhone/Safari device was available. Desktop Chromium touch emulation cannot verify Android system status-bar visibility, native long-press menu rendering/selection handles, actual pinch/keyboard behavior, installed-PWA metadata updates or background FCM delivery. Clipboard tests use the browser engine's keyboard copy/paste and synthetic context-menu dispatch; cancellation/selection assertions are not a claim that Android's system menu was displayed. A shorter viewport approximates keyboard space, without emulating the keyboard itself.
On the user's device after an authorized deployment: check the installed status bar and fixed-scale viewport, pan both ways through wide tables/quotes/code without navigation, long-press/copy/paste, change model, then explicitly enable notifications and inspect the new error if registration fails. Verify actual background delivery with all Sybil web sessions inactive. The underlying phone-specific notification failure remains unconfirmed until that check.