Skip to content

refactor(mobile): route screen RPC through host operation adapters - #19669

Closed
Jinwoo-H wants to merge 40 commits into
mainfrom
pr1-native-adapters
Closed

Jinwoo-H wants to merge 40 commits into
mainfrom
pr1-native-adapters

Conversation

@Jinwoo-H

@Jinwoo-H Jinwoo-H commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor
Files Added Deleted Net
Test 32 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1805 $\color{#cf222e}{\Huge{\mathbf{−}}}$​231 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​1574
Prod 138 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​4804 $\color{#cf222e}{\Huge{\mathbf{−}}}$​2588 $\color{#1a7f37}{\Huge{\mathbf{+}}}$​2216

Summary

Mobile screens on main call Desktop RPC inline (client.sendRequest('linear.listTeams'), envelope check and cast in the screen). This PR moves every such call in the task, session, file, workspace and account screens behind an operations adapter the screen is handed, so a screen calls taskOperations.linear.listTeams() and never sees the RPC. The native app hands each adapter a socket RpcClient. No hybrid or WebView code is included; a later PR hands the same adapters a different client.

This is PR 1 of the split of #13386. The adapter bodies are carried from that branch and regrouped from 31 interfaces into six.

Shape

Interface Namespaces (methods) Total
HostSessionOperations tab 9, terminal 6, terminalFile 2, file 1, markdown 2, quickCommand 2, browser 10, nativeChat 12 44
HostTaskOperations read 5, preference 3, list 5, detail 5, itemMutation 2, itemReview 5, itemFile 5, linear 9, providerWrite 2, projectRead 8, projectMutation 13, projectFile 4 66
HostWorkspaceCreationOperations flat 27
HostAccountOperations flat 6
HostFileExplorerOperations flat 2
DeviceOperations flat (open external URL) 1

One nativeHost<Domain>Operations(client) factory and one defaultHost<Domain>Operations per domain. Sub-adapters keep their own files. Screen hooks take one prop per domain instead of one per concern. Only adapters with a live consumer are included; the file-preview and workspace-catalog families and three dead methods were removed after review so no unreferenced adapter can drift from the inline call it shadows.

Behavior versus main

Matched exactly, including 82 user-facing fallback strings and 69 per-method timeouts swept individually. Three deliberate exceptions, each commented at the site:

  • worktree.listRetiredNames: a refused read holds the previous registry instead of blanking it, per the contract in src/shared/worktree/retired-name-cache.ts.
  • linear.selectWorkspace: a refused switch surfaces "Failed to select workspace" instead of leaving the picker showing a workspace the host never switched to.
  • A failed envelope with no error.message names the operation instead of rendering the literal word "undefined".

Three bugs in the source branch's adapters were fixed rather than carried: PR label/assignee edits routed to github.updatePR (which drops them), worker-terminal takeover reporting deleted at three send sites, and resolvePrBase/resolveMrBase forwarding params main never sent.

Left inline on purpose

Four calls with no adapter method meaning the same thing, pinned by count in the ratchet: terminal.close in session close actions, files.createFile + files.open in content create, the buffered terminal submit, and worktree.create from a task item. 112 inline calls remain elsewhere in mobile (diff review, AI vault, dictation, browser commands, PR sidebar); the ratchet caps that number so later PRs can only lower it.

Ratchet

mobile/src/transport/screen-rpc-ratchet.test.ts walks every directory under mobile/src and all of mobile/app, matches sendRequest and client.subscribe( by word boundary (so aliasing, bracket access and spacing cannot bypass it), pins every file with a remaining inline call by exact count, and keeps the four deliberate calls pinned.

Verification

  • pnpm --dir mobile typecheck clean
  • pnpm --dir mobile test 525 files, 4264 passed
  • pnpm --dir mobile lint, oxfmt --check clean
  • pnpm run check:code-quality:changed and pnpm run check:react-doctor:changed 0 findings
  • Three whole-screen parity digests re-frozen; declaration and style digests unchanged.

Second adversarial review follow-ups (0aaa70e)

  • Restored the state commit between the two Tasks hydration phases: read.tasksSupported() runs first and the hook commits supported before read.bootstrap() fans out to settings/ui/preflight/linear. A transport failure in the fan-out again renders the screen behind an error banner instead of a permanent spinner. Hook test pins the ordering.
  • Restored the 30 s timeouts on resolve-thread, review-reply, conversation-comment and request-reviewers project mutations (the shared fetchers now accept options).
  • A Linear comments transport failure again fails the detail load; only a refusal envelope yields an empty thread.
  • Reverted five undisclosed drifts to main behavior: github.listWorkItems timeout, clear-terminal toast on refusal, SSH badge on a null state answer, four error strings, legacy file-inventory cache reset.
  • Removed remaining dead surface: host-workspace-host-context.ts, five uncalled tab methods, three uncalled account methods, the uncalled nativeChat.openFile backing, and the device module whose only consumer was one of those account methods.
  • Restored four test assertions the extraction had weakened (Project host oracle, per-method prRepo, smart-source fan-out stamping, four unpinned RPC names), each verified to fail under mutation.

Third adversarial review follow-ups (5cbbadf)

  • Bounded the Project host oracle to the enclosing adapter method (a neighbour's host could previously satisfy it); verified by deleting updateComment's host, which now fails the test.
  • Removed the last adapter methods with no live caller: native-chat sendMessage/respond/attachImage (send paths call the source module directly, as on main) and workspace-creation createBlankWorkspace/createWorkspaceFromSource, plus a dead type re-export.
  • Known coverage gap, not a behavior gap: the session-route parity reader follows bare identifiers, so three device-token identity expressions that now live inside the terminal adapter are pinned by that adapter's tests rather than the parity family (11 vs 14 on main). The values are unchanged.

Mechanical census (892c6fc)

Run by the lead on the final head, not by review judgment:

  • every added non-test file has at least one non-test importer (67 files, 0 orphans; removed 21 that had none: the capability projection and 20 one-line per-namespace default factories)
  • every method declared on an adapter interface has a caller outside the adapter files (24 interfaces, 114 methods, 0 without)
  • no non-mobile file touched, no hybrid identifier added, no lint disable or @ts-ignore, no as any outside tests, no config or lockfile change

Fourth review follow-ups (59e5e6e)

Reverted to main: refused tab close prunes on ok alone with no result-body precondition; the Stop escape sends on any remaining budget (no 2 s floor); quick-command envelope check sits outside the cutover retry; the four Project-row guards use main's conditions; the read adapters dereference as main did instead of defaulting. Deleted: a Ctrl+U-prefix send option this PR had added with no caller, two create fields with no producer or consumer, the terminal subscribe adapter method (the hook still subscribes directly, as on main), and the remaining interface members with no reader. Three files are now byte-identical to main. The session-route parity oracle pins 14 identity bindings again by following adapter property calls.

Fourth disclosed change: in use-mobile-native-chat-stop.ts the active-route ref is written in a useLayoutEffect instead of during render, because check:react-doctor:changed rejects ref writes in render. Under a discarded render the ref holds the last committed route rather than the last rendered one.

Sixth review follow-ups (different model)

A Codex (gpt-6-astra) whole-diff read of bcba08b found three parity regressions the five earlier reviews had passed. All fixed on this head; each has a test that fails with the fix reverted.

  • Stale legacy file inventory. Main's @ file-search hook bumped a generation on workspace reset and ignored a files.list completion from an older generation; the adapter had dropped that guard, so a slow inventory started before leaving a workspace could repopulate that workspace's cache after returning. Restored per workspace, with main's identity-guarded in-flight cleanup.
  • Project mutation result acceptance. Main's add-comment required a truthy result.ok; every other Project site did result.ok === false on a dereferenced result, so a null result threw before any optimistic commit. The adapter's shared reader had accepted null as success. Restored per method with a Project-specific reader; sendGithubPrMutation in github-pr-mutations.ts is unchanged.
  • Linear detail settlement order. Main awaited both raw requests, then checked the issue envelope, then comments. The adapter had thrown inside Promise.all, so a comments transport failure no longer won over an issue refusal. Restored.

Also from that review: removed the top-level fallback fields the detail projection accepted beyond item; list results are typed as the raw Omit<…,'repoId'|'repoName'> items main used, with the compensating casts gone; three stale test fixtures corrected; the obsolete clipboard/haptics mocks and two history comments removed; the phantom cursor field and a duplicate repository type removed in favor of MobileWorkspaceRepo.

Not changed, disclosed: the operation-target types carry unused placeholder fields; the five default factories and the terminal-response-lane wrapper stay; request-only leaf adapters are inconsistently typed as RpcClient vs RpcRequestSender; the ratchet is a lexical count, not a semantic census. The extraction covers the session, task, workspace-creation, account and file-explorer domains; 112 inline calls remain elsewhere in mobile and are pinned by count.

Round-6 history note: commits 1ec6db4 through 76c8fc3 were produced by a worker whose parallel sub-agents overwrote each other (one left a duplicate declaration); be4a2da through 3d2c11e repair them fix-forward. No history was rewritten.

Six operations interfaces the mobile screens will be handed instead of an
RpcClient: HostSessionOperations, HostTaskOperations, HostWorkspaceOperations,
HostFileOperations, HostAccountOperations and DeviceOperations. Each host domain
groups its per-concern contracts as namespaces so a screen takes one prop while
every sub-adapter keeps its own file, and each has one native factory built over
the socket RpcClient.

No screen is wired yet, so this carries no behavior change.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The Tasks screens and the new-workspace composer now take an operations
adapter instead of an RpcClient. Every RPC envelope check, response cast and
payload literal that lived in a hook body moved into the matching
native-host-task-* adapter, and the composer's search and create requests moved
into HostWorkspaceCreationOperations.

Behavior is unchanged apart from four adapter-owned differences noted in the PR
body. `worktree.create` from a task item stays inline: the creation adapter
builds the composer's params and adds name-collision retry, which is a
different request, not the same one behind a seam.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…rations

The session hooks take HostSessionOperations, the file explorer takes
HostFileExplorerOperations, and the accounts route takes HostAccountOperations.
Terminal, tab, markdown, file, quick-command, browser and native-chat requests
now live in their native-host-session-* adapters.

Four session RPCs stay inline because no adapter method means the same thing:
terminal close, note creation's two calls, and the buffered terminal submit that
needs the raw response to classify a partial write. Worker-terminal takeover
reporting is re-issued at the call sites the adapter does not report from, so
the send-site ratchet still covers all of them.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Pins the screens this PR routed at zero inline `sendRequest`, pins the four
calls deliberately left inline at their exact count, and caps the total
screen-side call sites so the remaining debt can only shrink.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
An adapter body has to be the inline call it replaced, so each place where a
carried adapter answered differently from main is corrected here:

- Linear: per-method timeouts and error strings as main sent them, no result
  check on selectWorkspace or updateIssue, "Sub-issue not found" and
  "Failed to create sub-issue" restored.
- browser.tabCreate: the host's error message, main's 30s budget, and a
  tolerated missing page id.
- terminal.clearBuffer: an ok:false reply keeps main's success toast.
- Quick commands: only the load retries a logical cutover, and an empty error
  message falls back to main's per-operation copy.
- Project rows: the pull-request mutations no longer require a repository slug;
  a row without one forwards prRepo: null, as main did.
- worktree.listRetiredNames: a refused read reads as an empty registry again.
- repo.hooks: the create sheet keeps its state on a refused read, while task
  create still fails on one.

The paced-Escape route ref moves from render into a layout effect so the
changed-code React Doctor gate passes; a timer cannot observe the ref between
commit and layout, so the guard's timing is unchanged.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Two refusals the old inline calls dropped are now reported, because dropping
them is a bug rather than behavior worth preserving:

- linear.selectWorkspace: a refused switch reaches the picker's error instead
  of leaving it showing a workspace the host never selected.
- worktree.listRetiredNames: a refused read fails rather than reading as an
  empty registry, so the create sheet keeps its previous answer instead of
  offering a name that is already spent. This is the policy
  src/shared/worktree/retired-name-cache.ts documents.

Every other adapter still answers exactly as the call it replaced did.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
@greptile-apps

greptile-apps Bot commented Sep 9, 2026 •

Copy link
Copy Markdown

Too many files changed for review (170 files, 100 file limit).

Bypass the limit by tagging @greptile-apps to review.

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — minor suggestions inline.

Reviewed changes

This run reviewed the full refactor across 203 files: the six host-operation adapter groups (session, task, workspace, file, account, device) and their native/default factories, the ~45 screens and hooks re-routed off inline sendRequest, three deliberate behavior exceptions, three carried-branch bug fixes, and the new screen-RPC ratchet.

  • Adapter interfaces + native factories — one Host<Domain>Operations per domain with a nativeHost<Domain>Operations(client) factory; defaultHost<Domain>Operations delegates to native (a later PR hands a different client for hybrid/WebView).
  • Screen re-routing — task/session/file/workspace/account screens now call e.g. taskOperations.linear.listTeams(); envelope checks and casts move behind the seam. Adapters mostly delegate to pre-existing helpers (subscribeMobileTerminalSafely, sendMobileNativeChatMessageWithOutcome, fetchMergePR, …), keeping parity risk low.
  • Deliberate exceptions + bug fixes — worktree.listRetiredNames and linear.selectWorkspace now surface refusals; PR label/assignee edits route to the issue endpoint; worker-terminal takeover reporting restored at three send sites; resolvePrBase/resolveMrBase params corrected.
  • Ratchet — screen-rpc-ratchet.test.ts pins 45 routed screens at zero inline sendRequest(, pins four deliberate inline calls by count, and caps the screen-side total at 112.

ℹ️ The ratchet only closes the sendRequest seam, not subscribe

The screen-RPC ratchet counts sendRequest( occurrences and nothing else, so the "screen never sees the RPC" goal is only enforced for request/response calls. Several session/worktree screens still subscribe to host streams inline — use-mobile-session-tabs-reconciliation.ts, use-mobile-structured-agent-state.ts (agentSession.subscribe), use-live-worktree-name.ts, and host-worktree-refresh.ts — and a regrown inline client.subscribe(...) would pass the ratchet unnoticed. For the follow-on hybrid PR that hands the adapters a non-socket client, these inline subscriptions are the same seam leak sendRequest had.

Technical details
# Ratchet covers sendRequest only

## Affected sites
- mobile/src/transport/screen-rpc-ratchet.test.ts — `countCalls` matches `/sendRequest\(/g` only.

## Required outcome
- Decide whether inline `client.subscribe(...)` in screens is in scope for the "never sees the RPC" goal; if so, extend the ratchet (or the adapter surface) to cover `subscribe` before the hybrid PR relies on the seam being closed.

## Open questions for the human
- Are the remaining inline `subscribe` calls (tab-strip reconciliation, agent-session state, live worktree name, worktree refresh) meant to be routed in a later PR, or are they out of scope because they read streams rather than issue requests?

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@coderabbitai

coderabbitai Bot commented Sep 9, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

The PR routes mobile account, workspace, file, session, and task interactions through typed host-operation adapters. Native adapters centralize RPC mapping, response validation, fallbacks, caching, and timeouts. Screens and hooks now consume operation namespaces instead of raw RPC clients. Tests cover adapter contracts, legacy fallbacks, operation caching, hydration phases, parity counts, and remaining inline RPC calls.

Priority: ⬇️ Low

Merge Risk: 🟡 Moderate · up to 5cbba

The mobile adapter refactor preserves most routing behavior, but several failure paths can report success or surface unexpected errors when host requests are refused or malformed. These cases can leave task, review, browser, and workspace state misleading, so the remaining issues should be addressed before merge.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 14.06% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 128 functions across 91 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title clearly and concisely describes the main change: routing mobile screen RPC calls through host operation adapters.
Description check ✅ Passed The description is detailed and on-topic. It explains the refactor, rationale, architecture, preserved behavior, follow-up fixes, and verification results. The required Linked Issue and Visual Proof s…
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (4)
mobile/src/session/native-host-session-quick-command-operations.ts (1)

38-56: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Make loadWithCutoverRetry abort-aware.

signal is checked only after client.sendRequest settles. The resolved RpcClient.sendRequest contract accepts timeoutMs, budgetSpansConnect, and failWhenDisconnected, but not AbortSignal. The request tracker otherwise waits for a response, connection close, cutover, or its 30-second timeout. Therefore, an aborted snapshot can remain pending until the timeout. Add abort support to the RPC transport and pass signal through the request tracker.

mobile/src/session/host-session-native-chat-operations.ts (1)

39-44: 🗄️ Data Integrity & Integration | 🔵 Trivial | 💤 Low value

Remove the redundant onError parameter.

MobileRelayRpcStreams.fail sends an error frame through the subscription listener, and use-mobile-native-chat-session handles that frame. Transcript failures are not silent. The required onError callback is unused; remove it from the contract and call.

mobile/src/tasks/github-project-host-routing-source.test.ts (1)

35-38: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Make the host assertion request-scoped and type-aware.

The 700-character slice can cross into another RPC. The bare payload alternative can then satisfy the assertion without proving the current request carries a host. The listAccessible request also uses { host } shorthand, and several other requests pass payload types that declare host.

Bound the slice to the next github.project.* literal. Match { host }, explicit host: fields, and the known host-bearing payload types. Do not use hostPayload(; that helper does not exist.

mobile/src/tasks/native-host-task-linear-operations.ts (1)

31-37: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Handle refused linear.updateIssue mutations.

linear.updateIssue returns { ok: false, error } when Linear rejects the update. The local request helper returns that result without throwing. Wrap the result with assertMutation so updateState rejects instead of reporting success.

🛠️ Proposed change
     async updateState(target, stateId) {
-      await request(client, 'linear.updateIssue', {
-        id: target.issueId,
-        workspaceId: target.workspaceId,
-        updates: { stateId }
-      })
+      assertMutation(
+        await request(client, 'linear.updateIssue', {
+          id: target.issueId,
+          workspaceId: target.workspaceId,
+          updates: { stateId }
+        }),
+        'Failed to update issue state'
+      )
     },

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: b152222a-100a-44e4-aab9-fc347979a91a

📥 Commits

Reviewing files that changed from the base of the PR and between 98df62d and 654db50.

📒 Files selected for processing (203)
  • mobile/app/h/[hostId]/accounts.tsx
  • mobile/src/accounts-route-reset-credit.test.ts
  • mobile/src/accounts/default-host-account-operations.ts
  • mobile/src/accounts/host-account-operations.ts
  • mobile/src/accounts/native-host-account-operations.ts
  • mobile/src/components/NewWorktreeModal.tsx
  • mobile/src/components/NewWorktreeModalDrawers.tsx
  • mobile/src/components/SmartWorkspaceSourceDrawer.tsx
  • mobile/src/components/use-new-workspace-execution-target.ts
  • mobile/src/components/use-new-workspace-repositories.ts
  • mobile/src/components/use-new-workspace-runtime-context.ts
  • mobile/src/components/use-new-workspace-setup-script.ts
  • mobile/src/device/default-device-operations.ts
  • mobile/src/device/device-operations.ts
  • mobile/src/device/native-device-operations.ts
  • mobile/src/files/MobileFileExplorerPanel.tsx
  • mobile/src/files/default-host-file-explorer-operations.ts
  • mobile/src/files/default-host-file-operations.ts
  • mobile/src/files/default-host-file-preview-operations.ts
  • mobile/src/files/host-file-explorer-operations.ts
  • mobile/src/files/host-file-operations.ts
  • mobile/src/files/host-file-preview-operations.ts
  • mobile/src/files/native-host-file-explorer-operations.ts
  • mobile/src/files/native-host-file-operations.ts
  • mobile/src/files/native-host-file-preview-operations.ts
  • mobile/src/session/MobileSessionSheets.tsx
  • mobile/src/session/QuickCommandsSheet.tsx
  • mobile/src/session/default-host-session-browser-operations.ts
  • mobile/src/session/default-host-session-file-operations.ts
  • mobile/src/session/default-host-session-markdown-operations.ts
  • mobile/src/session/default-host-session-native-chat-operations.ts
  • mobile/src/session/default-host-session-operations.ts
  • mobile/src/session/default-host-session-quick-command-operations.ts
  • mobile/src/session/default-host-session-tab-operations.ts
  • mobile/src/session/default-host-session-terminal-file-operations.ts
  • mobile/src/session/default-host-session-terminal-operations.ts
  • mobile/src/session/github-pr-mutations.ts
  • mobile/src/session/host-session-browser-operations.ts
  • mobile/src/session/host-session-file-operations.ts
  • mobile/src/session/host-session-markdown-operations.ts
  • mobile/src/session/host-session-native-chat-operations.ts
  • mobile/src/session/host-session-operations.ts
  • mobile/src/session/host-session-quick-command-operations.ts
  • mobile/src/session/host-session-runtime-capabilities.ts
  • mobile/src/session/host-session-tab-operations.ts
  • mobile/src/session/host-session-terminal-file-operations.ts
  • mobile/src/session/host-session-terminal-operations.ts
  • mobile/src/session/mobile-file-tap-open.test.ts
  • mobile/src/session/mobile-file-tap-open.ts
  • mobile/src/session/mobile-native-chat-open-file.test.ts
  • mobile/src/session/mobile-native-chat-open-file.ts
  • mobile/src/session/mobile-native-chat-operation-target.ts
  • mobile/src/session/mobile-native-chat-send.ts
  • mobile/src/session/mobile-session-route-parity.test.ts
  • mobile/src/session/mobile-worker-takeover-send-sites.test.ts
  • mobile/src/session/native-host-session-browser-operations.ts
  • mobile/src/session/native-host-session-file-operations.ts
  • mobile/src/session/native-host-session-markdown-operations.ts
  • mobile/src/session/native-host-session-native-chat-operations.test.ts
  • mobile/src/session/native-host-session-native-chat-operations.ts
  • mobile/src/session/native-host-session-operations.ts
  • mobile/src/session/native-host-session-quick-command-operations.ts
  • mobile/src/session/native-host-session-tab-operations.test.ts
  • mobile/src/session/native-host-session-tab-operations.ts
  • mobile/src/session/native-host-session-terminal-file-operations.ts
  • mobile/src/session/native-host-session-terminal-operations.test.ts
  • mobile/src/session/native-host-session-terminal-operations.ts
  • mobile/src/session/use-mobile-file-tap-handlers.test.ts
  • mobile/src/session/use-mobile-file-tap-handlers.ts
  • mobile/src/session/use-mobile-native-chat-controller.ts
  • mobile/src/session/use-mobile-native-chat-file-search.test.ts
  • mobile/src/session/use-mobile-native-chat-file-search.ts
  • mobile/src/session/use-mobile-native-chat-readability.test.ts
  • mobile/src/session/use-mobile-native-chat-readability.ts
  • mobile/src/session/use-mobile-native-chat-session-lane.ts
  • mobile/src/session/use-mobile-native-chat-session.test.ts
  • mobile/src/session/use-mobile-native-chat-session.ts
  • mobile/src/session/use-mobile-native-chat-stop.test.ts
  • mobile/src/session/use-mobile-native-chat-stop.ts
  • mobile/src/session/use-mobile-native-chat-terminal-response-lane.ts
  • mobile/src/session/use-mobile-session-close-actions.ts
  • mobile/src/session/use-mobile-session-content-create-actions.ts
  • mobile/src/session/use-mobile-session-document-readers.ts
  • mobile/src/session/use-mobile-session-feedback-capabilities.ts
  • mobile/src/session/use-mobile-session-file-actions.ts
  • mobile/src/session/use-mobile-session-markdown-actions.ts
  • mobile/src/session/use-mobile-session-native-chat-dictation.ts
  • mobile/src/session/use-mobile-session-screen-state.ts
  • mobile/src/session/use-mobile-session-terminal-input.ts
  • mobile/src/session/use-mobile-session-terminal-runtime.ts
  • mobile/src/session/use-mobile-session-terminal-send-actions.ts
  • mobile/src/session/use-mobile-session-terminal-stream-display.ts
  • mobile/src/session/use-quick-commands.test.ts
  • mobile/src/session/use-quick-commands.ts
  • mobile/src/tasks/composer-source-base-resolve.ts
  • mobile/src/tasks/default-host-task-detail-operations.ts
  • mobile/src/tasks/default-host-task-item-file-operations.ts
  • mobile/src/tasks/default-host-task-item-mutation-operations.ts
  • mobile/src/tasks/default-host-task-item-review-operations.ts
  • mobile/src/tasks/default-host-task-linear-operations.ts
  • mobile/src/tasks/default-host-task-list-operations.ts
  • mobile/src/tasks/default-host-task-operations.ts
  • mobile/src/tasks/default-host-task-preference-operations.ts
  • mobile/src/tasks/default-host-task-project-file-operations.ts
  • mobile/src/tasks/default-host-task-project-mutation-operations.ts
  • mobile/src/tasks/default-host-task-project-read-operations.ts
  • mobile/src/tasks/default-host-task-provider-write-operations.ts
  • mobile/src/tasks/default-host-task-read-operations.ts
  • mobile/src/tasks/github-project-host-routing-source.test.ts
  • mobile/src/tasks/github-task-detail-projection.ts
  • mobile/src/tasks/host-task-detail-operations.ts
  • mobile/src/tasks/host-task-item-file-operations.ts
  • mobile/src/tasks/host-task-item-mutation-operations.ts
  • mobile/src/tasks/host-task-item-review-operations.ts
  • mobile/src/tasks/host-task-linear-operations.ts
  • mobile/src/tasks/host-task-list-operations.ts
  • mobile/src/tasks/host-task-operations.ts
  • mobile/src/tasks/host-task-preference-operations.ts
  • mobile/src/tasks/host-task-project-file-operations.ts
  • mobile/src/tasks/host-task-project-mutation-operations.ts
  • mobile/src/tasks/host-task-project-payloads.ts
  • mobile/src/tasks/host-task-project-read-operations.ts
  • mobile/src/tasks/host-task-provider-payloads.ts
  • mobile/src/tasks/host-task-provider-write-operations.ts
  • mobile/src/tasks/host-task-read-operations.ts
  • mobile/src/tasks/host-task-runtime-payloads.ts
  • mobile/src/tasks/mobile-tasks-filter-pickers.tsx
  • mobile/src/tasks/mobile-tasks-mutation-targets.ts
  • mobile/src/tasks/mobile-tasks-project-row-targets.ts
  • mobile/src/tasks/mobile-tasks-refactor-parity.test.ts
  • mobile/src/tasks/native-host-task-detail-operations.ts
  • mobile/src/tasks/native-host-task-item-file-operations.ts
  • mobile/src/tasks/native-host-task-item-mutation-operations.ts
  • mobile/src/tasks/native-host-task-item-review-operations.ts
  • mobile/src/tasks/native-host-task-linear-operations.ts
  • mobile/src/tasks/native-host-task-list-operations.ts
  • mobile/src/tasks/native-host-task-operation-contracts.test.ts
  • mobile/src/tasks/native-host-task-operations.ts
  • mobile/src/tasks/native-host-task-preference-operations.ts
  • mobile/src/tasks/native-host-task-project-file-operations.ts
  • mobile/src/tasks/native-host-task-project-mutation-operations.ts
  • mobile/src/tasks/native-host-task-project-read-operations.ts
  • mobile/src/tasks/native-host-task-provider-write-operations.ts
  • mobile/src/tasks/native-host-task-read-operations.ts
  • mobile/src/tasks/native-host-task-review-comment-addressing.test.ts
  • mobile/src/tasks/setup-hook-trust.ts
  • mobile/src/tasks/smart-source-fan-out.test.ts
  • mobile/src/tasks/smart-source-fan-out.ts
  • mobile/src/tasks/smart-source-paste-concurrency.test.ts
  • mobile/src/tasks/smart-source-paste-intent.test.ts
  • mobile/src/tasks/smart-source-paste-intent.ts
  • mobile/src/tasks/smart-source-search-requests.ts
  • mobile/src/tasks/source-workspace-create.ts
  • mobile/src/tasks/use-mobile-tasks-client-settings-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-github-check-file-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-github-reply-merge-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-gitlab-github-status-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-item-detail-loading.tsx
  • mobile/src/tasks/use-mobile-tasks-item-detail-metadata-effects.tsx
  • mobile/src/tasks/use-mobile-tasks-linear-item-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-list-and-detail-effects.tsx
  • mobile/src/tasks/use-mobile-tasks-project-detail-loading.tsx
  • mobile/src/tasks/use-mobile-tasks-project-file-merge-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-project-loading-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-project-metadata-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-project-metadata-loading.tsx
  • mobile/src/tasks/use-mobile-tasks-project-repository-resolution.tsx
  • mobile/src/tasks/use-mobile-tasks-project-review-check-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-project-thread-reply-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-project-workspace-comment-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-provider-load-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-route-and-item-state.tsx
  • mobile/src/tasks/use-mobile-tasks-runtime-hydration.tsx
  • mobile/src/tasks/use-mobile-tasks-task-create-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-task-list-loading.tsx
  • mobile/src/tasks/use-mobile-tasks-task-pagination-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-workspace-create-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-workspace-source-effects.tsx
  • mobile/src/tasks/use-mobile-tasks-workspace-sparse-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-workspace-ssh-state.tsx
  • mobile/src/tasks/use-smart-workspace-source.ts
  • mobile/src/tasks/worktree-create-capability.ts
  • mobile/src/tasks/worktree-create-retry.ts
  • mobile/src/terminal/terminal-input-connection-gate.test.ts
  • mobile/src/transport/rpc-client.ts
  • mobile/src/transport/screen-rpc-ratchet.test.ts
  • mobile/src/worktree/default-host-workspace-catalog-operations.ts
  • mobile/src/worktree/default-host-workspace-creation-operations.ts
  • mobile/src/worktree/default-host-workspace-operations.ts
  • mobile/src/worktree/host-workspace-catalog-operations.ts
  • mobile/src/worktree/host-workspace-creation-operations.ts
  • mobile/src/worktree/host-workspace-host-context.ts
  • mobile/src/worktree/host-workspace-operations.ts
  • mobile/src/worktree/native-host-workspace-catalog-operations.test.ts
  • mobile/src/worktree/native-host-workspace-catalog-operations.ts
  • mobile/src/worktree/native-host-workspace-creation-operations.test.ts
  • mobile/src/worktree/native-host-workspace-creation-operations.ts
  • mobile/src/worktree/native-host-workspace-creation-read-operations.ts
  • mobile/src/worktree/native-host-workspace-creation-source-operations.ts
  • mobile/src/worktree/native-host-workspace-operations.ts
  • mobile/src/worktree/rpc-workspace-creation-operations.ts
  • mobile/src/worktree/use-retired-worktree-names.test.tsx
  • mobile/src/worktree/use-retired-worktree-names.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment on lines +76 to +88
const sourceTabId =
terminalHandle ?? activation.sourceTerminalHandle ?? activation.sourceSessionTabId ?? ''
// Why: resolution opts into sibling-workspace hits; this caller honors resolved.workspaceId.
const resolved = await options.operations.resolveTerminalPath({
workspaceId: options.worktreeId,
tabId: sourceTabId,
terminalHandle,
pathText: options.pathText,
cwd,
nativeChatContext: options.nativeChatContext ?? null,
line: options.line,
column: options.column
})

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
# Check every implementation of resolveTerminalPath for tabId usage.
rg -n -C 12 'resolveTerminalPath' --glob 'mobile/src/session/*terminal-file-operations*.ts'
rg -n 'tabId' --glob 'mobile/src/session/*terminal-file-operations*.ts'

Repository: stablyai/orca

Length of output: 4712


🏁 Script executed:

#!/bin/bash
printf '%s\n' '--- repository conventions and learnings ---'
# No repository-knowledge capability is needed for this narrow contract check.
printf '%s\n' '--- mobile caller and request contract ---'
sed -n '55,100p' mobile/src/session/mobile-file-tap-open.ts
sed -n '20,45p' mobile/src/session/host-session-terminal-file-operations.ts
printf '%s\n' '--- native adapter ---'
sed -n '12,32p' mobile/src/session/native-host-session-terminal-file-operations.ts
printf '%s\n' '--- all files.resolveTerminalPath bindings and tabId references ---'
rg -n -C 8 "files\.resolveTerminalPath|resolveTerminalPath|tabId" mobile src --glob '*.ts' --glob '*.tsx' | head -n 240

Repository: stablyai/orca

Length of output: 20560


Forward tabId in the native adapter. openMobileFileTapAsync supplies sourceTabId, but nativeHostSessionTerminalFileOperations.resolveTerminalPath omits request.tabId from files.resolveTerminalPath. A structured chat tab without a terminal handle therefore reaches the resolver without its session-tab anchor.

{ worktree: `id:${workspaceId}`, relativePath },
{ timeoutMs: 15_000 }
)
if (!response.ok || !(response.result as RuntimeFileOpenResult).opened) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard the result shape before reading opened.

If a host/version-skew response has ok: true with a null or undefined result, this access throws a TypeError instead of the adapter's file_open_failed error. Use a null-safe check.

🐛 Proposed fix
-      if (!response.ok || !(response.result as RuntimeFileOpenResult).opened) {
+      if (!response.ok || (response.result as RuntimeFileOpenResult | null)?.opened !== true) {
         throw new Error('file_open_failed')
       }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if (!response.ok || !(response.result as RuntimeFileOpenResult).opened) {
if (!response.ok || (response.result as RuntimeFileOpenResult | null)?.opened !== true) {

{
repo: `id:${target.repoId}`,
prNumber: target.number,
updates: { title: updates.title, body: updates.body }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Route GitHub pull-request label and assignee changes through github.updateIssue.

When updateMetadata receives a GitHub pull-request target, this branch sends only title and body to github.updatePR. That RPC accepts only those fields. Send addLabels, removeLabels, addAssignees, and removeAssignees through github.updateIssue. Coordinate both requests when one update contains title/body and label or assignee changes.

Comment on lines +126 to +134
const result = response.result as {
ok?: boolean
error?: string
comment?: DetailComment
}
if (result.ok === false) {
throw new Error(result.error ?? fallback)
}
return result.comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Default nullable mutation results before reading ok.

If a review-mutation RPC returns { ok: true, result: null }, mutationComment and assertMutation can throw a TypeError when they read result.ok. The shared RPC contract accepts this success shape. Default the result to an empty object in both helpers:

🐛 Proposed fix: default the result body
-  const result = response.result as {
+  const result = (response.result ?? {}) as {
     ok?: boolean
     error?: string
     comment?: DetailComment
   }
-  const result = response.result as { ok?: boolean; error?: string }
+  const result = (response.result ?? {}) as { ok?: boolean; error?: string }

async function bootstrap(client: RpcRequestSender): Promise<HostTaskBootstrap> {
const statusResponse = await client.sendRequest('status.get')
requireSuccess(statusResponse)
const status = statusResponse.result as { capabilities?: string[] }

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the RPC response contract and the relevant command fixtures/handlers.
rg -n -C 5 'export type Rpc(Response|Success)|result:' mobile/src/transport
rg -n -C 5 "sendRequest\\('(status.get|repo.list|github.createIssue|gitlab.createIssue)" mobile/src/tasks
rg -n -C 5 "(status\\.get|repo\\.list|github\\.createIssue|gitlab\\.createIssue)" \
  mobile src --glob '*test*' --glob '*.ts' --glob '*.tsx'

Repository: stablyai/orca

Length of output: 50370


🤖 get_repo_knowledge executed:

get_repo_knowledge stablyai/orca /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4/learnings /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4/conventions

Length of output: 29495


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- changed task files ---'
sed -n '1,140p' mobile/src/tasks/native-host-task-read-operations.ts
sed -n '1,120p' mobile/src/tasks/native-host-task-provider-write-operations.ts

printf '%s\n' '--- direct imports and RPC definitions ---'
rg -n -C 4 '^(import|export).*?(sendRequest|RuntimeRpc|RpcResponse|NativeHost)|sendRequest|interface .*Response|type .*Response' \
  mobile/src/tasks mobile/src/transport mobile/src --glob '*.ts' --glob '*.tsx' \
  | head -n 500

printf '%s\n' '--- exact task call sites and nearby error handling ---'
rg -n -C 8 'status\.get|repo\.list|github\.createIssue|gitlab\.createIssue' \
  mobile/src/tasks --glob '*.ts' --glob '*.tsx' | head -n 500

Repository: stablyai/orca

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- native-host-task-read-operations.ts ---'
cat -n mobile/src/tasks/native-host-task-read-operations.ts | sed -n '1,110p'

printf '%s\n' '--- native-host-task-provider-write-operations.ts ---'
cat -n mobile/src/tasks/native-host-task-provider-write-operations.ts | sed -n '1,90p'

printf '%s\n' '--- symbols imported by these files ---'
head -n 35 mobile/src/tasks/native-host-task-read-operations.ts
head -n 35 mobile/src/tasks/native-host-task-provider-write-operations.ts
rg -n -C 6 'function sendRequest|const sendRequest|sendRequest<' mobile/src --glob '*.ts' --glob '*.tsx' | head -n 300

printf '%s\n' '--- response type declarations ---'
rg -n -C 8 'RuntimeRpcResponse|RpcResponse|NativeHost.*Response|result\?:|result:' mobile/src shared/src --glob '*.ts' --glob '*.tsx' | head -n 500

Repository: stablyai/orca

Length of output: 50369


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '%s\n' '--- repository knowledge files ---'
if command -v find >/dev/null; then
  find /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4 -maxdepth 2 -type f -print 2>/dev/null | head -n 80
fi

printf '%s\n' '--- exact initial-log matches for transport and task files ---'
rg -n -C 8 'native-host-task-read-operations|native-host-task-provider-write-operations|export (type|interface) .*Rpc|RuntimeRpcResponse|sendRequest' \
  /tmp/coderabbit-shell-logs/shell-output-7srvV7 | head -n 500

Repository: stablyai/orca

Length of output: 43616


Validate RPC result shapes before field access.

RpcSuccess.result is unknown; requireSuccess and response.ok only validate the envelope. These casts perform no runtime validation. A null or undefined result can cause a TypeError, while other wrong shapes can return invalid values at status.capabilities, result.repos, or the issue fields. Add narrow runtime decoders for the object and expected arrays and fields, and throw a controlled task-operation error.

📍 Affects 2 files
  • mobile/src/tasks/native-host-task-read-operations.ts#L24-L24 (this comment)
  • mobile/src/tasks/native-host-task-read-operations.ts#L59-L59
  • mobile/src/tasks/native-host-task-provider-write-operations.ts#L20-L20

- Clear terminal consumes the boolean the adapter returns, so a clear that
  never reached the host no longer toasts success.
- A refused `@`-autocomplete search returns null instead of an empty list, so
  the failure is not cached and the next keystroke retries.
- The Linear workspace picker reloads the host's context on both outcomes, and
  `selectWorkspace` detects a refusal by the id the host echoes back, since it
  answers a refusal with its unchanged status and no `ok` field.
- `read.loadLinearContext` splits into `linearStatus` and `linearTeams`, so the
  screen commits the workspace list and the resolved selection before it asks
  for teams, as it did before the seam existed.
- Resolving a review thread reports the host's message for a transport failure
  and the caller's wording only for a refusal, in both the item and the
  Projects lane.
- The RPC ratchet matches the identifier rather than `sendRequest(`, counts
  client subscriptions, walks every source directory and all of `mobile/app`,
  classifies adapters by path, and pins each remaining site by file. Verified
  against bind, bracket access, a space before the paren, an aliased client, a
  helper in another directory and an adapter-shaped new file.
- Deletes the adapter families nothing constructs (file preview, workspace
  catalog and the workspace group), the dead device wiring, and the three
  methods that shadowed a live call with different behavior.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
mobile/src/session/native-host-session-terminal-file-operations.ts (1)

42-42: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard response.result before reading .opened. RpcSuccess.result is unknown, and the reachable file-tap path calls openWorktreeFile. If files.open returns ok: true with result: null, the cast does not add a runtime check, so this expression throws TypeError instead of Error('file_open_failed'). Check for a non-null result first and use the existing failure error when it is absent or opened is false.

mobile/src/tasks/use-mobile-tasks-provider-load-actions.tsx (1)

165-168: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Await listOperations.countGitHub inside this try block. countGitHub returns a promise, so the current return catches only synchronous errors. A rejected count propagates through mapWithConcurrency, and the total is not updated. Use return await so the catch returns 0 for that repository.

mobile/src/tasks/native-host-task-item-review-operations.ts (1)

126-134: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Guard successful mutation results before reading ok. RpcSuccess.result is unknown, but mutationComment and assertMutation dereference result.ok without checking for null or undefined. A successful comment, reviewer, or merge mutation can therefore throw TypeError instead of its operation-specific fallback. Guard the result object in both helpers. Keep resolveThread's existing non-true check.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: c0b3e8d6-32cc-4c60-92a9-3552372150c7

📥 Commits

Reviewing files that changed from the base of the PR and between 654db50 and 1b86a68.

📒 Files selected for processing (30)
  • mobile/src/device/device-operations.ts
  • mobile/src/device/native-device-operations.ts
  • mobile/src/session/host-session-native-chat-operations.ts
  • mobile/src/session/host-session-tab-operations.ts
  • mobile/src/session/host-session-terminal-operations.ts
  • mobile/src/session/mobile-session-route-parity.test.ts
  • mobile/src/session/native-host-session-browser-operations.ts
  • mobile/src/session/native-host-session-native-chat-operations.ts
  • mobile/src/session/native-host-session-tab-operations.test.ts
  • mobile/src/session/native-host-session-tab-operations.ts
  • mobile/src/session/native-host-session-terminal-operations.test.ts
  • mobile/src/session/native-host-session-terminal-operations.ts
  • mobile/src/session/use-mobile-native-chat-file-search.test.ts
  • mobile/src/session/use-mobile-native-chat-file-search.ts
  • mobile/src/session/use-mobile-session-terminal-input.ts
  • mobile/src/tasks/host-task-read-operations.ts
  • mobile/src/tasks/host-task-runtime-payloads.ts
  • mobile/src/tasks/linear-status-projection.ts
  • mobile/src/tasks/mobile-tasks-filter-pickers.tsx
  • mobile/src/tasks/mobile-tasks-refactor-parity.test.ts
  • mobile/src/tasks/native-host-task-item-review-operations.ts
  • mobile/src/tasks/native-host-task-linear-operations.ts
  • mobile/src/tasks/native-host-task-operation-contracts.test.ts
  • mobile/src/tasks/native-host-task-project-mutation-operations.ts
  • mobile/src/tasks/native-host-task-read-operations.ts
  • mobile/src/tasks/use-mobile-tasks-project-detail-loading.tsx
  • mobile/src/tasks/use-mobile-tasks-provider-load-actions.tsx
  • mobile/src/tasks/use-mobile-tasks-route-and-item-state.tsx
  • mobile/src/transport/screen-rpc-ratchet.test.ts
  • mobile/src/worktree/native-host-workspace-creation-read-operations.ts
💤 Files with no reviewable changes (6)
  • mobile/src/tasks/use-mobile-tasks-route-and-item-state.tsx
  • mobile/src/device/native-device-operations.ts
  • mobile/src/session/native-host-session-tab-operations.ts
  • mobile/src/session/host-session-terminal-operations.ts
  • mobile/src/session/host-session-tab-operations.ts
  • mobile/src/session/native-host-session-terminal-operations.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 5 remain after this review.

Comment on lines +53 to +56
// No move/down/up fallback here: the live caller wraps that sequence in a try/catch
// because pointer commands race page navigation, and a throwing copy of it would surface
// those races as errors the user never saw.
await client.sendRequest(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🔎 Supported by static analysis

🏁 Script executed:

#!/bin/bash
# Check how callers of the browser click operation handle success and failure.
rg -nP -C6 '\.click\s*\(' --type=ts --type=tsx mobile/src | rg -n -C6 'browser|click' | head -80
rg -nP -C8 'browser\.mouseClick' mobile/src

Repository: stablyai/orca

Length of output: 3268


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- adapter operations ---'
sed -n '1,130p' mobile/src/session/native-host-session-browser-operations.ts
printf '%s\n' '--- requestResult and client contract ---'
rg -n -C8 'function requestResult|const requestResult|requestResult\s*=|sendRequest\s*\(' mobile/src/session mobile/src/browser --glob '*.ts'
printf '%s\n' '--- direct caller ---'
sed -n '90,145p' mobile/src/browser/use-mobile-browser-commands.ts

Repository: stablyai/orca

Length of output: 50370


🤖 get_repo_knowledge executed:

get_repo_knowledge stablyai/orca /tmp/coderabbit-repo-knowledge/stablyai-orca-89dc44e4/learnings

Length of output: 21485


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- RpcClient declaration and implementation ---'
rg -n -C12 'export (interface|type) RpcClient|interface RpcClient|class .*RpcClient|sendRequest\(' mobile/src/transport mobile/src --glob 'rpc-client.ts' --glob '*.ts' | head -220
printf '%s\n' '--- browser operation interface and native adapter wiring ---'
rg -n -C10 'HostSessionBrowserOperations|nativeHostSessionBrowserOperations|operations\.click|browserOperations' mobile/src --glob '*.ts' --glob '*.tsx' | head -260
printf '%s\n' '--- click helper contract ---'
sed -n '100,165p' mobile/src/browser/use-mobile-browser-commands.ts

Repository: stablyai/orca

Length of output: 30953


🏁 Script executed:

#!/bin/bash
set -e
printf '%s\n' '--- browser operation consumers ---'
rg -n -C8 '\.browser\.click|browser\.click|browser:\s*.*operations|const .*browser.*operations|HostSessionBrowserOperations' mobile --glob '*.ts' --glob '*.tsx' | head -260
printf '%s\n' '--- request helper and response handling ---'
sed -n '1,85p' mobile/src/browser/use-mobile-browser-request.ts
rg -n -C5 'function assertRpcOk|const assertRpcOk|assertRpcOk' mobile/src/browser/use-mobile-browser-commands.ts

Repository: stablyai/orca

Length of output: 13184


Surface refused browser.mouseClick responses. click awaits client.sendRequest directly and ignores ok: false, so a refused click resolves successfully. Use requestResult for this request while keeping the navigation-race fallback omitted.

Comment on lines +36 to +37
async updateState(target, stateId) {
await request(client, 'linear.updateIssue', {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Validate the linear.updateIssue result before resolving updateState. The host returns { ok: false, error } when the Linear mutation fails, but the request helper checks only the outer RPC envelope. The reachable caller updates the local item after updateState resolves, so it can display an unapplied state. Apply assertMutation to this result as addComment does.

Comment on lines 60 to +61
setSelectedLinearWorkspaceId(workspaceId)

const teamsResponse = await client.sendRequest('linear.listTeams', {
workspaceId: workspaceId ?? undefined
})
if (!isSuccess(teamsResponse)) {
throw new Error(teamsResponse.error.message)
}
const teams = teamsResponse.result as LinearTeam[]
const teams = await taskOperations.read.linearTeams(workspaceId)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Clear stale Linear team state before loading the new workspace.

If linearTeams(workspaceId) rejects, the new workspace is already committed, but linearTeams and selectedLinearTeamIds retain their previous values. The team picker can then show teams from the previous workspace, and task filtering can use stale team IDs. Clear both before the awaited request.

Proposed fix
 setLinearWorkspaces(status.workspaces)
 setSelectedLinearWorkspaceId(workspaceId)
+setLinearTeams([])
+setSelectedLinearTeamIds(new Set())
 const teams = await taskOperations.read.linearTeams(workspaceId)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
setSelectedLinearWorkspaceId(workspaceId)
const teamsResponse = await client.sendRequest('linear.listTeams', {
workspaceId: workspaceId ?? undefined
})
if (!isSuccess(teamsResponse)) {
throw new Error(teamsResponse.error.message)
}
const teams = teamsResponse.result as LinearTeam[]
const teams = await taskOperations.read.linearTeams(workspaceId)
setSelectedLinearWorkspaceId(workspaceId)
setLinearTeams([])
setSelectedLinearTeamIds(new Set())
const teams = await taskOperations.read.linearTeams(workspaceId)

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one minor suggestion inline.

Reviewed changes

This run reviewed the single follow-on commit (1b86a68, "close the seven defects the adapter review found") on top of the prior Pullfrog review.

  • Removed unreferenced adapters — dropped the file-preview and workspace-catalog adapter families and narrowed DeviceOperations to randomNonce, consistent with the "only live consumers" rule.
  • Removed three dead session methods — createBlank, createAgent, and sendQueryReply are gone from the session adapters and their interfaces.
  • Fixed file-search refusal caching — searchFiles now returns string[] | null, so a refused files.searchPaths is no longer cached as an empty autocomplete answer (with a test that a refused search retries on the next keystroke).
  • Split loadLinearContext — linearStatus and linearTeams now commit workspaces before the team read, so a failed team read no longer blanks the picker.
  • Hardened linear.selectWorkspace — refusal is detected by the echoed selectedWorkspaceId rather than assertMutation.
  • Fixed handleClearTerminal — removed a double-clear and keyed the toast off the host's ok result.
  • Rewrote the screen-RPC ratchet — now also matches inline subscribe (several alias/bracket/stream shapes) and pins remaining inline RPC sites by method name, addressing the prior review's sendRequest-only concern.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

Returning the promise from inside the try let a rejected count escape the
per-repo catch, reject the whole batch and reach the caller, which fires this
without a handler. The inline call it replaced awaited and reported zero.

The new test drives the real batching shape and fails if the await is removed.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Only back, forward and reload have a consumer; the browser pane still issues
its own RPCs. subscribe, navigate, scroll, click, insertText, keypress and
dialog go, with the event and frame-comparison types that existed only for
them. click's fallback had also lost main's move/down/up sequence and cited a
caller that does not exist here.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
The native chat subscribe declared an onError the adapter never invoked, since
the transport has none: a stream error arrives as an error frame through the
listener, which is where the caller already handles it. The hook's second
callback was unreachable, so it goes with the parameter.

The terminal adapter's acknowledge was a no-op with no caller here or on main.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
resolveTerminalPath took a tabId the native adapter never sent, and main's
wire had none either, so the caller's sourceTabId computation fed nothing.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the four commits pushed since the prior Pullfrog review (1b86a68): three dead-surface removals that close out the open threads, and one per-repo count robustness fix.

  • Dropped the seven dead browser adapter methods — HostSessionBrowserOperations now carries only back/forward/reload, its three live consumers; the uncalled subscribe/navigate/scroll/click/insertText/keypress/dialog methods and their screencast types are gone.
  • Removed the native-chat onError callback — subscribe no longer declares an onError param the transport could never fire; the interface now documents that stream errors arrive as error frames through onEvent.
  • Removed the no-op terminal acknowledge — dropped from HostSessionTerminalOperations and its native implementation (nothing called it).
  • Dropped the resolve tabId field — HostSessionTerminalFileResolveRequest.tabId and the sourceTabId derivation in mobile-file-tap-open.ts were removed, since the native adapter never sent it on the wire.
  • Awaited per-repo GitHub counts — countGitHubItems now awaits countGitHub inside its per-repo try so a rejected count is read as zero instead of rejecting the whole batch, with a test that pins the propagation.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

… phases

read.bootstrap() folded status.get and the settings fan-out behind one await,
so the supported commit ran only after all five requests succeeded. A
post-connect timeout on any of the four left tasksSupportState at 'unknown',
which the list surface renders as a bare spinner, and the effect deps do not
change on that failure so it never retried.

The probe is now read.tasksSupported() and the fan-out is read.bootstrap(),
with the caller committing supported and clearing the error between them, as
it did before the seam existed.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
- The Project host oracle no longer accepts a bare `payload` identifier, which
  every call site satisfies by construction. Inline payloads must show a host
  or a slug builder; the four typed-payload reads are named explicitly and
  their payload types are pinned to carry the host.
- `prRepo` is pinned per mutation and bounded to that adapter method, so
  dropping it from one of six fails instead of needing all six.
  `github.addPRReviewComment` is pinned again, with the row repository.
- The smart-source fan-out fake now delegates to the shipped search functions
  over a scripted transport, so the repoId stamping under test is the real one
  rather than the fake's copy.
- The retired-name read's method and `id:` prefix are asserted again, in the
  adapter test, since the hook now takes a callback.
- The session adapters join the route parity family, bringing the runtime
  strings and identity fields that moved out of the hooks back into scope.

Each restored assertion was verified to fail under the mutation it guards.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the two follow-on commits since the prior Pullfrog review (cac6346): one restoring main's observable behavior and one hardening the extracted adapter tests.

  • Split tasksSupported() out of bootstrap() — the Tasks hydration hook now commits its supported state before the settings/ui/preflight/linear fan-out, so a post-connect transport failure renders behind an error banner instead of a permanent spinner (pinned by use-mobile-tasks-hydration-phases.test.tsx).
  • Restored the 30 s project-mutation timeouts — resolve-thread, review-reply, conversation-comment, and request-reviewers now pass a timeout through the shared github-pr-mutations fetchers via a new options param, with rerun/merge keeping their 60 s budget.
  • Made Linear comment reads fail on transport, not refuse — optionalComments tolerates only a refusal envelope, so a timed-out comment read again fails the detail load.
  • Reverted five parity drifts — the github.listWorkItems timeout, the clear-terminal toast on refusal, the SSH badge on a null-state answer, four error strings, and the legacy file-inventory cache reset (resetFileSearchCache).
  • Removed remaining dead surface — the device module, host-workspace-host-context.ts, uncalled tab/account methods, and nativeChat.openFile.
  • Restored four weakened test assertions — the Project host oracle, per-method prRepo, smart-source fan-out stamping, and four unpinned RPC names, each verified to fail under mutation.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

The per-call slice was still a fixed 700 characters, so deleting host from
updateComment passed on deleteComment's host sitting inside the window. It now
ends at the enclosing method, matching the prRepo loop, and accepts the
shorthand property form that github.project.listAccessible uses.

Verified: dropping `host: target.host` from updateComment fails the oracle.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
- HostSessionNativeChatOperations sendMessage, respond and attachImage. Every
  shipped send path calls sendMobileNativeChatMessageWithOutcome or
  typeMobileNativeChatCommandWithOutcome directly, as on main.
- HostWorkspaceCreationOperations createBlankWorkspace and
  createWorkspaceFromSource. use-new-workspace-create-submit imports
  createBlankWorkspace from the source module instead.

With those two gone the native creation factory only spread the RPC
operations and RpcWorkspaceCreationOperations only excluded them, so both
collapse. The arg and result types that existed solely for the five methods go
with them, as does the dead SessionTabsResult re-export.

Each name was grepped against non-test, non-adapter callers before and after.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the two commits pushed since the prior Pullfrog review (0aaa70e): a test-hardening commit and a dead-surface removal commit.

  • Dropped the five dead adapter methods — native-chat sendMessage/respond/attachImage and workspace-creation createBlankWorkspace/createWorkspaceFromSource are gone. I confirmed the send paths call sendMobileNativeChatMessageWithOutcome and the image-upload hooks directly, and the create paths import blank-workspace-create.ts/source-workspace-create.ts directly, so these adapter methods had no live caller.
  • Removed the dead SessionTabsResult re-export — every consumer already imports it from mobile-session-route-types.ts.
  • Bounded the Project host-routing oracle — the test now slices to the enclosing adapter method (\n },) and matches \bhost\s*[:,}], so a neighbour's host can no longer satisfy the assertion.
  • Re-froze the session-route parity digest — runtime strings 593 → 592, matching the removed method bodies.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 1


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Advanced

Run ID: 82153d6d-c543-4188-a961-c8caf329fdb8

📥 Commits

Reviewing files that changed from the base of the PR and between 0aaa70e and 5cbbadf.

📒 Files selected for processing (8)
  • mobile/src/session/host-session-native-chat-operations.ts
  • mobile/src/session/host-session-tab-operations.ts
  • mobile/src/session/mobile-session-route-parity.test.ts
  • mobile/src/session/native-host-session-native-chat-operations.ts
  • mobile/src/tasks/github-project-host-routing-source.test.ts
  • mobile/src/worktree/host-workspace-creation-operations.ts
  • mobile/src/worktree/native-host-workspace-creation-operations.ts
  • mobile/src/worktree/rpc-workspace-creation-operations.ts
💤 Files with no reviewable changes (3)
  • mobile/src/session/host-session-native-chat-operations.ts
  • mobile/src/session/native-host-session-native-chat-operations.ts
  • mobile/src/session/host-session-tab-operations.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 6 remain after this review.

… behind

The tab adapter's capabilities() method was removed as uncalled; this module existed only to serve it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…ports

Each was a one-line pass-through to its native factory. The five domain-level defaults are the only ones the screens construct.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the two commits pushed since the prior Pullfrog review (5cbbadf): a mechanical dead-surface census that removes 21 files nothing imports and prunes the ratchet's adapter list to match.

  • Dropped the dead capability projection — host-session-runtime-capabilities.ts (and projectHostSessionRuntimeCapabilities) existed only to serve the tab adapter's capabilities() method, which was already removed as uncalled; no references remain anywhere in mobile/.
  • Dropped 20 one-line per-namespace default factories — each defaultHost<X>Operations was a pass-through to its nativeHost<X>Operations with zero importers. The five domain-level defaults (session/task/account/file-explorer/workspace-creation) are the only ones screens construct.
  • Pruned the ratchet adapter list — screen-rpc-ratchet.test.ts's ADAPTER_FILES set dropped the 20 removed paths, keeping the "pins every remaining inline RPC site" assertion consistent with the deleted files.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the single follow-on commit (753ec13, "bound the Project host oracle to a real method boundary") pushed since the prior Pullfrog review (892c6fc).

  • Extracted enclosingAdapterMethod — a helper that slices the adapter source from an offset to the next 4‑space‑indent method-closing brace (\n {4}\},?\n) and throws if none exists, so a host/prRepo/repoPayload assertion can no longer leak into a neighbour method or the rest of the file when the boundary is absent.
  • Replaced the three indexOf('\n },') + EOF‑fallback slices — the host-qualification, per-method prRepo, and per-method repoPayload assertions now use the helper instead of falling back to slice(offset, undefined).
  • Corrected the last‑method boundary — the comma‑optional regex (},?) now stops at a file's final method closing with } rather than },, which the previous code could only handle by silently slicing to end‑of‑file.

Verified mobile/src/tasks/github-project-host-routing-source.test.ts passes (4/4), and confirmed the regex matches the actual 4‑space method indentation in all three adapter source files it inspects.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

main had no clearInputFirst. The flag prefixed a clear byte onto the body of a
terminal.send, which is the shape the same file documents as observed broken in
the field: the burst reached the agent as literal control characters with the
draft still parked. No production caller set it.

mobile-native-chat-send.ts now has no diff against main. The assertions that
existed only to prove nobody set the flag are gone; the case that pins verbatim
text stays and fails if a prefix comes back.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
main checked only response.ok, pruned the tab, wrote a tombstone and let the
host's republished snapshot restore it if the close was refused. The adapter
had mapped a refusal to an outcome the caller skipped every state commit for,
leaving the tab on screen, and had added a `closed !== true` precondition main
never had, so a host answering ok without that field would strand the tab.

close() returns a boolean again and the caller prunes on it. The test that
pinned the refusal projection is replaced by two that pin main's contract: ok
with an empty result still reports acceptance, a transport refusal does not.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
… identity again

Stop routed through the shared chat write, which refuses to start under a 2s
residual budget. The call it replaced tried on anything above zero and could be
accepted, so Stop now has its own send: main's params, main's
{ timeoutMs, budgetSpansConnect: true }, no enter field, the same worker
takeover report on accept and the same unknown/rejected classification.

The parity oracle also follows the two adapters the route's identity fields
moved into, since the visitor only expands bare-identifier calls and could
never reach operations.terminal.sendInput. Identity fields are 15 rather than
main's 14: the seam turned two hook-side deviceToken fields into three
adapter-side client:{id} builders. Dropping the binding in the adapter now
fails the oracle.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…umer

sparseCheckout was added to the composer create args and forwarded into the
worktree.create params; its only caller never passes it. warning was added to
WorktreeCreateResult and read off the response; nothing reads it, and the one
screen that shows a create warning is on the unchanged inline path with its own
copy.

worktree-create-retry.ts and source-workspace-create.ts are now byte-identical
to main.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…r retry

main handled `!response.ok` after the retry helper returned. Throwing inside
the try made a refusal replayable, because the cutover predicate matches any
Error whose message is exactly the migration string, so a host refusal carrying
that text was retried five times.

The helper returns the response again and the caller checks it. The new test
fails under the old shape with six attempts instead of one.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
Each was confirmed to have zero non-test readers before and after:

- HostSessionTerminalOperations.subscribe, its adapter body and its test. The
  live stream still calls subscribeMobileTerminalSafely directly, as on main,
  so the method existed only to be tested.
- pasteImages and releaseImages, declared on the native-chat interface and
  never implemented or called.
- explicitQuickCommandScope, exported with no callers.
- The quick-command snapshot's totalCount and repoId, which nothing read, and
  with them the workspaceId argument that existed only to compute repoId. The
  hook takes { operations, enabled } again, as main took { client, enabled }.
- The web-artifact result variant, line and column on the resolve request,
  rename's third parameter and the directory variant's truncated. The
  legacy-list variant's truncated stays: the panel reads it.

Deleting the terminal subscribe also brings the parity oracle's identity
fields to 14, matching main exactly.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…actions

Each had gained a non-null target requirement that demanded a number and a
recognised item type where main's guard did not, so a row main would have sent
for now returns silently or reports an error.

File-viewed and the comment reply gate on main's conditions and build the
never-null identity target after them. Metadata update, issue type and comment
add gate on the slug and the number, as main did, through the slug target,
which does not read the item type.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
- listRepositories dereferences result.repos, so a repo.list answer without it
  throws into the caller's catch as it did before.
- projectResult requires an explicit ok:true again; a result that omits the
  flag is not a confirmed success.
- readRuntimeSettings returns null for a settings-less answer, so the create
  sheet keeps what it already had instead of committing {}. The task-create
  path keeps its own {} default, which is what it had.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…to its call

The project target carried a `slug` field the type does not declare and omitted
owner/repo/host, so repoPayload read them as undefined and the oracle only ever
exercised the prRepo:null path. It now uses the real shape and asserts the row
repository reaches the wire; blanking prRepo fails it.

The connection gate asserts TERMINAL_INPUT_SEND_OPTIONS inside sendInput rather
than anywhere in the counted set; removing it from that call fails the gate.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb
…e comments

The file explorer's reconnect ternary called the same function on both sides,
and its comment claimed the null branch mattered. Retry now calls
forceReconnect directly, as main did, and `reconnect` leaves the explorer
operations interface, where a UI action did not belong.

Comments corrected: the ratchet's regex doc no longer claims aliasing cannot
hide a subscribe, and names the two shapes it misses; the deliberate-inline
comment says those four are in the routed screens, not the whole remaining
surface; and the chat stop's layout effect names the gate that requires it.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

ℹ️ No critical issues — one minor suggestion inline.

Reviewed changes

This run reviewed the ten commits pushed since the prior Pullfrog review (753ec13): a parity-restoration pass that reverts the adapters to main's observable behavior and deletes the seam surface the extraction had left unreferenced.

  • File-explorer Retry routed through the transport — the disconnected-state revive path now calls forceReconnect directly instead of hanging off an operations.reconnect(), so HostFileExplorerOperations no longer carries a reconnect method and defaultHostFileExplorerOperations takes only the client.
  • Quick-command seam slimmed and the envelope check moved — the workspaceId/repoId/totalCount threading is gone from snapshot/mutate (the sheet derives repoId from route helpers itself), and ok is checked outside the cutover retry.
  • Tab close prunes on ok alone — close() returns the transport's ok instead of the HostSessionTabCloseResult union, matching main which never inspected the result body.
  • Stop uses its own send — sendStopEscape sends a bare Escape (no enter, no 2 s floor) on whatever budget remains, and the device-identity binding is re-pinned via the new ADAPTER_IDENTITY_SOURCES parity walk.
  • Project-row guards restored to main — the four rows re-gate on projectRowSlugTarget/projectRowIdentityTarget, and projectResult now requires an explicit ok: true, listRepositories dereferences repos directly, and readRuntimeSettings returns null for a settings-less answer.
  • Dead surface removed — the Ctrl+U clearInputFirst send path, the native-chat pasteImages/releaseImages options, the terminal subscribe adapter method, and two create fields with no producer or consumer are gone.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

… leftovers

The refused-body case had no test after the round-5 revert; reintroducing the defect
passed the suite. Also removes the stream types stranded when the terminal subscribe
adapter was deleted, and a truncated field the directory result type no longer declares.

Claude-Session: https://claude.ai/code/session_01JNnE9qzUZMMnqpZWCqM3nb

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the single follow-on commit (bcba08b, "pin that a refused tab close still prunes, and drop two leftovers") pushed since the prior Pullfrog review (59e5e6e).

  • Dropped the dead truncated field — nativeHostFileExplorerOperations no longer emits truncated: false on the directory result, closing the prior review's open nit; truncated only exists on the legacy-list variant.
  • Removed the stranded terminal stream types — HostSessionTerminalData / HostSessionTerminalStreamEvent / HostSessionTerminalSubscribeArgs and the TerminalOscLinkRange import are gone from host-session-terminal-operations.ts, the surplus left behind when the subscribe adapter was deleted; I confirmed no references remain repo-wide.
  • Added a refused-close regression pin — native-host-session-tab-operations.test.ts now asserts close() resolves true on ok: true even when the result body reports refused: true, so reintroducing result-body inspection fails the suite.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@pullfrog pullfrog Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

✅ No new issues found.

Reviewed changes

This run reviewed the ten commits pushed since the prior Pullfrog review (bcba08b): the three parity regressions a Codex whole-diff read had surfaced, the type/fixture cleanups that landed alongside them, and the fix-forward commits that repaired a parallel-worker overwrite in the middle of the range.

  • Legacy file-inventory generation guard — searchFiles in native-host-session-native-chat-operations.ts now bumps a per-workspace generation on resetFileSearchCache and drops an in-flight files.list completion whose generation no longer matches, so a slow inventory started before leaving a workspace can no longer repopulate that workspace's cache afterward; the in-flight cleanup is identity-guarded.
  • Project mutation result acceptance — projectMutation gained a requireOk parameter: addComment requires a truthy ok, the other mutations check result.ok === false on a dereferenced result (a null/undefined result throws before any optimistic commit), and resolveReviewThread requires a literal true result. Error wording splits bare-string (github.*) vs object (github.project.*) failures consistently with github-pr-mutations.ts.
  • Linear detail settlement order — loadLinear awaits both raw requests via Promise.all and checks the issue envelope before comments, so a comments transport rejection again wins over an issue refusal while a refused comments envelope still yields an empty thread.
  • Projection/list types tightened — the top-level fallback fields the detail projection accepted beyond item are gone, list results are typed as the raw Omit<…, 'repoId' | 'repoName'> items main used (compensating casts removed), and the phantom cursor field and duplicate NewWorkspaceRepository type were dropped in favor of MobileWorkspaceRepo.
  • Fixture/type cleanup — mock envelopes now carry id/_meta, and the obsolete clipboard/haptics mocks were removed from the accounts reset-credit test.

Each of the three parity fixes carries a dedicated test that fails with the fix reverted.

Pullfrog  | View workflow run | Using DeepSeek Pro (free via Pullfrog for OSS) | 𝕏

@Jinwoo-H

Copy link
Copy Markdown
Contributor Author

Closing as superseded. The step-4 to step-7 RPC-foundation series landed every screen call as a fixed operation descriptor with a checked reply (session #21089, tasks #21169 and #21246, notifications/terminal/transport #21176, files/dictation/host-screen/agent-history #21269), with the recorded golden corpus and the CI pin guard as the oracle. The adapter routing this PR proposed is now built on those descriptors; the hybrid re-split of #13386 continues on top of the foundation rather than on this branch.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant