mirror of
https://github.com/stablyai/orca.git
synced 2026-11-13 09:27:29 +00:00
hide-codex-error-msg
60
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
97df516add |
test: prune shared guards and manual Swift policy checks (#41554)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
fbec96cf29 |
test: retire low-value runtime and native checks (#41533)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
eb16a6e351 |
test: remove low-value input and native decoder checks (#41481)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
46b225e473 |
chore(links): point website, docs and download links at orca.dev (#41376)
The site now lives at orca.dev; the old onorca.dev and www.onorca.dev hosts redirect there. Linking to orca.dev directly skips that redirect. Only page links change. Service hosts, sign-in and relay endpoints stay as they are. |
||
|
|
bbe22681c2 |
test: remove notification, icon and native policy inventories (#41375)
* test: remove notification and native policy inventories * test: remove icon projection samples and tray suite --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
5d61e00c6d |
build(mobile): upgrade to Expo SDK 58 and React Native 0.88 (#37935)
* build(mobile): upgrade to Expo SDK 58 and React Native 0.88 Expo 58 drops Zod from @expo/cli, which clears the last Zod 3 advisory (GHSA-mrhx-c7xc-m3w5) in mobile along with its local patch and override. - Expo 55 -> 58, React Native 0.83 -> 0.88, React 19.3, gesture-handler 3, webview 14, reanimated 4.7, worklets 0.13 (SDK 58 bundled versions, pinned below today's batch to respect minimumReleaseAge). - Port the react-native and react-native-webview patches unchanged; drop expo-constants, expo-notifications, @expo/cli, expo-router, zod and query-string patches plus the zod/query-string/xcode>uuid overrides, which upstream fixed or no longer need. - iOS minimum rises to 16.4 (Expo 58 / RN 0.88 requirement). - Migrate to RN 0.88 strict types: instance ref types, NamedStyles, AppState.currentState, list header/footer nulls. - Replace removed runtime APIs: InteractionManager -> idle callback with a macrotask fallback; StyleSheet.absoluteFillObject -> absoluteFill; FileHandle.readBytes -> readBytesSync (it became async). - Resolve react-native/asset-registry for shared assets in Metro and alias the old registry path for react-native-svg in the desktop mobile-web bundle. - Reduce the iOS scene plugin to the reconnect override; Expo 58's template now ships the scene delegate itself. * fix(mobile): bridge react-native-svg web imports and libsodium runtime for RN 0.88 - Share react-native-svg web shims between the mobile-web build and its render checks: map the old asset-registry path to react-native-web's registry and stub the Android-only touch-sound TurboModule. - Patch react-native-libsodium 1.7.0 to read the JSI runtime from the bridge proxy; RN 0.88 removed RCTCxxBridge.runtime. * fix(mobile): use Expo 58's legacy promise resolver for audio permissions * fix(mobile): skip null handles in the RPC recording timer shims for RN 0.88 types * style(mobile): format MobilePRSidebar after rebase * fix(mobile): name RN 0.88 text input refs for declarations; allow queued back in stack render check - Type text input refs with an exported interface so declaration emit can name hook results (RN 0.88 aliases TextInputInstance to an unexported class). - Expo Router 58 drains back() after a commit; check the interrupted push's exit from the frame it starts, bounded to three frames of lag. * chore(mobile): justify the text input handle interface for React Doctor * chore(mobile): use a nameable intersection alias for text input refs * fix(mobile): use sync expo-file-system writes, copies and moves on Expo 58 Expo 58 made File.write/copy/move and FileHandle.writeBytes return promises. Our callers never awaited them, so failures escaped their try/catch and the store's tmp-then-move and PDF writes could race their close or rename. * build(mobile): raise the page bundle budget to 10 MiB for Expo 58 Expo 58 adds about 310 KB to the page: expo-router (+258 KB, now carrying React Navigation, net about +110 KB) and Reanimated 4.7 (+152 KB). Main had 85 KB of headroom left. 10 MiB still stays within the 10 MiB per-asset contract. * fix(mobile): drop the window-metrics seed RN 0.88 removed from Android RN 0.88 no longer holds window metrics: DeviceInfoModule reads the activity's WindowMetrics on demand and setWindowDisplayMetrics is gone, so MainActivity no longer compiled. Layout scale still comes from the screen metrics we reseed. --------- Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> |
||
|
|
a9a0e8cd21 |
test: remove shallow catalog, browser and launch checks (#41258)
* test: remove routine native and catalog suites * test: remove shallow browser, bridge and CLI checks --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
d5f6fe17bf |
test: remove 129 low-value cases and 21 whole suites (#41210)
* test: remove 83 low-value cases and ten whole suites * test: remove 46 more low-value cases and eleven suites --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
9576e55773 |
test: remove 1,144 low-value declarations (#41177)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
c4e4428af7 |
fix(mobile): say which WebView to install when Android's is too old to open a workspace (#40907)
* fix(mobile): tell Android users which WebView to update when it is too old The page shell needs androidx's DOCUMENT_START_SCRIPT:1, which stable WebView only reports from Chromium 101; the comments claimed 83/88. Name the required WebView and the one installed, and how to switch it. * fix(mobile): read the WebView provider through the shell module index Tests stub the module index; a direct file import loaded expo-modules-core unstubbed. |
||
|
|
258d3b375a | perf(mobile): brotli-compress the page build (#40532) | ||
|
|
a4291dbc4a |
fix(mobile): keep page builds where the OS doesn't purge them (#40524)
iOS emptied Library/Caches under storage pressure, and Android's cacheDir is cleared by Clear cache, cleaner apps and low storage. Each purge cost a full foreground download of the page build on the next open. Builds now live in Application Support (excluded from backup) on iOS and filesDir on Android (kept off device transfers), and the old cache folder moves over once. |
||
|
|
41867a2a85 |
perf(mobile): stop rebooting the Home spare page on every hop and after iOS memory warnings (#40270)
* perf(mobile): back off spare page refills after an unclaimed drop or memory warning * fix(mobile): skip a spare whose store read outlived an unclaimed drop * fix(mobile): arm the spare refill timer on whole milliseconds |
||
|
|
00f109d9e1 |
test: remove low-value renderer, chain, and native checks (#40287)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
c38a8e4b9a |
test: remove low-value wrappers, inventories, and timing checks (#40284)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
a44cb9ac3e |
perf(mobile): prewarm one page while Home is shown so opening a page skips the WebView start (#40018)
* perf(mobile): answer a page's ready that arrived before its bridge host A page's ready can reach the shell before the screen has built its bridge host (the host waits on the client and the host profile read). It used to be dropped, so init waited for the page's next retry, up to 2 s later. Keep the latest ready per session and answer it once the host exists. * perf(mobile): keep one prewarmed page document while Home is shown While native Home is on screen, boot one page document for the most recently connected host with a cached build, parked invisibly in the window. Opening a page from that build adopts it instead of building a WebView, which removes the WebView start and script parse from the tap. The spare is dropped when Home is left without a claim (2 s grace), on background, memory pressure, its own failure and before the cache deletes its files. It is never kept beside a shown page, and only phones with more than 3 GiB of RAM (4 GB phones) get one. A parked page is told to stop asking for init; the adopting view answers the ready it sent. Builds on Jinwoo's spare pool and replay work in #32203 and #32204. Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> * fix(mobile): unregister the shell's memory callbacks from the context that registered them --------- Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> |
||
|
|
da76713d82 |
refactor(mobile): move the shell's WebView and its fences into a document object (#39946)
The native shell view now hosts a MobileWebShellDocument that owns the WebView, its scheme handler or request intercept, the network block, the bridge and the navigation policy. The view keeps layout, the back-edge swipe and its React events. Nothing changes for a page; this is the seam a prewarmed spare page needs. Based on Jinwoo's page split in #32202. Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> |
||
|
|
537d764f58 |
perf(mobile): warm WebKit's processes on iOS before the first page opens (#39851)
* perf(mobile): warm WebKit's processes on iOS before the first page opens Builds a blank, non-persistent WKWebView a second after Home has a paired host, so the first page open starts against WebContent, GPU and Network processes already running. A release before the delayed start now makes the start a no-op. Based on #32163 by Jinwoo-H. Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> * fix(mobile): guard warmUp on older binaries and release the warm view on page failure Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> --------- Co-authored-by: m4air <m4air@m4airs-MacBook-Air.local> Co-authored-by: Jinwoo-H <73622457+Jinwoo-H@users.noreply.github.com> |
||
|
|
e4a479ceec |
test: remove 282 low-value tests (#39632)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
8897f986cf | perf(mobile): tell a buried Android page it's hidden (#39409) | ||
|
|
1363f16782 |
fix(mobile): keep the page after the phone reclaims its WebView in the background (#39415)
* fix(mobile): remount silently after background WebView losses * fix(mobile): hold a reclaimed remount until the app is in front * fix(mobile): give a reclaimed renderer one free remount per painted document * fix(mobile): report a dead Android renderer again with its original loss facts * refactor(mobile): build the page update-failure record in one place * fix(mobile): declare desktop-bound native verbs in the verb table and let the device store skip the verdict hold * refactor(mobile): name the update-failure record builder after the effect it returns * test(mobile): check the device store read before the verdict actually succeeds * test(mobile): leave the store-verb hold test to its own PR |
||
|
|
60834fb1b6 |
fix(mobile): a failed page remounts instead of re-downloading; deadlines count only foreground time (#38868)
* fix(mobile): a failed page remounts instead of re-downloading; deadlines count only foreground time Page deadlines now pause in the background and restart on return, so React Native firing overdue timers on resume no longer reads as a page failure. A failed document remounts the same files once, then shows the failure screen; it never deletes and re-downloads on its own. Try again after a page failure or an unreadable generation deletes the host cache first, so it downloads fresh. Replaces #34550 without its file-size check. * fix(mobile): drop reports a remounted page's retired view already queued A failed page remounts at once, minting the replacement's session before React commits it. A fault or ready the old view had already queued then landed on the replacement: a second fault showed the failure screen before the new page loaded, and a late ready marked it ready and cancelled its load deadline. Each report now carries the session of the view that rendered it and is dropped once that view is retired. |
||
|
|
7928495938 |
fix(android): prevent black frames while dragging text handles (#38022)
* Protect Android insertion and selection magnifier readback * ci: refresh Android checks against current main * ci: refresh Android checks after main repairs --------- Co-authored-by: Kelvin Amoaba <97001695+AmoabaKelvin@users.noreply.github.com> |
||
|
|
c739600f88 |
fix(mobile): warm up the split keyboard before the first WebKit focus (#37260)
Co-authored-by: Claude Sonnet 5.5 <noreply@anthropic.com> |
||
|
|
f1d8e5daa3 | perf(ios): reuse compiled network-block rules (#39056) | ||
|
|
c884428609 |
feat(mobile): copy, save and share an image from the full-screen image viewer (#38303)
* feat(mobile): copy, save and share an image from the chat image viewer * refactor(mobile): pass image viewer actions in so diagrams skip them * fix(mobile): name the image verb native.image.act and serialize image actions * fix(mobile): queue image actions per page and skip SVG links * refactor(mobile): share the cache-folder helper and table the viewer's image actions * fix(mobile): keep the toast's live region mounted so screen readers hear it * test(mobile): re-record agent history goldens for the always-mounted toast region * test(mobile): import the declared routes in the moved routes test |
||
|
|
818fb8d683 |
fix(mobile): keep iPad page headers clear of the window controls (#38635)
* fix(mobile): keep iPad page headers clear of the window controls The reserved-regions probe reported the window's plain safe area, which on iPadOS 26+ windowed mode does not cover the window controls. On iOS 27.1+ pages take their top inset from that value, so the host header sat under the controls while native screens, which get the corner-adapted inset, cleared them. * fix(mobile): only raise the page's top inset for window controls |
||
|
|
00b56535c6 |
test: remove low-value native helper tests (#38552)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
31d0f53cd9 |
Remove 230 low-value main, renderer, and mobile test cases (#38223)
Delete 19 whole suites and update coverage metadata and reduced flaky-pattern counts. Co-authored-by: Codex <codex@openai.com> |
||
|
|
016530156c |
fix(mobile): back from a session lands on its host's session list (#36956)
* fix(mobile): back from a session lands on its host's session list A session opened from Home's "Jump back in" or a notification replaced the host list it had just mounted, so Back went to Home. It is now pushed over that list. A session opened from the host list lives inside the list's own page, and the stack holding it has one screen, so the iOS edge swipe belonged to the parent screen and popped the whole host to Home. While the page claims Back the parent's swipe is turned off too, and a left-edge pan on the shell view sends Back to the page. * fix(mobile): keep the host swipe off until its last page claim ends Count claims per parent screen instead of guessing whether a shell is its stack's root, and measure the back edge from the shell's own left edge so a landscape safe area does not hide it. * refactor(mobile): share the session route name and write the host swipe once per claim run * test(mobile): give the catch-all screen mock a parent navigator |
||
|
|
70256eab83 |
test: remove 181 low-value tests and 2 suites (#37024)
Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
ff54751393 |
fix(mobile): render inline HTML cards and previews on iOS (#36986)
The iOS web shell cancelled every subframe navigation, including the sealed about:srcdoc frames that HTML previews and chat visual cards render in, so they stayed blank. Allow exactly about:srcdoc subframes, let only a main-frame load spend the shell-load allowance, and never fail the shell document over a subframe response. Chromium never asks the Android shell about about: subframes, so Android already renders them. |
||
|
|
57e287b555 |
fix(mobile): stop the unfolded Duo crashing on split keyboard (#36948)
UIKit's split-keyboard update crashes breaking our constraints on keyboardLayoutGuide, so the probe reads keyboardWillChangeFrame instead. The hinge interaction goes too: UIKit already relays out a view that read reserved regions in layoutSubviews when they change. Fixes #36742 |
||
|
|
a803cbead8 |
feat(mobile): iPhone Duo key rail with D-pad, split terminal panes and half-fold layout (#35177)
Carries over #33221 and #33210. Replaces our #34484 Duo support with the community design, on top of main's rebuilt terminal key bar (#36166). Fixes #33200 Co-authored-by: Takeshi Matsumoto <zytakeshi520@gmail.com> Co-authored-by: Lars Keggenhoff <lars@kghf.io> |
||
|
|
5121f25a5a |
perf(mobile): batch iOS push-dismissal ledger writes per page (STA-7366) (#36145)
Catch-up pages called the native ledger once per dismissal, each call re-reading, encoding and writing the full 24h UserDefaults ledger. A new rememberAll bridge merges a whole page in one locked read/encode/write with identical semantics (max seq per identity, refreshed 24h expiry). |
||
|
|
86f2f24ddc | feat(mobile): open PDFs from the file browser in Quick Look or the system viewer (#33771) | ||
|
|
7d6603aa17 |
[comment audit] Complete repository-wide comment cleanup (#33249)
* [comment audit] Align legacy failure-read counts with cached display * [comment audit] Condense shared provider lifecycle and browser recovery comments [skip ci] * [comment audit] Condense shared generation, path and preview comments [skip ci] * [comment audit] Condense host, file and Git contract rationale [skip ci] * [comment audit] Tighten Git lifecycle and provider contract comments [skip ci] * [comment audit] Clarify shared host authority and launch isolation [skip ci] * [comment audit] simplify local access mobile and native chat contracts [skip ci] * [comment audit] simplify native chat presentation and state contracts [skip ci] * [comment audit] simplify native runtime and provider contracts [skip ci] * [comment audit] simplify migration and orchestration contracts [skip ci] * [comment audit] simplify plugin trust and process capture rationale [skip ci] * [comment audit] simplify terminal delivery and remote compatibility rationale [skip ci] * [comment audit] clarify remote recovery and launch wire rationale [skip ci] * [comment audit] shorten shared RPC compatibility and runtime evidence comments [skip ci] * [comment audit] simplify secure storage shell startup and sidebar rationale [skip ci] * [comment audit] clarify sidebar, skill and SSH invariants [skip ci] * [comment audit] condense structured chat delivery and timing rationale [skip ci] * [comment audit] condense terminal replay and telemetry invariants [skip ci] * [comment audit] clarify launch permission and terminal delivery rationale [skip ci] * [comment audit] clarify workspace ownership and layout contracts [skip ci] * [comment audit] wait for fitted and painted SSH terminal links * [comment audit] clarify layout persistence and session ownership [skip ci] * [comment audit] clarify host routing and workspace lifecycle contracts [skip ci] * [comment audit] clarify startup persistence and app lifetime comments [skip ci] * [comment audit] clarify recovery and activity boundaries [skip ci] * [comment audit] condense activity and artifact invariants [skip ci] * [comment audit] condense automation authority contracts [skip ci] * [comment audit] simplify automation scheduling and history contracts [skip ci] * [comment audit] clarify browser chrome and annotation lifecycles [skip ci] * [comment audit] clarify browser routing and retained guest ownership [skip ci] * [comment audit] clarify browser recovery and document ownership comments [skip ci] * [comment audit] restore hosted runners required by native IME fixtures * [comment audit] shorten preview, palette and dashboard invariants [skip ci] * [comment audit] clarify terminal preview, dashboard and retention comments [skip ci] * [comment audit] clarify diff-note recovery and editor lifecycle comments [skip ci] * [comment audit] shorten rich-text and combined-diff ownership comments [skip ci] * [comment audit] shorten autosave, draft recovery and model ownership comments [skip ci] * [comment audit] shorten save-queue, notebook and Markdown constraints [skip ci] * [comment audit] shorten Monaco, PDF and rich Markdown safeguards [skip ci] * [comment audit] clarify rich Markdown source and selection safeguards [skip ci] * [comment audit] clarify editor loading and emulator stream lifetimes [skip ci] * [comment audit] trim emulator and feature tour narration [skip ci] * [comment audit] clarify floating workspace and GitHub cache ownership [skip ci] * [comment audit] trim provider controls and Linear filter explanations [skip ci] * [comment audit] clarify pairing and chat resume lifetimes [skip ci] * [comment audit] clarify chat delivery and transcript lifetimes [skip ci] * [comment audit] clarify session options and host-owned status [skip ci] * [comment audit] clarify draft durability and live-status precedence [skip ci] * [comment audit] Trim chat send and model ownership explanations [skip ci] * [comment audit] Shorten transcript ownership and send recovery comments [skip ci] * [comment audit] Simplify native chat composer and transcript hook comments [skip ci] * [comment audit] Shorten native chat scroll and structured session explanations [skip ci] * [comment audit] Trim workspace picker and onboarding commentary [skip ci] * [comment audit] Simplify pet rendering, review cache and quick open comments [skip ci] * [comment audit] Trim sidebar and session history explanations [skip ci] * [comment audit] Condense session resume and review checks comments [skip ci] * [comment audit] Shorten checks state and file explorer refresh comments [skip ci] * [comment audit] Trim plugin bridge and source-control action rationale [skip ci] * [comment audit] Trim source-control listing and review rationale [skip ci] * [comment audit] Shorten sidebar lifecycle and explorer safeguards [skip ci] * [comment audit] Condense Settings account and appearance rationale [skip ci] * [comment audit] Trim pairing and repository Settings explanations [skip ci] * [comment audit] Condense Settings search SSH and terminal safeguards [skip ci] * [comment audit] Shorten Settings voice and provider ownership comments [skip ci] * [comment audit] Condense Settings draft persistence and host-state rationale [skip ci] * [comment audit] Trim Settings hooks and sidebar setup rationale [skip ci] * [comment audit] Condense sidebar deletion and board-filter safeguards [skip ci] * [comment audit] Condense worktree cards, host ownership and Markdown contracts [skip ci] * [comment audit] Condense sidebar drag, folder and filtering contracts [skip ci] * [comment audit] Condense sidebar parity, sleep and host ownership contracts [skip ci] * [comment audit] Prune focus-read baseline after comment cleanup * [comment audit] Clarify sidebar visibility, board drops and agent evidence [skip ci] * [comment audit] Tighten sidebar listing, drag and reveal contracts [skip ci] * [comment audit] Clarify viewport geometry and skill update safeguards [skip ci] * [comment audit] Tighten skill inventory, usage and host status comments [skip ci] * [comment audit] Clarify usage ownership and resource kill safeguards [skip ci] * [comment audit] Tighten tab selection, rename and search contracts [skip ci] * [comment audit] Tighten tab-strip geometry and drag ownership comments [skip ci] * [comment audit] Tighten Tasks recovery, pagination and mutation comments [skip ci] * [comment audit] Tighten provider loading and terminal recovery comments [skip ci] * [comment audit] Tighten completion, cadence and PTY ownership comments [skip ci] * [comment audit] Clarify terminal restore and parked-pane boundaries [skip ci] * [comment audit] Tighten PTY startup and replay explanations [skip ci] * [comment audit] Clarify PTY identity and delivery ownership [skip ci] * [comment audit] Shorten remote reconnect and input replay rationale [skip ci] * [comment audit] Condense terminal parking clipboard and file link comments [skip ci] * [comment audit] Clarify IME ownership and trim trace history [skip ci] * [comment audit] Shorten pane recovery and parked watcher explanations [skip ci] * [comment audit] Condense snapshot replay and visibility recovery rationale [skip ci] * [comment audit] Shorten terminal parking and keyboard ownership rationale [skip ci] * [comment audit] Trim renderer lifecycle and menu rationale [skip ci] * [comment audit] Condense workspace cleanup ownership and settlement notes [skip ci] * [comment audit] Clarify renderer hook evidence and source ownership [skip ci] * [comment audit] Tighten IPC ownership and reconnect explanations [skip ci] * [comment audit] Trim renderer focus, paste and attention comments [skip ci] * [comment audit] Shorten scroll, locale and agent lifecycle comments [skip ci] * [comment audit] Trim agent launch, resume and ownership commentary [skip ci] * [comment audit] Shorten account, browser and editor comments [skip ci] * [comment audit] Clarify host-mirror waits, previews and input ownership [skip ci] * [comment audit] Condense launch recovery and editor tokenization comments [skip ci] * [comment audit] Condense workspace selection and terminal pane rationale [skip ci] * [comment audit] Condense terminal fitting and rendering recovery comments [skip ci] * [comment audit] Condense terminal input and host routing explanations [skip ci] * [comment audit] Condense ownership and sleeping-session recovery rationale [skip ci] * [comment audit] Condense structured launch and terminal link explanations [skip ci] * [comment audit] Condense terminal ownership and workspace file routing rationale [skip ci] * [comment audit] Condense session persistence and workspace activation rationale [skip ci] * [comment audit] Condense workspace routing and browser ownership rationale [skip ci] * [comment audit] Condense mirror recovery and layout authority rationale [skip ci] * [comment audit] Condense runtime transport and file safety rationale [skip ci] * [comment audit] Condense runtime graph and structured session rationale [skip ci] * [comment audit] Condense browser creation and remote tab recovery rationale [skip ci] * [comment audit] Condense snapshot authority and publisher recovery rationale [skip ci] * [comment audit] Condense frame, startup and provider cache rationale [skip ci] * [comment audit] Condense host twin and agent lifecycle rationale [skip ci] * [comment audit] Condense browser authority and editor cleanup rationale [skip ci] * [comment audit] Condense editor buffer identity and review cache rationale [skip ci] * [comment audit] Condense provider lifetime and repository ownership rationale [skip ci] * [comment audit] Condense reconnect, hydration and terminal retirement rationale [skip ci] * [comment audit] Condense recovery, reconciliation and persistence rationale [skip ci] * [comment audit] Condense cleanup ownership and listing admission rationale [skip ci] * [comment audit] Condense teardown ordering and terminal ownership rationale [skip ci] * [comment audit] Condense recovery ledger and web host-boundary rationale [skip ci] * [comment audit] Condense web reconnect and credential lifecycle rationale [skip ci] * [comment audit] Condense cloud rollout and assignment safety rationale [skip ci] * [comment audit] Condense relay session and database recovery rationale [skip ci] * [comment audit] Condense relay placement and protocol compatibility rationale [skip ci] * [comment audit] Condense build and packaging constraints [skip ci] * [comment audit] Condense CI gates and validation isolation rationale [skip ci] * [comment audit] Condense catalog identity and browser validation rationale [skip ci] * [comment audit] Condense mobile browser and frame-budget rationale [skip ci] * [comment audit] Shorten mobile route and browser test explanations [skip ci] * [comment audit] Condense editor and session seam test rationale [skip ci] * [comment audit] Trim session and terminal closure history [skip ci] * [comment audit] Shorten terminal browser and census explanations [skip ci] * [comment audit] Preserve root-only E2E diagnostic baseline * [comment audit] Condense mobile seam and HTML preview test rationale [skip ci] * [comment audit] Condense preview instrumentation and render harness rationale [skip ci] * [comment audit] Condense bundle contracts and native-build constraints [skip ci] * [comment audit] Shorten native packaging and CI routing rationale [skip ci] * [comment audit] Condense release tooling and benchmark constraints [skip ci] * [comment audit] Condense packaging and platform gate rationale [skip ci] * [comment audit] Shorten mobile route and history rationale [skip ci] * [comment audit] Condense mobile browser and component rationale [skip ci] * [comment audit] Shorten drawer and workspace-form rationale [skip ci] * [comment audit] Trim mobile review and editor-document explanations [skip ci] * [comment audit] Condense editor lifecycle and Markdown explanations [skip ci] * [comment audit] Trim mobile file, diagnostics and dictation commentary [skip ci] * [comment audit] Simplify mobile host-list and workspace-view explanations [skip ci] * [comment audit] Clarify mobile shell lifetime and bridge delivery constraints [skip ci] * [comment audit] Condense bridge stream, native request and audio rationale [skip ci] * [comment audit] Reduce bridge schema and page-client commentary [skip ci] * [comment audit] Tighten mobile page bridge contracts [skip ci] * [comment audit] Clarify mobile cache and session constraints [skip ci] * [comment audit] Condense mobile route and lifecycle rationale [skip ci] * [comment audit] Tighten mobile shell hook timing rationale [skip ci] * [comment audit] Clarify route handoff and notification compatibility [skip ci] * [comment audit] Clarify push registration and dictation cleanup constraints [skip ci] * [comment audit] Condense keyboard geometry and native media ownership rationale [skip ci] * [comment audit] Tighten mobile chat scroll and composer concurrency rationale [skip ci] * [comment audit] Condense mobile reply compatibility and upload ownership rationale [skip ci] * [comment audit] Condense optimistic echo and scroll ownership rationale [skip ci] * [comment audit] Tighten stream replay and queued session ownership rationale [skip ci] * [comment audit] Condense mobile composer and reply-schema constraints [skip ci] * [comment audit] Clarify mobile transcript recovery and session ownership [skip ci] * [comment audit] Clarify mobile queue and source-control compatibility [skip ci] * [comment audit] Condense storage and task launch constraints [skip ci] * [comment audit] Clarify task schemas and terminal document ownership [skip ci] * [comment audit] Tighten terminal input and viewport rationale [skip ci] * [comment audit] Shorten terminal and RPC recorder contracts [skip ci] * [comment audit] Condense recorder engine and connection contracts [skip ci] * [comment audit] Shorten host credential and relay recovery comments [skip ci] * [comment audit] Tighten pairing recovery and bundle transport rationale [skip ci] * [comment audit] Clarify RPC delivery and workspace catalog boundaries [skip ci] * [comment audit] Condense catalog and relay entry contracts [skip ci] * [comment audit] Shorten shutdown and SSH relay delivery contracts [skip ci] * [comment audit] Condense WSL relay and E2E regression rationale [skip ci] * [comment audit] Shorten cross-version harness and renderer fixture explanations [skip ci] * [comment audit] Condense paired-host and E2E lifecycle rationale [skip ci] * [comment audit] Shorten layout oracle and regression explanations [skip ci] * [comment audit] Condense paired recovery and ownership regression contracts [skip ci] * [comment audit] Shorten restart, sidebar and SSH lifecycle contracts [skip ci] * [comment audit] Condense terminal rendering and IME regression rationale [skip ci] * [comment audit] Shorten terminal recovery and benchmark contracts [skip ci] * [comment audit] Condense tooling and Windows lifecycle proof rationale [skip ci] * [comment audit] Condense workflow routing and release rationale [skip ci] * [comment audit] Condense mobile security and installer contracts [skip ci] * [comment audit] Shorten native rationale and stylesheet workarounds [skip ci] * [comment audit] Shorten Windows visibility probe rationale [skip ci] * [comment audit] Condense embedded release and signing rationale [skip ci] * [comment audit] Retain current main test behavior in final reconciliation [skip ci] * [comment audit] Complete final comment review across current source inventory * [comment audit] Handle consolidated audit reports before final CI * [comment audit] Preserve credential storage census detail [skip ci] * [comment audit] Scale broad changed E2E selections with verified timing shards * [comment audit] Align E2E assertions with browser close and settled session metadata * [comment audit] Match existing E2Es to current resource and windowing contracts * [comment audit] Observe the shared server outage notice in reconnect coverage * [comment audit] Make existing renderer probes measure settled tab and PTY work * [comment audit] Align palette E2E selector with task search * [comment audit] Refresh remaining palette search selectors * [comment audit] Keep selector correction within selected audit specs * [comment audit] Await emoji fixture settings inside renderer evaluation * [comment audit] Cancel fixture fit retries between terminal tests --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
89b3d38284 |
test: audit delayed replies and database boundaries (#32426)
* test: audit delayed replies and database boundaries * docs: reconcile the fourth test audit batch --------- Co-authored-by: OrcaWin <293788423+OrcaWin@users.noreply.github.com> |
||
|
|
5a097e7739 |
feat(mobile): decode page-bundle download windows natively (#31817)
* feat(mobile): decode page-bundle download windows natively Each 384 KiB window of the page-bundle download cost about 200-300 ms of Hermes JS time (tweetnacl secretbox open, fflate gunzip, a base64 re-encode check and the TextDecoder polyfill), which kept the JS thread busy for the whole download. The phone now opens v2 frames with react-native-libsodium (secretbox open and strict base64) and decodes ranges with a local Expo module, orca-bundle-window-codec (bounded gunzip via system zlib on iOS and java.util.zip.Inflater on Android, plus strict UTF-8). The shared v2 session takes these as optional primitives; the desktop keeps tweetnacl. If any native piece is missing or fails a known-answer probe, the phone keeps today's JS path, with the base64 re-encode replaced by an O(1) canonical check. * refactor(mobile): name the native decode for every inbound frame, not just bundle windows The native secretbox, base64 and UTF-8 primitives decode every inbound v2 frame on the phone, so the files and the Expo module are renamed for what they cover (paired-e2ee-native-*, orca-gzip-utf8-codec). A throw from the native UTF-8 decode now falls back to TextDecoder, since the frame is already open and its counter advanced. * test(mobile): pin the TextDecoder fallback when native UTF-8 throws * fix(mobile): hand the native decode to the transport from the app entry The transport modules are reached by shared, desktop and e2e code. Importing the native bindings there pulled expo-modules-core into the e2e typecheck, whose global types make NODE_ENV required and broke ~20 e2e files. The app entry now provides the bindings; without them the transport runs the JS path. * fix(mobile): a native-codec lookup before the app provides bindings does not pin the JS path |
||
|
|
0cba9f2e08 |
fix(mobile): render offline Nerd Font symbols on first terminal display (#31446)
Fixes #17168 Supersedes community PR #17220. Original contributor credit is preserved below. Co-authored-by: Arthur Chang <arthurc0102@gmail.com> |
||
|
|
11352d4c0c |
fix(mobile): open the OTA workspace page on iOS 18 (#29197)
WebKit reports no source frame for an app-initiated load() before iOS 18.5 (WebKit c28b242), so the iOS shell read its own document load as subframe-initiated and cancelled it, leaving the phone on "Opening workspace" forever. The navigation policy no longer takes the initiating frame as an input: the shell's own document loads when its one-shot load flag is up, the URL is the exact document URL, the target is the main frame, and it is not a download, matching the Android copy. Verified on an iOS 18.1 simulator (hang before, workspace opens after) and iOS 26.5 (unchanged). Refs #28626, STA-10545 |
||
|
|
a0c46b405f |
feat(jira): load attachment images outside issue Markdown (#28631)
Load Jira attachment images separately from issue Markdown so descriptions and comments stay small while screenshots load lazily and open in the existing lightbox. Downloads begin when an image mounts, capture its owning host, require negotiated image support, and use bounded authenticated byte streams and caches. Older clients retain their inline-image handlers. Keep legacy and cancellable downloads in separate in-flight domains so cancelling a new image reader cannot abort a legacy reader; completed bytes share the bounded cache. Preserve the landed assignee, board, transition/comment queue and request lifetimes. Move unchanged project metadata IPC handlers into their domain module and tighten two inherited focus-check ceilings without changing behavior. Validation:410 tests/54 suites; all changed-source quality stages, lint, format, routing and contract checks; exact final node/web types; independent source review;48/48 fresh hidden Electron checks with226 stable source hashes and nine inspected screenshots. Synthetic transports exercise production clients and UI. Current CI static checks, mobile bundle, wire compatibility, Linux/Windows packaging and Windows serve pass; five test shards were pending at review. Extends the existing attachment work credited to Frank (@lifefloating) and Brennan Benson in PR #8938; existing contribution credits and media remain in this PR. |
||
|
|
9764dc8b97 |
fix(mobile): keep chat drafts and the sidebar reachable on iPad (#26096)
* fix(mobile): keep chat drafts and the sidebar reachable on iPad Chat composer drafts now live in a module-level store keyed by host, workspace and tab, so leaving the session screen no longer drops them. On iPadOS 26 windowed mode the window buttons covered the top-left header controls; a native launch hook adds their height to the root safe area. A hidden sidebar now shows a Show sidebar button in the session header. * fix(mobile): preserve draft edits across chat remounts * test(mobile): type sidebar header fixture as a partial controller |
||
|
|
7b91405547 |
Let in-page links switch sections inside a chat visual (#26729)
* Let in-page links switch sections inside a chat visual * Make in-page links work in chat visuals on mobile and in WebKit The first fix (an about:srcdoc base in the visual) only reached desktop Chromium. On mobile, the page hosting the visual carried a policy with base-uri 'none', and a srcdoc visual inherits its host's policy before its own, so the base was refused and an in-page link still took the visual off its page and showed it as failed. The mobile web shell sends the same rule as a header. Separately, WebKit (iOS, and the web client in Safari) fires the frame's load event for an in-page link inside a sandboxed frame, and both hosts treated any second load as the visual navigating away and retired it. - The mobile host page and the mobile web shell header now allow only an about: base, so the shell's own base applies; the visual's own policy still refuses any base it declares. - Instead of retiring a visual on its second load, the host now asks the visual's shell after every load. A later load must be answered by the same run of the shell within the plugin panel watchdog's 5 s deadline; no answer, or an answer from a new run (a reload), still retires it. - Tests: unit tests for the policy, the answer reader and the frame's keep/retire rules, and a Chromium + WebKit test of both mobile surfaces that runs in the mobile web app CI job. * Keep any answered later load, and say plainly what the check is for The previous commit tagged each run of a visual's wrapper with a random id so a reload would still retire it. The visual shares a window with its wrapper, so it could read or set that id itself, and a reload only reruns the same Orca-written document, which is not an escape. Drop it: after any load past the first, the host asks once, and any answer on this visual's channel within the wait keeps it; silence retires it. - Comments now say the check is a tripwire for a frame gone blank or to another page; the frame guard (desktop), the host page's policy plus the app's load filter (mobile) and the sandbox are the boundaries. - Visuals get their own 5 s wait instead of borrowing the plugin panel watchdog's, because a 1 s wait retired a busy visual in WebKit. - Tests: a reload is now kept; going blank, or dropping the base and following a link, still retires. The CI routing for the shared wrapper is now pinned in the path-classification test. * Load the visual links check's mobile modules only where they can build The unit shards run every config/scripts test with only the root dependencies installed. This check imported a mobile/ module at load, and transforming it reads mobile/tsconfig.json, which extends a package those shards do not have. The transform error could not be sent back to the test worker under Bun, so the whole shard stopped with "DataCloneError: The object can not be cloned". Import the visual shell and the mobile host page inside the dependency gate, as the session-dictation check already does, so the shards skip the file without loading them. The mobile web app job, which installs mobile dependencies, still runs it in full. |
||
|
|
2584ed906e |
feat: play video and music files in mobile previews (#26148)
Play workspace video and music files on mobile using bounded authenticated downloads and native media controls. Adversarial review fixes cover rewritten files and Android policy tests. Includes iOS simulator screenshots and a playback recording in PR #26148. Co-authored-by: ChangJun Park <40492343+ckdwns9121@users.noreply.github.com> Co-authored-by: Lirone Levy <lirone88@outlook.fr> Co-authored-by: dupi <david.li.du@gmail.com> Co-authored-by: John Cusack <5961784+John-Cusack@users.noreply.github.com> |
||
|
|
069eaf5668 |
fix(mobile): iOS shell keeps WebKit text interaction on so page fields take text (#24270)
With isTextInteractionEnabled = false (#21589) WebKit delivered keydown to a focused page field but never inserted text. The page's user-select rule (#24277) now keeps WebKit's selection off long presses. Needs an iOS shell release. |
||
|
|
ba858ee446 |
feat(mobile): the keyboard covers the page like a native screen, and the shell says its height (#23110)
The shell no longer shortens the WebView for the keyboard; it publishes the keyboard height like the safe-area insets, so native's keyboard lift, refit hold and dismiss key run on the page unchanged. Keyboard and inset arithmetic read the shell's OS through a host-os seam. One page-version floor (manifest pageVersion, shell floor 1) replaces per-feature accept negotiation; a page below the floor gets the existing update wall, a desktop with no bundle keeps native screens. iOS shell drops the form accessory bar and its own keyboard observers. Native session screens untouched. |
||
|
|
9cdbc0c128 |
fix(mobile): keep the shell's window insets out of the page WebView (OTA phase C follow-up) (#22549)
* fix(mobile-web): stop the page declaring viewport-fit=cover The shell already pads the WebView out of the status and navigation bars. With viewport-fit=cover, Android's edge-to-edge WebView still reports the window's bar insets through env(safe-area-inset-*), which react-native-safe-area-context on web reads, so every page-side SafeAreaView padded a full bar a second time. Without it env() reads 0 and the shell's pad is the only one. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): keep the shell's window insets out of the page WebView WebView M144+ forwards the window's systemBars and displayCutout insets to CSS env(safe-area-inset-*) for every WebView, and Chromium applies them regardless of viewport-fit. The shell already pads the WebView out of both bars, so every page-side SafeAreaView (expo-router's DefaultNavigator and the session header) padded a bar a second time. M139+ likewise resizes the visual viewport for ime(), which the shell has already done by shortening the WebView. The WebView now sees those three types zeroed, per Android's "zeroing" approach (not CONSUMED, so later changes still reach it). A listener replaces the WebView's own onApplyWindowInsets, so the zeroed set is passed back into it. iOS needs nothing: the WKWebView uses contentInsetAdjustmentBehavior = .never inside the padded shell and reports zero insets. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * docs(mobile-web): say why the page declares no viewport-fit The earlier comment claimed dropping viewport-fit=cover makes env() read 0 on Android; Chromium's WebView applies the safe area regardless of viewport-fit. The page simply never asks to extend under the bars, and the shell owns the safe area. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): keep the page inset zeroing private to the shell view The transformation has no honest JVM test (the builder runs as SDK 0 there and drops every inset type), so it moves into MobileWebShellView.kt as private members instead of standing alone. The listener comment now covers both the P-R listener and the S+ onApplyWindowInsets path it replaces, and the page document's comment says the env() zeroing is Android's; on iOS the padded WKWebView reports none. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb |
||
|
|
3bb9a4e261 |
fix(mobile): keep a painted frame under the page until its first paint (#22264)
* fix(mobile): keep a painted frame under the page until its first paint The shell tore its own frame down the moment a generation was on screen (`MobileWebShellScreen.tsx`, the `ready` branch), and a mounted WebView draws nothing until its document paints. What showed for the whole of the page's boot was the surface behind it with nothing on it: 1.42 s on a cached generation, against a one-frame budget. The page is the only thing that knows when it has a frame, so it says so. It declares `painted` in `ready.reports` and posts the notify after the browser has painted its first commit; the shell holds the same neutral frame it was already painting while it opened the generation, then fades it out. The wait is bounded by the declaration and never by a timer: a generation served by an older desktop declares nothing and is uncovered on `ready`, which is what every shell did before this. iOS painted white rather than nothing: a WKWebView is opaque by default, so the shell's own surface never showed through. It is now transparent, as the Android view already was. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): hold the cover through the compositor handover The page reports the paint its own renderer made; putting that on the app's surface costs another frame or two. A linear fade from the report left two frames of bare surface between the two on an emulator, which is the hole the cover exists to close. Eased in over 220 ms, the cover keeps most of its opacity across that handover: five reopens now show 0-21 ms of bare surface against 102-2043 ms on the build without it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): negotiate the paint report in both directions The page posted `painted` whatever shell it met, and `notify` is a closed union: every shell installed before this answered it with an error frame, once per mount. The shell now advertises the name in `init.accepts` beside the param clear and the client identity, and the page posts only when it was advertised. The declaration in `ready.reports` stays unconditional, because it is an optional field an older reader strips rather than a new opcode, and because the first `ready` — the only one that matters for the first paint — is sent before any `init` has arrived. The accepts list moves into `bridge-init-frame.ts` beside the grants, which is the module that builds the frame carrying it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): read the cover's colour instead of asserting its shape Two gate findings on the round-two head. The cover test reached the background through a cast of the style prop; it now reads it through a checked narrowing, so the test proves the shape it depends on rather than declaring it. `use-mobile-web-shell-bridge.test.ts` stopped typechecking when the bridge args gained `onPagePainted`: its harness is a literal, so a new required handler is a missing property. The probe now counts paints and one case spends the counter, which is what a handler wired only to satisfy a type would not do. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * refactor(mobile): move the cached-generation opening out of the reducer `mobile-web-shell-session.ts` crossed `max-lines` after the merge: the refused- update work and the paint handling both grew it. What comes out is one thing — putting a generation already on disk on screen, and deciding whether this route is one that bundle carries. It is the reducer's cache path and its refused- update path both, and it was already three functions sitting together. `step` goes into a module of its own because the two now share it; a copy in each would be two spellings of one transition, and exporting it from either would point the dependency the wrong way. No behaviour moves: the reducer's table tests are unchanged and the page closure is unchanged at 4,211, since neither new module is reachable from a page route. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): drop the previous document's paint when a new one starts A document that replaced a painted one inside the same mount inherited its `pagePainted`, so the cover lifted before the replacement had drawn anything. The native view already reports `loading`; the screen dropped it. It now reaches the reducer as `document-started` and clears the page document state. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): make the declaration case call the frame policy The case compared the name to itself and never called `shellPageFrame`, so it passed for a policy that ignored the declaration entirely. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): report the page's frame from the route screen, not the router Every route screen is behind `import()`, so the wrapper above expo-router commits with a suspense fallback while the chunk is still arriving. The paint report hung there, which uncovered the shell's view over an empty body on a cold chunk. It now hangs on the screen the manifest resolves, layouts excluded. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): retire the readiness wait a replaced document armed `document-started` cleared the page document state and left the flow alone, so the previous document's readiness deadline passed the flow check, read `pageReady` as false and failed a session whose replacement was still loading. The flow moves with the document, for the reason `remounted` already moves it. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): let a departing route screen take its paint report back The report waits two frames, and nothing cancelled the second one, so a screen unmounted in between still told the shell to uncover. The reporter now answers with a take-back the wrapper returns as its cleanup, and the once-per-document latch frees only when a report was cancelled before it landed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * fix(mobile): let the screen that arrived take over a frame still owed A screen committing inside the two frames an earlier one was owed found the latch taken and reported nothing; the earlier screen then freed that latch on its way out and nobody was left to lift the cover. The newest commit now supersedes the pending report, and only a posted one spends the latch. Covers the redirect window with a render check against the pr route, whose target chunk is held open while the document sits on the hub's fallback. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb * test(mobile): make the take-over case turn on the take-over The case cancelled the first screen's frame through the cleanup path, so it passed with the take-over deleted. It now leaves that screen mounted and reads the clock: the frame after the replacement commits is the replacement's first, not the one the screen behind it was still owed. Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb |
||
|
|
a3c6d4266a |
fix(mobile): admit https: images on the web shell's CSP (OTA phase C, ruling 27) (#21964)
* fix(mobile): admit https: images on the web shell's CSP (OTA phase C, ruling 27)
Native markdown and the native rich editor load images the author referenced
by URL, so the page has to as well or a remote image is a blank where native
paints a picture. `img-src` widens to `img-src 'self' data: https:` on both
platforms; `script-src`, `connect-src`, `object-src`, `frame-src` and
`child-src` do not move.
`http:` stays out, and the pins say so directly rather than by absence: the
Kotlin test's blanket `!contains("http")` could not survive `https:`, so both
native pins now check `http:` (not a substring of `https:`) and check that
`https:` appears in `img-src` and nowhere else, the same shape the `data:`
pin already had.
No behaviour change on released phones: the shell ships in no released tag
(mobile-v0.0.9 predates it), so this reaches devices with the Phase E native
build and not before.
Neither native module has a CI job, so both ran locally: swiftc over the
module plus MobileWebShellChecks, and
`:orca-mobile-web-shell:testDebugUnitTest`. Both were confirmed red against
the old directive first.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): correct what the sealed preview frame is stricter about
The doc comment said the page was deliberately stricter than the native
preview because it loads no remote image and runs no script. Since `img-src`
gained `https:` only the script half is true: the frame loads a remote image
exactly as the native WebView does.
Says instead what an artifact's image URL now is -- a channel that fires on
view and carries whatever its author encoded, with nothing dynamic behind it
because no script runs -- and names `referrerPolicy` as what keeps the
document's own origin out of the request.
Comment only; no behaviour and no test moves.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): measure both halves of the preview frame's image fence
"fetches nothing of the artifact that leaves the origin" stopped being what
the sealed arm proves once `img-src` gained `https:`. The fixture's foreign
origin is `http://127.0.0.1`, so its two images are refused on the scheme
alone and only the font is refused by `font-src 'none'`. Renamed to say
exactly that.
The half that was missing is an https arm. Playwright route interception
answers an `https://…invalid` origin in the page, so the arm needs no TLS
server and no new dependency, and a request only reaches the handler if the
policy let it out. Under the shipped header, on Chromium and WebKit, the
`<img>` and the CSS background are both requested -- `img-src` governs a
background too -- and the font still is not.
`artifact()` takes the subresource origin; the links stay on the cleartext
one so no existing navigation case changes.
Red-first: with `img-src 'self' data:` put back into the parsed Kotlin
policy, the new arm fails on both engines with `expected [] to deeply equal
[ '/css-bg.png', '/img.png' ]`. The directive was restored byte-identical
before this commit.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* refactor(scripts): split the preview frame's settling out of the render check
The https arm pushed mobile-web-app-html-preview-render.test.mjs to 620
counted lines, over the 600 cap config/scripts carries. Split at a module
boundary rather than bumped: the four wait-and-settle functions are rig
mechanics with no assertion in them, and they now sit beside the diagnosis
module they already reported through.
`waitForLoadedFrame` and `settleAfterMount` are the two the render check
calls; `waitForRecordedNavigation` and `settleWithoutNavigation` stay
internal to the new module.
Move only. Same 20 tests pass on both engines.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* fix(mobile): send Referrer-Policy: no-referrer on the shell document
`img-src https:` gave the page somewhere to send a request, and the document
origin is `orca-mobile-web://<sessionId>/`, so a request that carries a
referrer carries the session id to whatever host an artifact or a markdown
document named.
`referrerPolicy="no-referrer"` on the preview iframe does not cover it.
Measured in the render rig against a permissive control policy: WebKit puts
the embedder's URL on a srcdoc frame's image request despite the attribute,
and Chromium sends none. So the guarantee belongs on the document, where one
header covers every request the page makes, and it rides the document alone
with the policy -- the referrer of a request is decided by the document that
made it, so on a subresource response it would govern nothing.
WKWebView under the custom scheme is unverified: the rig is Playwright
WebKit over http, not WKWebView over `orca-mobile-web://`. The header is the
hedge, and it costs nothing if that host never leaked.
Pinned three ways, each confirmed red first:
- Swift, exit 133 with the header removed.
- Kotlin, MobileWebShellResponseHeadersTest "sends the policy on the
document" FAILED at :17 with it removed.
- The rig, through a new `readShellDocumentHeaders` that parses the Kotlin
source the way `readShellCsp` does and throws rather than returning an
empty map. With the value flipped to `unsafe-url` the WebKit arm fails
`expected [ …(2) ] to deeply equal [ null, null ]`; with the line deleted
the parse throws "could not parse the shell document headers".
The rig's arm carries its own presence precondition: a third server serves
the shipped policy with `unsafe-url`, so the WebKit reading is the header
doing the work, and Chromium's null either way is pinned as the browser's
behaviour rather than sold as evidence the header arrived.
MobileHtmlPreview.web.tsx said the iframe attribute kept the origin out of
the request. Corrected to name the header, since the measurement above is
what disproved it.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): quote the current directive where the old text was written down
Three comments still read `img-src 'self' data:`, so a grep for the old
directive found live prose that no longer matches the header. Each stays
about `data:`, which is what those paths rest on; only the quoted policy
changes.
The two remaining hits in the repo are src/main/browser/doc-preview-protocol,
which is the desktop preview's own policy and not this one.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): name the surfaces img-src https: actually unblocks today
The comment justified `https:` with markdown and the rich editor, and
neither renders a remote image on the page. Verified in the tree:
MobileMarkdown paints `` as a tappable link at both of its image
branches and never mounts an Image, and it has no `.web` sibling, so that is
what native does too; MobileRichMarkdownEditor.web.tsx is a 92-line
multiline TextInput, still C7.6's plain source field.
What the directive unblocks today is four surfaces, none of them overridden
on the page:
- MobileAgentIcon's favicon, a hardcoded `google.com/s2/favicons` URL, used
by thirteen callers including the session header and the worktree rows;
- MobileRepoIcon's project icon, a host-named favicon, avatar or upload, on
the worktree list and the host workspace list;
- PRCommentCard's author avatar, from the review reply schema;
- the sealed HTML preview frame, which inherits the policy.
Markdown and the editor are named as the anticipated surfaces ruling 26
points at, so a later reader does not take the loosening as already covering
them. Both native pins carried the same wrong claim and are corrected.
That comment is the only record of why the policy loosened, so it says what
is true now and what is coming, separately.
Comment only: the parsed header is unchanged, checked through the harness
reader the render suite uses.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): point the new source-control route pin at the current directive
Merge resolution, not a conflict git could see. #21957 landed the
source-control and review page routes on main while this branch was open,
and its render check pins the directive text twice: `cspHeader` by substring,
which survives the widening, and the Swift source by the quoted literal
`"img-src 'self' data:"`, which does not. Two PRs green alone, red on the
merge.
Both pins now read the current directive.
One comment goes with it. "Not one request left the origin, so there is
nothing for the policy to have refused" now needs saying why: `https:` is
admitted, so an empty host list is these two closures fetching nothing
rather than the policy refusing something. The avatar that would fetch needs
provider data this page never gets, which the file's own closing note
already explains.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): wait for the admitted images before reading their hits
CI's Chrome 152 recorded the CSS background and not the `<img>` by the time
the bounded settle returned, so both https arms failed on a count: "expected
[ '/css-bg.png' ] to deeply equal [ '/css-bg.png', '/img.png' ]" and
"expected 1 to be 2". The reads were absence-shaped -- two frames and 200 ms
-- and the claim they carry is a presence.
So the arms wait for their own evidence, the way the `'refusal'` arm already
does. `frameReady: 'images'` polls until both admitted paths are recorded,
bounded by nothing but the case's own `ctx.signal`. It sits after the marker
wait, because an image is requested by a document that has parsed, and the
arm hands its reader in rather than the settling module reaching for state
that belongs to an arm.
One reader now serves the wait and the reading. An arm that waits on one
list and asserts on another has proved nothing about the list it asserts on.
The `/probe.woff2` absence is untouched and is now an absence standing
behind two presences rather than beside them.
What the wait prints when it does not arrive, captured by making the paths
unsatisfiable against a 12 s case:
[html-preview-render] the arm recorded ["/img.png","/css-bg.png"] of
["/css-bg.png","/img.png","/never-arrives.png"]; #remote
{"complete":true,"naturalWidth":1,
"currentSrc":"https://artifact-images.invalid/img.png?n=n1",
"loading":null}: arm csp=shipped sandbox=product frameReady=images
nonce=n1 | browser 147.0.7727.15 | ... | frames [...]
`complete` with a zero `naturalWidth` is a request that finished and
produced no image; `complete` false is one still in flight. So a Chrome that
never issues the request says which of those it was, instead of a bare count.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): say why an admitted image never arrived, and hand back the context
CI's Chrome 152 read the `<img>` as complete with a zero naturalWidth and a
resolved currentSrc while the route handler never saw the request, and the
CSS background from the same origin did reach it. The diagnosis could say
the image failed but not why, because nothing was watching the request.
Now four sources are, for the `.invalid` origin only, in a module of their
own so the rig file stays under its cap: `request` says whether the page
asked at all, `requestfailed` carries the browser's `errorText`, and CDP's
`Network.loadingFailed` adds `blockedReason` and `corsErrorStatus`, which is
the only place a refusal names itself once the request never reaches a route
handler. `Network.requestWillBeSent` records the resource type, the initiator
and the frame, which separates an image the parser found from one nothing
asked for. They fill arrays while an arm passes and are only read on abort.
Proved by forcing the abort rather than assuming: with the awaited paths made
unsatisfiable, the reading names the font's refusal in both vocabularies at
once, `failed [{"url":".../probe.woff2","errorText":"csp"}]` and `cdp
loadingFailed [{"errorText":"","blockedReason":"csp",...,"type":"Font"}]`,
beside `cdp sent` showing every request's type, initiator and frameId.
Teardown: `open()` now takes an explicit context and closes both the page and
the context in a `finally`. The close used to sit on the happy path, so an
arm whose wait aborted and whose result reads then raced vitest's teardown
left its page and its implicit context open on a browser every later case in
that engine still runs on.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* docs(mobile): correct three rationales the widening left wrong
(a) A review comment's avatar is not a surface the widening unblocks.
PRCommentCard renders it only under `Platform.OS !== 'web'` and a component
test pins the skip, so on the page it never renders. Dropped from both native
rationales and moved to the anticipated list beside markdown and the editor,
with the reason each is anticipated rather than current.
(b) The Kotlin rationale quoted the iOS origin. Android serves from
`https://<sha256(sessionId) first 32 hex>.orca-mobile-web.invalid/`, so a
referrer there carries a stable per-session handle and not the id itself,
while iOS serves `orca-mobile-web://<sessionId>/` and carries it verbatim.
Both are something an image host can key on across requests, which is what
the header is for; each file now names its own origin.
(c) "Only the script half of that is stricter than native" overstated it.
`font-src 'none'` and `connect-src 'self'` are stricter too. Images are the
one of the four that stopped being stricter, and the comment now says which
three remain and why.
A fourth, found while checking (a): the skip's own comment justified itself
with `img-src` being `'self' data:`, so a provider avatar would be "one
refused request per card". That is no longer true -- the avatar would load
now -- so the skip is a page capability gap rather than a policy consequence.
Recorded as such at the guard. Whether to lift the guard is a ruling-26
question and not this PR's.
Comments only. The parsed policy and document headers are unchanged, checked
through the harness readers the render suite uses.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): probe why Chrome never asks for the artifact image
CI's read was decisive: on Chrome 152 only the CSS background was requested,
while the `<img>` reported complete with a zero naturalWidth and a resolved
currentSrc. A request that went out and failed cannot produce both readings,
so the next probe asks the frame rather than the network.
On abort it now reads, inside the artifact frame: readyState, the init
script's own moment, document.images.length, every
`performance.getEntriesByType('resource')` name, the navigation entry types,
and for #remote its src, isConnected, complete, naturalWidth, currentSrc and
the outcome of decode(). A resource entry for a URL the rig never saw would
mean the request left the frame and died before reaching it.
Then it issues a `new Image()` at a URL that has never existed and reports two
seconds later whether the rig saw it. That splits the two live explanations: if
the fresh request is seen and the artifact's was not, the frame can fetch and
the parser-inserted element is the cause; if neither is seen, requests from
this frame are not reaching the rig at all. Subframe document commits are
counted from mount, because a second parse is a new window and leaves nothing
behind to count, and a second parse could be meeting a failure the first
cached.
`cdp sent` was empty on CI even for a request Playwright did record, so the
page's own session is blind to the frame. Chromium isolates sandboxed iframes
into their own process, srcdoc included, so flattened Target.setAutoAttach now
puts each child target on the same connection with Network.enable on the
child, and the attached list reports whether the frame is a separate target
at all.
The navigation arm gets the same reading, since CI showed it fails on its own
rather than behind the aborted image arms.
Verified by forcing the abort rather than assumed. Locally the reading prints
one subframe parse, decode resolved, every resource the document fetched, and
`fresh ... issued true seen true`, with the attached list empty, which is
consistent with this Chrome not isolating the frame and its page session
seeing the requests.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): time the artifact image against the frame's attachment
CI's second read showed the frame did issue the request -- it has a
resource-timing entry and decode rejected with EncodingError -- while the rig
saw only the CSS background, and a fresh image created later from the same
frame was both issued and seen. The remaining question is whether the entry
starts before anything was listening to that frame.
So the entry is now reported in full for the element under test:
responseStatus, transferSize, encodedBodySize, nextHopProtocol, startTime and
duration. A zero status with a zero transferSize is a fetch that reached the
network stack and came back with nothing, which is what an unintercepted
request looks like once `.invalid` fails to resolve.
Both sides of the comparison get a wall clock: `Target.attachedToTarget` and
Playwright's own `frameattached` now carry the moment they fired, and every
recorded request carries the moment it was seen. An entry that starts before
the attachment is the race stated rather than inferred.
Abort path only; the passing run is unchanged.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): serve the artifact's https assets from a real TLS listener
Interception could not measure what the directive admits. Chrome 152 isolates
the sandboxed srcdoc frame into its own target and the parser-inserted `<img>`
is the document's first fetch, issued before interception attaches there: the
request escaped to the real network, `artifact-images.invalid` did not
resolve, and the rig recorded nothing while the frame's own resource timing
showed the fetch and a later fresh image was both issued and seen.
So the assets come from a listener that is already accepting before the page
exists. It cannot be raced: the request arrives or it does not, and either
answer is the measurement. Hits and referrers are recorded server-side, the
way this rig's cleartext origin already does it, and read per arm by nonce.
`img-src 'self' data: https:` matches on scheme, so `https://127.0.0.1:<port>`
exercises the same directive as any other https host.
Lifecycle: started in beforeAll before any browser, closed in afterAll beside
the other servers. Its certificate is generated per run by openssl into the
suite's own scratch directory under `mobile/.tmp`, which the root gitignore
already covers and into which the server writes a second `.gitignore` as well;
the key never leaves that directory and nothing trusts it, since the context
is created with `ignoreHTTPSErrors`. No arm shares state: one hit list keyed
by each arm's nonce, and the permissive-Referrer-Policy control stays what it
was, a second bundle server serving the page, because the control is the
document's header and not the image host's.
The navigation record moves off interception too. It is now `page.on('request')`,
one subscription over every frame, armed after the rig's own `goto` exactly
where the route used to be registered; the route stays only for what only a
route can do, refuse the navigation. That answers the top-nav arm's `recorded
[]`: its record depended on the same per-target interception.
And the arms stop swallowing their clicks. `click(...).catch(() => {})` made a
tap that never landed and a tap that produced no navigation the same empty
counter; `open()` now records the error and the two top-nav arms assert it is
null before reading any count.
One correction to the reading added in the previous commit. The resource-timing
fields came back zero for a request that had plainly succeeded: they are opaque
cross-origin. The listener now sends `Timing-Allow-Origin`, after which
transferSize, encodedBodySize and nextHopProtocol carry real values.
`responseStatus` still reads zero on a successful request, so the comment names
the three that discriminate rather than the four that are printed.
24/24 on both local engines.
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
* test(mobile): compare the artifact fetch and the attachment on one clock
The early-or-late comparison spanned two clocks and could not answer the
question it was written for. Every `at` in the request log is Node's
`performance.now()`, counting from process start; the resource entry's
`startTime` is the frame's own, counting from that document's navigation. A
frame entry reads as earlier than a Node attachment by roughly the process
uptime, so the comparison would have reported the race as confirmed on every
run, including runs where there was no race. A green CI would not have caught
it.
So the comparison is stated where both numbers actually live: `asked` against
`attached` in the request log, on the Node clock alone. `startTime` and
`duration` stay, labelled as the frame's own account and explicitly not
comparable to an attachment time. The module docstring says the same, so the
next reading added here starts from the rule rather than rediscovering it.
The commit message of
|