Repository navigation
feat(mobile): draw the host Accounts screen from the desktop's page - #30020
Conversation
…prop; unknown routes say unavailable
…uild switch The owner retired the native-screen fallback with no emergency native build, so EXPO_PUBLIC_MOBILE_SHELL, mobileShellBuildKind, shellSwitchDecision, the Metro cache-key suffix and both release workflows' shell input go. Every host route mounts the shell; a route naming no screen the shell could open gets the existing refusal screen instead of a native screen. Claude-Session: https://claude.ai/code/session_6cea92f6
With every build the OTA shell, these native siblings run nowhere: the native-executed closure (index.js plus every non-web route) does not reach them, and the page resolves their .web siblings. Each .web sibling takes the plain name, its web-overrides entry goes, and the native-only rich Markdown editor and Mermaid WebView generators and their outputs go with them. Kept: native siblings the node test suite still renders shared screens through (terminal WebView, browser frame path, clipboard, dictation, media picker, live input, snapshot budget, input styles). Claude-Session: https://claude.ai/code/session_6cea92f6
…bling held Claude-Session: https://claude.ai/code/session_6cea92f6
…one-value shell kind The host route files no longer switch, so their comments say what they do now: a route naming no screen shows the unavailable screen. app/h/[hostId]/web.tsx duplicated index.tsx and nothing linked to it, so it and its stack entry go (the bundle sweep is re-measured: 77 scripts at 15 routes). Diagnostics print the bridge protocol on its own instead of a shell kind that has one value. Claude-Session: https://claude.ai/code/session_6cea92f6
…oo/ota-native-screens-delete # Conflicts: # mobile/web-entry/web-overrides.json
…two unused native deps MobileSelectableText was react-native's Text, so callers use Text. The rich Markdown editor's host-facing seams (messages, URL prompt, keyboard inset) are required now that the page is its only host; the WebView defaults go. Comments that described deleted native components are rewritten, and expo-image-manipulator and react-native-uitextview leave package.json and the lockfile. Claude-Session: https://claude.ai/code/session_6cea92f6
Accounts was the last host screen the shell still drew natively, so its changes needed a phone build. It is now a page route like Tasks: the screen moves to src/accounts/MobileAccountsScreen.tsx, accounts.web.tsx renders it on the page, and accounts.tsx mounts the shell for it. What React Native Web needed: Alert.alert is a no-op there, so the reset confirmation, its outcome and a failed account switch are ConfirmModal drawers (ConfirmModal can now drop Cancel for a notice); Back goes through the route handoff, which pops the shell's stack; and the reset idempotency key is built from getRandomBytes, since randomUUID needs a secure context the iOS custom scheme is not. edit.tsx edits the phone's own pairing and never renders on the page, so the route manifest leaves it out and the page's host stack does not declare it. The script sweep is re-measured for the 14-route tree. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…d outcome; mobile mirrors the desktop The desktop keeps no client journal for a reset credit: the host's ledger owns the attempt. A remote client that lost a reply and retried the same offer under a new key was refused with "already attempted" (or "unknown outcome" while the provider call was unresolved), which is why the phone carried its own durable journal to replay the old key. The coordinator now answers a request for an offer an earlier key already claimed as that attempt: the settled outcome, the running attempt, or a resume of the pending one under its original key. A different offer for the account is still refused while an outcome is unknown, so nothing can spend twice. The reply shape is the one a same-key replay always had, so installed clients with their journal read it unchanged. With that, the Accounts page sends a fresh key per attempt like the desktop and keeps nothing on the phone: the AsyncStorage journal, its client singleflight and the retry-record warnings are deleted, and the journal-resume recording goes with them. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…try names a newer offer Review follow-ups: - Host: after an unknown provider outcome the usage refresh moves the offer, so a retry names a newer one. The coordinator now answers it as the account's unresolved attempt (resumed under its own key and scope), as the desktop's consumeCurrent does, instead of refusing forever. One ledger lookup finds own key, offer claimant, then unresolved attempt. - Mobile: a reply is matched on target, account and account revision, not the offer, so the resumed attempt's scope is accepted. - The key is minted inside requestCodexResetCredit from randomV4Uuid, the session helper moved to src/platform/random-v4-uuid.ts. - RefreshControl goes; the header refresh stays. ConfirmModal's onConfirm is optional for an OK-only notice. - Stale journal comments and the mobile docs line are corrected. - The edit.tsx page exclusion is dropped from this PR; the script sweep is re-measured for 15 routes. - Root oxlint: escape the apostrophe; zod v4 strictObject. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…outing - randomV4Uuid never falls back to Math.random: without crypto.randomUUID it builds the v4 UUID from ExpoCrypto.getRandomBytes, as the old reset key did. Tests get Node's secure bytes for expo-crypto from the setup file, since the native module has no Node build. - Host tests: a new offer after a settled attempt spends again rather than replaying, and a retry naming a newer offer joins a resumed attempt in flight (one provider call, original key). Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…s-on-page # Conflicts: # config/scripts/build-mobile-web-app-bundle.test.mjs # config/scripts/verify-mobile-web-app-bundle.mjs # mobile/src/session/MobileNativeChatMessage.tsx # mobile/web-entry/web-overrides.json
…ouched The merge commit's lint-staged run reformatted 27 files this PR does not change. Committed without the hook so they match origin/main exactly. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
|
Warning Review limit reachedYou've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository. Next included review available in 6 minutes. View limit details
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to
|
There was a problem hiding this comment.
Actionable comments posted: 3
ℹ️ Review info
⚙️ Run configuration
- Configuration used: Repository UI
- Review profile: CHILL
- Plan: Advanced
- Run ID:
d06e0d97-c968-4b89-acd1-b9f8e5f65087
⛔ Files ignored due to path filters: 1
mobile/pnpm-lock.yamlis excluded by!**/pnpm-lock.yaml
📒 Files selected for processing: 216
.github/workflows/mobile-android-release.yml.github/workflows/mobile-ios-release.ymlconfig/scripts/build-mobile-web-app-bundle.mjsconfig/scripts/build-mobile-web-app-bundle.test.mjsconfig/scripts/mobile-web-app-clipboard-image-resize-render.test.mjsconfig/scripts/mobile-web-app-external-navigation-grant.test.mjsconfig/scripts/mobile-web-app-mermaid-render.test.mjsconfig/scripts/mobile-web-app-page-closure-families.mjsconfig/scripts/mobile-web-app-page-route-modules.mjsconfig/scripts/mobile-web-app-push-subsystem-closure.test.mjsconfig/scripts/mobile-web-app-session-dictation-capture.test.mjsconfig/scripts/mobile-web-app-session-media-picker.test.mjsconfig/scripts/mobile-web-app-session-webview-consumers.test.mjsconfig/scripts/mobile-web-page-routes.mjsconfig/scripts/verify-mobile-web-app-bundle.mjsdocs/site/content/docs/mobile.mdxmobile/.gitignoremobile/app/h/[hostId]/accounts.tsxmobile/app/h/[hostId]/accounts.web.tsxmobile/app/h/[hostId]/agent-history/[worktreeId].tsxmobile/app/h/[hostId]/agent-history/[worktreeId].web.tsxmobile/app/h/[hostId]/files/[worktreeId].tsxmobile/app/h/[hostId]/files/[worktreeId].web.tsxmobile/app/h/[hostId]/files/preview/[worktreeId].tsxmobile/app/h/[hostId]/files/preview/[worktreeId].web.tsxmobile/app/h/[hostId]/index.tsxmobile/app/h/[hostId]/index.web.tsxmobile/app/h/[hostId]/review/[worktreeId].tsxmobile/app/h/[hostId]/review/[worktreeId].web.tsxmobile/app/h/[hostId]/session/[worktreeId].tsxmobile/app/h/[hostId]/session/[worktreeId].web.tsxmobile/app/h/[hostId]/source-control/[worktreeId].tsxmobile/app/h/[hostId]/source-control/[worktreeId].web.tsxmobile/app/h/[hostId]/tasks.tsxmobile/app/h/[hostId]/tasks.web.tsxmobile/app/h/[hostId]/web.tsxmobile/app/h/[hostId]/web.web.tsxmobile/app/troubleshoot.tsxmobile/metro.config.jsmobile/package.jsonmobile/rpc-foundation/goldens/codex-reset-credit-consumed.jsonmobile/rpc-foundation/goldens/codex-reset-credit-resumed.jsonmobile/rpc-foundation/goldens/matrix-components.codex-reset-credit-accounts.consumecodexresetcredit-1.jsonmobile/rpc-foundation/pilot-scenarios.jsonmobile/scripts/build-mermaid-webview-engine.mjsmobile/scripts/build-rich-markdown-editor-script.mjsmobile/scripts/metro-shell-cache-key.test.tsmobile/src/accounts/MobileAccountsScreen.tsxmobile/src/accounts/mobile-accounts-screen-reset-credit.test.tsmobile/src/components/ConfirmModal.tsxmobile/src/components/MobileHtmlPreview.tsxmobile/src/components/MobileHtmlPreview.web.tsxmobile/src/components/MobileMarkdown.tsxmobile/src/components/MobileRichMarkdownEditor.test.tsxmobile/src/components/MobileRichMarkdownEditor.tsxmobile/src/components/MobileRichMarkdownEditor.web.tsxmobile/src/components/MobileSelectableText.ios.tsxmobile/src/components/MobileSelectableText.tsxmobile/src/components/ProtocolBlockScreen.test.tsmobile/src/components/ProtocolBlockScreen.tsxmobile/src/components/codex-reset-credit-consume-operations.tsmobile/src/components/codex-reset-credit-reply-schema.tsmobile/src/components/codex-reset-credit.test.tsmobile/src/components/codex-reset-credit.tsmobile/src/components/html-preview-link-affordance.test.tsxmobile/src/components/mobile-rich-markdown-editor-document.test.tsmobile/src/components/mobile-rich-markdown-editor-html.test.tsmobile/src/components/mobile-rich-markdown-editor-html.tsmobile/src/components/mobile-rich-markdown-editor-script-string.tsmobile/src/components/mobile-selectable-text-ios.test.tsxmobile/src/components/mobile-webview-editor-web-fallbacks.test.tsxmobile/src/components/pr-sidebar/MermaidDiagram.tsxmobile/src/components/pr-sidebar/MermaidDiagram.web.tsxmobile/src/components/pr-sidebar/mermaid-diagram-config.tsmobile/src/components/pr-sidebar/mermaid-diagram-html.test.tsmobile/src/components/pr-sidebar/mermaid-page-engine.tsmobile/src/components/rich-markdown/create-rich-markdown-editor-document.tsmobile/src/components/rich-markdown/document-host-root.test.tsmobile/src/components/rich-markdown/document-host-seams.tsmobile/src/components/rich-markdown/document-lifecycle.test.tsmobile/src/components/rich-markdown/document-markup.tsmobile/src/components/rich-markdown/document-scope.tsmobile/src/components/rich-markdown/editor-commands.tsmobile/src/components/rich-markdown/editor-content.test.tsmobile/src/components/rich-markdown/editor-selection.test.tsmobile/src/components/rich-markdown/editor-surface.tsmobile/src/components/rich-markdown/keyboard-inset.tsmobile/src/components/rich-markdown/markdown-code-fence-allocation.test.tsmobile/src/components/rich-markdown/markdown-round-trip.test.tsmobile/src/components/rich-markdown/native-document-bundle.test.tsmobile/src/components/rich-markdown/native-document-entry.tsmobile/src/components/rich-markdown/rich-markdown-document-parse-time.test.tsmobile/src/components/rich-markdown/rich-markdown-editor-host.test-support.tsmobile/src/components/rich-markdown/rich-markdown-host-seams.test.tsmobile/src/components/rich-markdown/rich-markdown-web-document-mount.tsmobile/src/components/use-codex-reset-credit-action.tsmobile/src/diagnostics/connection-diagnostics-report.test.tsmobile/src/diagnostics/connection-diagnostics-report.tsmobile/src/diagnostics/mobile-web-shell-update-failure-copy.tsmobile/src/diagnostics/native-diagnostics-operations.tsmobile/src/diagnostics/troubleshoot-view.tsxmobile/src/files/MobileMediaPlayback.tsxmobile/src/files/MobileMediaPlayback.web.tsxmobile/src/files/mobile-media-preview-cache.test.tsmobile/src/files/mobile-media-preview-cache.tsmobile/src/files/mobile-media-preview-cache.web.tsmobile/src/files/mobile-media-preview-document.tsmobile/src/host-screen/HostScreen.tsxmobile/src/mobile-web-shell/MobileWebShellScreen.test.tsxmobile/src/mobile-web-shell/MobileWebShellScreen.tsxmobile/src/mobile-web-shell/PageRouteUnavailableScreen.tsxmobile/src/mobile-web-shell/bridge-host-notify.tsmobile/src/mobile-web-shell/bridge/bridge-init-route.tsmobile/src/mobile-web-shell/bridge/bridge-route-update.tsmobile/src/mobile-web-shell/cancelled-navigation-target.tsmobile/src/mobile-web-shell/catch-all-page-route-states.test.tsxmobile/src/mobile-web-shell/catch-all-page-route.test.tsxmobile/src/mobile-web-shell/catch-all-page-route.tsxmobile/src/mobile-web-shell/host-area-layout-class-change.test.tsxmobile/src/mobile-web-shell/host-area-leave-session.test.tsxmobile/src/mobile-web-shell/host-area-session.test.tsmobile/src/mobile-web-shell/host-area-sidebar-matrix.test.tsxmobile/src/mobile-web-shell/host-area-two-hosts.test.tsxmobile/src/mobile-web-shell/mobile-web-shell-agent-history-route.test.tsxmobile/src/mobile-web-shell/mobile-web-shell-cached-generation.tsmobile/src/mobile-web-shell/mobile-web-shell-files-route.test.tsxmobile/src/mobile-web-shell/mobile-web-shell-gates.tsmobile/src/mobile-web-shell/mobile-web-shell-host-list-route.test.tsxmobile/src/mobile-web-shell/mobile-web-shell-page-route-wall.test.tsmobile/src/mobile-web-shell/mobile-web-shell-refused-update-decision.test.tsmobile/src/mobile-web-shell/mobile-web-shell-route.test.tsxmobile/src/mobile-web-shell/mobile-web-shell-screen-test-harness.tsxmobile/src/mobile-web-shell/mobile-web-shell-session-contract.tsmobile/src/mobile-web-shell/mobile-web-shell-session-effects.tsmobile/src/mobile-web-shell/mobile-web-shell-session-route.test.tsxmobile/src/mobile-web-shell/mobile-web-shell-session.test.tsmobile/src/mobile-web-shell/mobile-web-shell-session.tsmobile/src/mobile-web-shell/mobile-web-shell-update-failure-outcome.tsmobile/src/mobile-web-shell/mobile-web-shell-update-failure-record.test.tsmobile/src/mobile-web-shell/mobile-web-shell-update-failure.tsmobile/src/mobile-web-shell/page-route-policy.test.tsmobile/src/mobile-web-shell/page-route-policy.tsmobile/src/mobile-web-shell/removed-host-shell-cache.tsmobile/src/mobile-web-shell/shell-lifecycle-log.test.tsmobile/src/mobile-web-shell/shell-lifecycle-log.tsmobile/src/mobile-web-shell/shell-page-frame.test.tsmobile/src/mobile-web-shell/shell-route-first-frame.test.tsxmobile/src/mobile-web-shell/shell-screen-route.tsmobile/src/mobile-web-shell/shell-switch-decision.test.tsmobile/src/mobile-web-shell/shell-switch-decision.tsmobile/src/mobile-web-shell/use-mobile-web-shell-bridge.tsmobile/src/mobile-web-shell/use-mobile-web-shell-session.test.tsmobile/src/mobile-web-shell/use-mobile-web-shell-session.tsmobile/src/mobile-web-shell/use-shell-stack-pop.test.tsxmobile/src/navigation/host-stack-screens.tsmobile/src/navigation/workspace-route.test.tsxmobile/src/platform/keyboard-persisting-taps.test.tsmobile/src/platform/keyboard-persisting-taps.tsmobile/src/platform/keyboard-persisting-taps.web.tsmobile/src/platform/live-input-composing-range.test.tsmobile/src/platform/live-input-composing-range.tsmobile/src/platform/live-input-composing-range.web.tsmobile/src/platform/random-v4-uuid.test.tsmobile/src/platform/random-v4-uuid.tsmobile/src/session/MobileDiffReviewRouteScreen.tsxmobile/src/session/MobileNativeChatAwaitingInputRow.tsxmobile/src/session/MobileNativeChatMessage.copy.test.tsxmobile/src/session/MobileNativeChatMessage.paced-reveal.test.tsmobile/src/session/MobileNativeChatMessage.tsxmobile/src/session/MobileNativeChatNoticeRow.tsxmobile/src/session/MobileNativeChatProse.tsxmobile/src/session/MobileNativeChatResolutionReceipt.tsxmobile/src/session/MobileNativeChatVisualFrame.tsxmobile/src/session/MobileNativeChatVisualFrame.web.tsxmobile/src/session/MobileSessionRouteScreen.tsxmobile/src/session/mobile-clipboard-image-resize.tsmobile/src/session/mobile-clipboard-image-resize.web.tsmobile/src/session/mobile-existing-agent-launch.tsmobile/src/session/mobile-native-chat-stopped-before-start.test.tsxmobile/src/session/mobile-native-chat-visual-bridge.test.tsmobile/src/session/mobile-native-chat-visual-bridge.tsmobile/src/session/mobile-structured-agent-session-launch.tsmobile/src/session/mobile-worker-takeover-send-sites.test.tsmobile/src/session/structured-session-operation-id.tsmobile/src/session/use-mobile-native-chat-session.tsmobile/src/session/use-mobile-terminal-paste.test.tsmobile/src/session/use-notification-pane-navigation.test.tsxmobile/src/session/use-notification-pane-navigation.tsmobile/src/session/use-notification-pane-navigation.web.test.tsxmobile/src/session/use-notification-pane-navigation.web.tsmobile/src/storage/codex-reset-attempt-journal.test.tsmobile/src/storage/codex-reset-attempt-journal.tsmobile/src/storage/preferences.test.tsmobile/src/storage/preferences.tsmobile/src/test-support/bridged-parity/divergence-classes.test.tsmobile/src/test-support/bridged-parity/divergence-classes.tsmobile/src/test-support/census-source-files.test.tsmobile/src/test-support/census-source-files.tsmobile/src/test-support/rpc-recording/README.mdmobile/src/test-support/rpc-recording/adapters/codex-reset-credit-mount-adapters.tsmobile/src/test-support/rpc-recording/adapters/session-terminal-input-mount-adapters.tsmobile/src/test-support/rpc-recording/declared-device-state.test.tsmobile/src/test-support/rpc-recording/rpc-recording-through-bridge.test.tsmobile/src/transport/mobile-web-bundle-reply-schemas.tsmobile/src/transport/rpc-subscription-inventory.tsmobile/src/transport/unvalidated-rpc-request-port-inventory.tsmobile/tests-typecheck-baseline.txtmobile/vitest.setup.tsmobile/web-entry/index.tsxmobile/web-entry/web-overrides.jsonsrc/main/codex-accounts/codex-reset-credit-coordinator.tssrc/main/codex-accounts/codex-reset-credit-ledger.test.tssrc/main/codex-accounts/codex-reset-credit-ledger.tssrc/main/codex-accounts/service-reset-credit-durability.test.tssrc/main/codex-accounts/service-reset-credit-retry-routing.test.tssrc/shared/rpc-contract/accounts-params.ts
💤 Files with no reviewable changes: 50
- mobile/src/session/MobileNativeChatMessage.copy.test.tsx
- mobile/src/components/rich-markdown/native-document-entry.ts
- mobile/src/components/pr-sidebar/MermaidDiagram.web.tsx
- mobile/src/components/mobile-rich-markdown-editor-html.test.ts
- mobile/src/components/MobileRichMarkdownEditor.test.tsx
- mobile/src/mobile-web-shell/shell-switch-decision.test.ts
- mobile/src/storage/codex-reset-attempt-journal.test.ts
- mobile/src/files/MobileMediaPlayback.web.tsx
- mobile/.gitignore
- mobile/src/components/rich-markdown/native-document-bundle.test.ts
- mobile/src/navigation/workspace-route.test.tsx
- mobile/src/components/mobile-selectable-text-ios.test.tsx
- mobile/src/mobile-web-shell/shell-switch-decision.ts
- mobile/tests-typecheck-baseline.txt
- mobile/src/mobile-web-shell/mobile-web-shell-route.test.tsx
- mobile/scripts/metro-shell-cache-key.test.ts
- mobile/src/storage/preferences.ts
- mobile/src/files/mobile-media-preview-cache.web.ts
- mobile/app/h/[hostId]/web.tsx
- mobile/app/h/[hostId]/web.web.tsx
- mobile/src/platform/live-input-composing-range.web.ts
- mobile/src/components/MobileSelectableText.ios.tsx
- mobile/src/components/MobileRichMarkdownEditor.web.tsx
- mobile/src/test-support/rpc-recording/README.md
- mobile/src/components/mobile-rich-markdown-editor-script-string.ts
- mobile/src/files/mobile-media-preview-cache.test.ts
- mobile/metro.config.js
- mobile/rpc-foundation/goldens/codex-reset-credit-resumed.json
- mobile/src/components/MobileSelectableText.tsx
- mobile/src/session/use-mobile-terminal-paste.test.ts
- mobile/src/session/mobile-clipboard-image-resize.web.ts
- mobile/src/storage/codex-reset-attempt-journal.ts
- mobile/src/session/MobileNativeChatVisualFrame.web.tsx
- mobile/src/session/use-notification-pane-navigation.web.test.tsx
- mobile/src/components/mobile-rich-markdown-editor-document.test.ts
- mobile/src/session/use-notification-pane-navigation.web.ts
- mobile/src/components/MobileHtmlPreview.web.tsx
- mobile/src/files/mobile-media-preview-document.ts
- mobile/src/components/mobile-rich-markdown-editor-html.ts
- mobile/scripts/build-mermaid-webview-engine.mjs
- mobile/src/navigation/host-stack-screens.ts
- mobile/src/session/mobile-worker-takeover-send-sites.test.ts
- mobile/src/storage/preferences.test.ts
- mobile/src/session/mobile-native-chat-visual-bridge.test.ts
- mobile/src/components/pr-sidebar/mermaid-diagram-html.test.ts
- mobile/scripts/build-rich-markdown-editor-script.mjs
- mobile/src/session/mobile-native-chat-stopped-before-start.test.tsx
- mobile/src/session/MobileNativeChatMessage.paced-reveal.test.ts
- mobile/src/platform/keyboard-persisting-taps.web.ts
- mobile/src/session/mobile-native-chat-visual-bridge.ts
Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 3 remain after this review.
…-file mock Anti-slop forbids module mocking outside test files. The vitest config now aliases expo-crypto to a faithful Node implementation of the names shared code reaches; a test file's own mock still wins. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
What changes for users
The host Accounts screen (agent accounts, usage bars, rate limits, Codex reset credits) is now drawn by the page the desktop serves, like Tasks and Agent history. Changes to it now ship with desktop releases instead of needing a phone build. It looks and behaves the same as before.
What it took to run on the page
/h/[hostId]/accountsis a page route with the same grants as the worktree list (navigate,storage,externalLink,haptics). The screen moved tosrc/accounts/MobileAccountsScreen.tsx.accounts.web.tsxrenders it on the page, andaccounts.tsxis now the shell route that mounts the page, in the same shape astasks.tsx.Alert.alertdoes nothing on React Native Web. The reset confirmation, the reset outcome and a failed account switch now useConfirmModal.ConfirmModalcan now hide Cancel (cancelLabel={null}) for a notice that only needs an OK.useRouteHandoff, so it pops the shell's stack. expo-router's ownback()does nothing on the page.requestCodexResetCreditbyrandomV4Uuid(moved from the session code tosrc/platform/random-v4-uuid.ts). Whencrypto.randomUUIDis missing (Hermes, or the iOS shell's custom scheme), it builds the UUID fromexpo-crypto's secure random bytes, neverMath.random.RefreshControlis removed (it has no gesture on the web). The header refresh button is unchanged and works.Mixed versions and release order
Codex reset credit: mobile mirrors the desktop
The desktop keeps no client-side record of a reset. It asks the host, and the host's ledger owns the attempt. The phone used to keep its own retry record (an AsyncStorage journal) because the host refused a retry under a new key with "already attempted". The page can't keep that record, so this PR changes the host instead:
orca serveno longer gets stuck on "unknown outcome".consumeCurrent, which already resumed by account. The reply shape is unchanged. One edge: an installed native Accounts screen that sends a new key with a newer offer while an attempt is unresolved now gets "Invalid reset response" instead of "unknown outcome". No credit can be double-spent, and it goes away when users update the app.Testing
src/accounts/mobile-accounts-screen-reset-credit.test.tscovers the confirm drawer, the outcome notices, cancel, a failed switch shown on screen, Back through the handoff, and a lost reply retried under a new key. On the host,service-reset-credit-durability.test.tscovers: a same-offer retry under a new key gets the recorded outcome with no second provider call, a pending attempt resumes under its original key, a different offer is still refused while an outcome is unknown, two concurrent keys for one offer spend once, an unresolved attempt reached with a newer offer resumes under its own key, and the desktopconsumeCurrentand the remote RPC converge on one attempt. On mobile, a test moves the snapshot after the failure and checks that the resumed outcome is shown. These fail without the coordinator change. Also: the manifest exclusion test, accounts in the shell-route first-frame table, and the browser back-stack checks against the built page.config/scripts/mobile-web*test including Chrome render checks,pnpm build:mobile-web(built and verified, 14 routes), andcheck:code-quality:changed.pnpm lintpasses every step except 5no-cyclewarnings in the native code-quality audit, and the base branch has the same 5.orca-dev servebuilt from this branch. Accounts opened from the worktree list renders on the page: no native text nodes, and the page target is at/h/<host>/accounts. The refresh button sendsaccounts.listover the bridge (frame captured over CDP). The chevron and hardware Back both return to the worktree list. A deep link through the shell route opens a new page session for Accounts. Edit host still opens natively. The isolated host has no agent accounts, so the switch and reset flows were covered by unit tests only. After the review fixes, the APK and host were rebuilt and rechecked: Accounts opens on the page and Back returns to the worktree list.Before (native, #29979 APK) and after (page, this branch):