Commit Graph
97 Commits
Author SHA1 Message Date
Jinwoo-H b78660541d chore(mobile): split the download-measurement harness under max-lines
Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-03 04:07:21 -04:00
Jinwoo-H b3c416963c Merge branch 'worktree-agent-a5d9fcc87bd472be9' into mobile-rearch
# Conflicts:
#	mobile/app/hybrid.tsx
#	mobile/src/mobile-web/use-mobile-web-package-recovery.ts
2026-09-03 04:07:05 -04:00
Jinwoo-H c6d79aa7eb perf(mobile): pipeline and widen mobile-web package reads
The hybrid app pulled its 9.1 MiB mobile-web package one 48 KiB chunk per RPC
round trip, strictly serially, and 99.3% of the package is a single 9.5 MB
script — so the whole download was 245 sequential round trips against whatever
the relay path's latency happened to be. Measured on the real package over the
real mobile relay client with a 120 ms round trip: 31.7 s (0.30 MB/s).

Two changes, both negotiated:

- the downloader now keeps up to MOBILE_WEB_PACKAGE_MAX_CONCURRENT_READS reads
  in flight across the whole manifest while still draining to the stager in
  offset order (the native stage appends at the file's current length), and
  narrows the window instead of failing when a host answers
  mobile_web_package_read_limited;
- mobileWeb.package.asset.gzip takes an optional `length` so one read answers up
  to eight chunks. The params schema is strict, so older hosts reject the field —
  it is gated on the new mobileWeb.package.range.v1 capability, and the client
  still splits the range into 48 KiB stage writes.

Same package, same harness, relay path: 31.7 s -> 2.8 s at 120 ms RTT and
62.9 s -> 5.5 s at 250 ms RTT, with 245 requests down to 76.

The download still dies when the app is backgrounded past
RELAY_BACKGROUND_GRACE_MS — the session suspends, the refresh effect aborts, and
the stage is discarded — so the progress panel now says to keep the app open.
Resuming from the staged offset needs a native stage-reopen API on both
platforms and is not attempted here.

mobile/scripts/measure-mobile-web-package-download.mjs drives the real
downloader over both the direct and relay mobile clients with an injectable
round trip, which is where every number above comes from.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-03 03:50:20 -04:00
Jinwoo-H 20a499baa5 test(mobile-e2e): name both landmarks when screenshot parity fails
The failure printed only landmarkDelta, which cannot say which side moved. The
two landmarks come from different sources, a native accessibility frame and a
hosted DOM rect, so the absolute pair is what makes the next failure readable.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 02:07:21 -04:00
Jinwoo-H 06a2dd7b5b Revert "read the native landmark after the route settles"
The landmark delta is bit-identical across the run before the change and the
run after it (0.047498512585812364 both times), so re-reading after the settle
changes nothing and the animation rationale was wrong. Reverting rather than
leaving an inert change carrying a disproved explanation.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 02:05:21 -04:00
Jinwoo-H 5e39d8647c test(mobile-e2e): assert session-origin reviewOpen matches native on this host
Opening a changed file from a session-origin Source Control sends
files.openDiff, which needs a renderer notifier. The e2e pairs against
`orca serve --mobile-pairing`, which has none, so the host answers
renderer_unavailable on native and hybrid alike. The journey asserted the diff
tab unconditionally and could never pass.

The native baseline now probes the same session-origin path and records what
the paired host answered. The hosted journey takes whichever arrives first, the
diff route or the bridge error, and pins it against the native result, so the
claim is "hybrid matches native on this host" rather than "hybrid fails". On a
full desktop the diff route wins and reviewOpen.headless records false.

Kept as evidence, not fixed: the hybrid banner reads "Source control action
failed" where native names renderer_unavailable.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 01:45:51 -04:00
Jinwoo-H 4dc2229283 test(mobile-e2e): capture a native journey stop only once its tree repeats
A single accessibility read caught the Tasks list mid-load on one client and a
second, transient xterm helper node on the other, and both read as client
differences. Poll until two consecutive volatile-free reads match, then
screenshot. Re-running the A/B with this in place left the twelve stops
identical except for live GitHub issue content.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 01:35:30 -04:00
Jinwoo-H 909edbb68a fix(mobile-e2e): read the native landmark after the route settles
The host-origin Source Control route animates in, so captureNativeRoute read
its landmark before the 500ms settle and screenshotted after. The parity check
then failed on landmarkDelta.y 0.0475 against a 0.005 budget while the pixels
agreed at 0.0121 against 0.03. Measured on a live hybrid session: the DOM point
and the accessibility point agree to 0.0000064, so the coordinate spaces were
never the problem.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 01:15:12 -04:00
Jinwoo-H 8e668a7531 test(mobile-e2e): drop the retired synthetic-resume gate and add a native A/B capture
The Agent History journeys still asserted "Failed to resume session." for a
page-synthesized resume. mr-gesture-delete removed that gate on purpose, so the
synthetic click now resumes and the wait timed out on the resumed session route.
Both journeys keep the native-touch resume.

Adds a native journey capture that walks the six baseline journeys recording
ordered accessibility labels plus a screenshot per stop, a diff over two
captures, and an ORCA_E2E_MOBILE_METRO_DIR override so an old client checkout
can serve Metro while the paired desktop runtime stays on this one.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 00:42:59 -04:00
Jinwoo-H 3db98b70f7 fix(mobile-e2e): hold the simulator long press across two gesture commands
One `orca emulator gesture` call delivers its whole point list as a single
batch, so the 62-point begin/hold/end sequence landed as a tap: the native
Source Control baseline long-pressed the workspace row, navigated into the
session instead, and failed on a missing "Source Control" control. Split the
press across two commands, hold, poll for the settled control while the touch
is down, and always lift.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-02 00:32:06 -04:00
Jinwoo-H d156826131 refactor(mobile): delete the mobile-web user-gesture window
The hybrid WebView required a recent native-observed touch before a bridge
capability could run. A scroll armed the window, so it gated nothing an
attacker on the first-party page could not already reach, and no peer hybrid
framework (Capacitor, Cordova, RN WebView) gates bridge calls this way.

Removes the gesture module, its React authority hook, the shared requirement
and its census, and all 20 gate sites: native.alert, clipboard write, external
link open, terminal text-scale and custom-key updates, dictation start and
model management, account select and reset-credit consume, agent-history
resume, terminal clipboard paste and image attach, navigation reconnect,
removeHost and terminal-settings route, and both workspace-creation writes.
Each operation now just runs.

The AppState foreground reporting the gesture hook also carried moves to
use-mobile-web-app-foreground-authority. permission_required stays in the
bridge error enum: it parses inbound responses, so narrowing it would break a
new page paired with an older shell.

Drops the G3 gesture-window e2e probe and renames its runner to
run-hosted-ios-webview-app-bound-probe.mjs, which keeps the app-bound probe.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-01 23:49:03 -04:00
Jinwoo-H 75205167a8 fix(mobile): drop the inert app-bound navigation limit from the iOS shell
No WKAppBoundDomains is declared, and an A/B native rebuild showed identical
behavior with and without the flag: the shell's navigation delegate refuses
external navigation either way. The probe script stays as the regression check.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-01 23:34:45 -04:00
Jinwoo-H 1e5774022f Merge branch 'mr-residue' into mobile-rearch 2026-09-01 23:18:00 -04:00
Jinwoo-H c8f7586f54 refactor: strip PR #10179 residue from the mobile rearchitecture branch
#10179 ("bound OOM-prone accumulators") was reverted on main by #10255, but
the hybrid WebView rewrite carried parts of it forward wherever the revert
could not apply cleanly. Remove those, keeping the hybrid work that had
merely adopted them.

Deleted the bounded mobile RPC queues, ledgers, budgets and JSON admission;
the bounded emulator-script readers; the transcript record buffer and
retention window; and the bounded relay/SSH directory and markdown-document
listing modules. Restored main's connection log store, E2EE v2 channel,
daemon spawner and relay error codes, and reverted the transcript readers'
page and drain budgets.

Kept, and rewired onto main's modules: the terminal binary frame path, the
hosted privacy redactions, the transcript file-source plumbing, and the
chunked file and terminal-artifact reads the hosted client depends on.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-01 23:02:11 -04:00
Jinwoo-H 696b83186f test(mobile): probe the hybrid WebView gesture window and app-bound flag
Two security-review questions about the hybrid iOS shell needed measurement
rather than reading: which native touches arm the 5s user-gesture window that
privileged page requests spend, and whether limitsNavigationsToAppBoundDomains
does anything while app.json declares no WKAppBoundDomains key.

The gesture probe drives clipboardWrite, the cheapest gesture-gated mutation,
through the page bridge after each candidate arming action and records whether
the shell granted or denied it. The app-bound probe asks the page to navigate
to an external https origin and records who refuses it: the shell's navigation
delegate raises a native warning banner, while an app-bound refusal would fail
the provisional navigation inside WebKit with no delegate decision.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-01 23:01:06 -04:00
Jinwoo-H 5735189af7 test(mobile): fix hosted iOS e2e harness and root test triage
Hosted iOS security e2e (three branch-only harness bugs):
- `simctl privacy grant` terminates the app; the relaunch cold-resumes
  straight onto the session route, so skip workspace-row activation when
  the hybrid handoff already landed on /session/. Activation failures now
  name the document href.
- Uploaded-path regex fused two abutting orca-paste-*.png paths; use a
  lazy segment quantifier.
- Photos orders by capture date, which addmedia takes from file birth
  time; stage a freshly written fixture copy (copyFile clones birth time
  on APFS) so "last Photo" is the fixture.

Root tests:
- Allowlist walker skips -test-fakes / -test-fixture(s) / .test-support
  sources, which are not mobile call sites.
- Drop the height-only OSC-8 test: main's #17759 clears restored ranges
  on any dimension change and pins it.
- Drop serve-update-handoff-startup-order.test.ts: main pins the same
  order against main-process-preflight.ts.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-01 21:17:38 -04:00
Jinwoo-H 5746d6f1dc test(mobile): arm a native gesture before the hosted alert probe; split the task project client
native.alert is now gesture-gated, so the simulator journey taps the page
before posting the probe. The task project request client sat one line over
its max-lines cap; its project echo check moves to its own module.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
2026-09-01 19:24:33 -04:00
Jinwoo-H b551c47173 fix(mobile): harden hosted web runtime boundaries 2026-08-30 22:31:44 -04:00
Jinwoo-H 66a9d9086e Merge remote-tracking branch 'origin/main' into mobile-rearch
# Conflicts:
#	.gitignore
#	config/scripts/pr-code-change-scope.mjs
#	config/scripts/pr-code-change-scope.test.mjs
#	mobile/app/connection-log.tsx
#	mobile/app/h/[hostId]/index.tsx
#	mobile/app/h/[hostId]/session/[worktreeId].tsx
#	mobile/app/pair-scan.tsx
#	mobile/src/browser/MobileBrowserPane.tsx
#	mobile/src/components/CustomKeyModal.tsx
#	mobile/src/components/NewWorktreeModal.test.tsx
#	mobile/src/components/NewWorktreeModal.tsx
#	mobile/src/components/mobile-rich-markdown-editor-html.ts
#	mobile/src/diagnostics/connection-diagnostics-report.test.ts
#	mobile/src/diagnostics/connection-diagnostics-report.ts
#	mobile/src/tasks/use-smart-workspace-source.ts
#	mobile/src/tasks/worktree-create-capability.ts
#	mobile/src/terminal/use-terminal-live-accessory-input-commit.ts
#	mobile/src/transport/connection-log-buffer.ts
#	mobile/src/transport/direct-rpc-client.ts
#	mobile/src/transport/host-status-gates.ts
#	mobile/src/transport/mobile-relay-rpc-session.ts
#	mobile/src/transport/rpc-client-socket-close-controller.ts
#	mobile/src/transport/rpc-client-socket-factory.ts
#	mobile/src/transport/rpc-client-socket-session.ts
#	src/main/providers/filesystem-provider-contract.ts
#	src/main/providers/ssh-filesystem-provider.ts
#	src/main/runtime/rpc/methods/index.ts
#	src/relay/fs-handler.ts
#	tests/e2e/helpers/docker-ssh-relay-connection.ts
2026-08-29 04:24:00 -04:00
Neil 7ee8b5e1a6 Refactor lower max-lines modules (#16760) 2026-08-27 16:10:51 -07:00
Jinwoo-H d0437be0d1 fix(mobile): launch hosted export without pnpm shim 2026-08-23 21:00:59 -04:00
Jinwoo-H 9e2da765fa feat(mobile): complete hybrid parity hardening 2026-08-23 20:08:01 -04:00
Jinwoo-H 2094dde1f9 test(mobile): harden hybrid parity bounds 2026-08-23 05:48:58 -04:00
Jinwoo-H 7815f3afb3 fix(mobile): restore hybrid cutover parity seams 2026-08-23 05:34:16 -04:00
Jinwoo-H 9b9c76222f Fix iOS host cache root boundary 2026-08-23 05:17:55 -04:00
Jinwoo-H 611428c9d5 Merge remote-tracking branch 'origin/main' into mobile-rearch
# Conflicts:
#	config/reliability-gates.jsonc
#	mobile/app/h/[hostId]/index.tsx
#	mobile/app/h/[hostId]/session/[worktreeId].tsx
#	mobile/app/h/[hostId]/tasks.tsx
#	mobile/app/index.tsx
#	mobile/package.json
#	mobile/pnpm-lock.yaml
#	mobile/src/browser/MobileBrowserPane.tsx
#	mobile/src/components/NewWorktreeModal.test.tsx
#	mobile/src/components/NewWorktreeModal.tsx
#	mobile/src/components/pr-sidebar/PRChecksSection.tsx
#	mobile/src/components/pr-sidebar/PRConflictingFilesSection.tsx
#	mobile/src/components/pr-sidebar/PRSidebarHeader.tsx
#	mobile/src/files/mobile-file-preview-source.test.ts
#	mobile/src/session/QuickCommandsSheet.test.ts
#	mobile/src/session/QuickCommandsSheet.tsx
#	mobile/src/session/mobile-file-tap-open.ts
#	mobile/src/session/mobile-session-route-types.ts
#	mobile/src/session/mobile-terminal-tab-agent.ts
#	mobile/src/session/use-mobile-diff-review-comment-actions.ts
#	mobile/src/session/use-mobile-diff-review-controller.ts
#	mobile/src/session/use-mobile-diff-review-send-actions.ts
#	mobile/src/session/use-mobile-native-chat-controller.ts
#	mobile/src/session/use-mobile-native-chat-drafts.test.ts
#	mobile/src/session/use-mobile-native-chat-drafts.ts
#	mobile/src/session/use-mobile-native-chat-message-send.ts
#	mobile/src/session/use-mobile-session-tabs-reconciliation.ts
#	mobile/src/session/use-quick-commands.ts
#	mobile/src/tasks/smart-source-paste-intent.ts
#	mobile/src/terminal/terminal-path-tap.ts
#	mobile/src/transport/host-removal-lifecycle.ts
#	mobile/src/transport/rpc-client.ts
#	package.json
#	src/main/browser/agent-browser-bridge.test.ts
#	src/main/browser/browser-screencast-stream.ts
#	src/main/daemon/headless-emulator.ts
#	src/main/native-chat/transcript-file-version.ts
#	src/main/native-chat/transcript-incremental-reader.ts
#	src/main/native-chat/transcript-tail-reader.ts
#	src/main/native-chat/transcript-watch-engine.ts
#	src/main/providers/filesystem-provider-contract.ts
#	src/main/providers/ssh-filesystem-provider.ts
#	src/main/providers/types.ts
#	src/main/runtime/orca-runtime-files.test.ts
#	src/main/runtime/orca-runtime-files.ts
#	src/main/runtime/orca-runtime.ts
#	src/main/runtime/rpc/methods/files.ts
#	src/main/runtime/rpc/terminal-multiplex.test.ts
#	src/relay/fs-handler.ts
#	src/renderer/src/components/ui/dialog.tsx
#	src/shared/terminal-quick-commands.ts
2026-08-21 03:46:22 -04:00
Neil 77f23b013f refactor(shared): drop the shared/types barrel and import from the real modules (#14447)
#14397 split `shared/types.ts` into 46 per-domain modules but kept the path as
a re-export barrel so the import sites did not have to change. This removes
the barrel: every consumer now imports from the module that actually declares
the type, and `src/shared/types.ts` is deleted.

Barrels hide where a type lives, make every consumer look like it depends on
the whole domain, and let an unrelated edit invalidate a module that ~2,000
files transitively import.

2,323 import declarations across 2,321 files. Rewritten mechanically: each
specifier was resolved to an absolute path via the TypeScript AST and
recomputed, rather than string-substituted, so alias forms (`@/../../shared/
types`) and per-specifier `type` modifiers survive.

Four cases the mechanical pass had to handle, each found by a gate rather than
by reading the diff:

- Modules inside `src/shared` import the barrel as `./types`, not
  `shared/types`. A pre-filter on the latter string skipped 176 of them and
  left imports dangling at a deleted file, which surfaced as confusing
  `Property 'x' is optional in type 'Repo' but required in Pick<Repo, ...>`
  errors rather than "module not found".
- The barrel RENAMED one type on the way through
  (`WorkspaceSource as WorkspaceCreateTelemetrySource`), so the original name
  in the owning module has to be re-aliased at each consumer.
- Three test files put `;(globalThis as ...)` on the line after the import.
  TypeScript parses that `;` as the import statement's terminator, so
  replacing through `statement.getEnd()` deletes it and breaks ASI. The
  rewrite now stops at the module specifier.
- A file that already imported directly from a module got a SECOND import
  from it, because the barrel re-exported those same names — which trips
  `import/no-duplicates` under `--deny-warnings`. A post-pass merges
  declarations sharing a specifier and type-only-ness; the `import type` plus
  `import` pair from one module is left alone, since that form is allowed.

Splitting one barrel import into several genuinely adds lines, which pushed
`terminal-layout-pty-ownership.ts` to 301 counted lines: its 107-character
import must wrap, and neither local type collapses onto one line (101 and 116
characters). Rather than contort a type declaration to fit a line budget,
`collectLeafIds` and `pruneLeaves` move to `terminal-pane-layout-tree.ts` —
they are pure structural operations on the layout tree and independent of PTY
ownership. `visible-worktrees.ts` similarly loses its own mini-barrel
re-export of `isDefaultBranchWorkspace`, with the four real consumers
repointed at the declaring module. No `max-lines` bypass added.

Verified: cold `tsc --noEmit` green on node, cli, and web (buildinfo deleted
first — these projects are `composite: true` and reuse stale caches); the full
`pnpm lint` green, not just bare oxlint — the narrower local check is what let
the duplicate imports reach CI; max-lines ratchet OK at 344.
2026-08-13 22:48:24 -07:00
Jinwoo-H f7ef96c8c9 Merge remote-tracking branch 'origin/main' into mobile-rearch
# Conflicts:
#	.gitignore
#	mobile/app/h/[hostId]/session/[worktreeId].tsx
#	mobile/src/components/MobileRichMarkdownEditor.tsx
#	mobile/src/components/mobile-rich-markdown-editor-html.ts
#	mobile/src/components/pr-sidebar/MobilePrComposeForm.tsx
#	mobile/src/session/use-mobile-native-chat-message-send.ts
#	src/main/git/runner.ts
#	src/main/native-chat/transcript-tail-reader.ts
#	src/main/runtime/orca-runtime.ts
#	src/main/runtime/rpc/methods/hosted-review.test.ts
#	src/main/runtime/rpc/methods/hosted-review.ts
2026-08-12 16:37:00 -04:00
Jinwoo-H 4846ee9538 fix(ci): restore mobile rearchitecture validation 2026-08-09 19:24:05 -04:00
OrcaWin ce43d114be feat(mobile): cut over to hybrid workspace route 2026-08-09 18:35:02 -04:00
OrcaWin f480eb23c4 test(mobile): harden hosted Android touch activation 2026-08-09 18:35:02 -04:00
OrcaWin 5112aa837d test(mobile): stabilize hosted Android journey 2026-08-09 18:35:02 -04:00
OrcaWin 279a1ff68e test(mobile): complete hybrid adversarial validation 2026-08-09 18:35:02 -04:00
OrcaWin 158cd4a311 test(mobile): harden hosted terminal links 2026-08-09 18:35:01 -04:00
OrcaWin 352fc2e0cb test(mobile): validate hosted image metadata 2026-08-09 18:35:01 -04:00
OrcaWin 32fef856af test(mobile): validate hosted repository files 2026-08-09 18:35:01 -04:00
OrcaWin 00e443fef2 test(mobile): validate hosted adversarial content 2026-08-09 18:35:01 -04:00
OrcaWin edc215d1a3 test(mobile): audit Android hosted privacy 2026-08-09 18:35:01 -04:00
OrcaWin ed31999634 test(mobile): prove automatic package rollback 2026-08-09 18:35:01 -04:00
OrcaWin c12c839e0d test(mobile): audit hosted crash logs 2026-08-09 18:35:01 -04:00
OrcaWin cb86bcc766 test(mobile): audit hosted page privacy 2026-08-09 18:35:01 -04:00
OrcaWin 0a247f9743 fix(mobile): harden native package stores 2026-08-09 18:35:00 -04:00
OrcaWin afe40ac26d chore(mobile): close post-rebase validation 2026-08-09 18:35:00 -04:00
OrcaWin b055ed1303 fix(mobile): reject linked cache writes 2026-08-09 18:34:59 -04:00
OrcaWin 2d2584eb0d fix(mobile): prevent cache cleanup symlink traversal 2026-08-09 18:34:59 -04:00
OrcaWin 0c2e8eb73d fix(mobile): require exact native package json 2026-08-09 18:34:59 -04:00
OrcaWin e2a4b2410e fix(mobile): reject unsafe cache file reads 2026-08-09 18:34:59 -04:00
OrcaWin 6d4228cc44 fix(mobile): require exact activation metadata 2026-08-09 18:34:59 -04:00
OrcaWin 31ad33828c test(mobile): verify hosted executable isolation 2026-08-09 18:34:58 -04:00
OrcaWin c8d7bdc175 test(mobile): verify hosted picker interruption 2026-08-09 18:34:58 -04:00