Skip to content

fix(mobile): retry session capability probe so tab-row actions survive relay cutover - #9794

Merged
brennanb2025 merged 2 commits into
mainfrom
brennanb2025/mobile-capability-probe-retry
Jul 22, 2026
Merged

brennanb2025 merged 2 commits into
mainfrom
brennanb2025/mobile-capability-probe-retry

Conversation

@brennanb2025

Copy link
Copy Markdown
Contributor

Problem

On mobile over relay, the quick-commands button in the session tab row sometimes never appeared; leaving the session and re-entering brought it back.

The session screen learns host capabilities (quick commands, browser screencast, agent session history, terminal query-reply input) from a single status.get fired when the screen connects. Two transient failure modes reject that probe while connState stays 'connected', so the effect never re-runs:

  • Relay→direct cutover: when the app starts on relay and then upgrades to a direct transport, migrateTo on the stable logical client rejects every in-flight request with LogicalClientCutoverError — but the published connection state goes connected→connected, so no re-probe is triggered.
  • Request timeout: a slow/dropped frame times the request out without any connection-state change.

The old .catch latched all four capability flags false (and an ok:false reply left them null forever), permanently hiding the quick-commands button until the screen remounted — exactly the observed one-time disappearance that self-healed on re-entry.

Fix

  • New mobile/src/transport/runtime-capability-probe.ts: startRuntimeCapabilityProbe retries until a probe lands — promptly (250ms) after a cutover, since the replacement transport is already authenticated, and with capped exponential backoff (1s → 15s) on other failures, including ok:false replies which previously wedged silently. Cancellation via the effect cleanup stops the loop; connState leaving 'connected' still resets the flags as before.
  • Session screen uses the probe; behavior on success is unchanged.
  • Export isLogicalClientCutoverError from stable-logical-rpc-client.ts and reuse it in worktree-create-capability.ts instead of its local copy.

This also heals the other three capability gates fed by the same probe (browser screencast, agent history, query-reply input forwarding).

Verification

  • 10 new tests in runtime-capability-probe.test.ts: first-try success, prompt cutover retry, backoff + recovery, ok:false retry, backoff cap, cancellation stops retries, and post-cancel results are dropped.
  • Full mobile suite: 304 files / 2178 tests pass; tsc --noEmit, oxlint, oxfmt --check clean.

…e relay cutover

The session screen learned host capabilities (quick commands, browser
screencast, agent history, query-reply input) from a single status.get
fired when the screen connected. Over relay, a relay-to-direct transport
cutover rejects every in-flight request while connState stays
'connected', and a request timeout does the same — so one transient
failure latched the capability flags false (or left them null on an
ok:false reply) and the quick-commands tab-row button stayed hidden
until the screen was remounted.

Replace the one-shot probe with startRuntimeCapabilityProbe: retry
promptly after a cutover (the replacement transport is already
authenticated) and with capped exponential backoff on other failures,
until a probe lands or the effect is cleaned up. Also export the
cutover-error predicate from stable-logical-rpc-client and reuse it in
worktree-create-capability instead of a local copy.
@coderabbitai

coderabbitai Bot commented Jul 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@brennanb2025, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 14 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: 0bd7c7d1-dbf9-4185-9bc7-c2450702f4f7

📥 Commits

Reviewing files that changed from the base of the PR and between 3a9ba75 and c3975bc.

📒 Files selected for processing (2)
  • mobile/app/h/[hostId]/session/[worktreeId].tsx
  • mobile/src/session/mobile-session-startup-source.test.ts
📝 Walkthrough

Walkthrough

Adds a reusable runtime capability probe that retries failed status.get requests with cutover-aware and exponential backoff, supports cancellation, and reports capabilities on success. Adds shared logical-client cutover error detection and updates worktree capability probing to use it. Adds tests covering success, retries, capped backoff, unsuccessful responses, and cancellation. The session screen now uses the shared probe to derive runtime capability state.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description does not follow the required template and omits several mandatory sections, including Screenshots, Testing, AI Review Report, Security Audit, and Notes. Rewrite it to match the repository template and fill in Summary, Screenshots, Testing, AI Review Report, Security Audit, and Notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title is concise and accurately summarizes the main change: retrying the mobile session capability probe after relay cutover.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
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.

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.

Caution

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

⚠️ Outside diff range comments (1)
mobile/app/h/[hostId]/session/[worktreeId].tsx (1)

2318-2333: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Reset every capability before probing a swapped client.

Only quick-command state is cleared here. A connected client swap can leave browser/agent actions enabled from the prior host, and leave hostQueryReplyInputSupportedRef true even when the replacement host lacks the capability—allowing query replies to be forwarded as normal terminal input. Reset those values before starting the probe.

Suggested fix
     // Why: a client swap can keep the route connected while moving to an older
     // host; clear the prior capability before exposing host-specific actions.
+    setBrowserScreencastSupported(null)
+    setAgentSessionHistorySupported(null)
     setQuickCommandsSupported(null)
     setShowQuickCommands(false)
+    hostQueryReplyInputSupportedRef.current = false
     // Why: the probe retries — a relay→direct cutover or request timeout rejects

ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository UI

Review profile: CHILL

Plan: Pro

Run ID: bd44ee30-47a5-4230-8efd-a5b1db8d77cb

📥 Commits

Reviewing files that changed from the base of the PR and between 71bbfa0 and 3a9ba75.

📒 Files selected for processing (5)
  • mobile/app/h/[hostId]/session/[worktreeId].tsx
  • mobile/src/tasks/worktree-create-capability.ts
  • mobile/src/transport/runtime-capability-probe.test.ts
  • mobile/src/transport/runtime-capability-probe.ts
  • mobile/src/transport/stable-logical-rpc-client.ts

@brennanb2025
brennanb2025 merged commit 6e6b7d8 into main Jul 22, 2026
2 checks passed
brennanb2025 added a commit that referenced this pull request Jul 22, 2026
…gration (#9798)

Opening the Quick Commands sheet right after connecting over relay races
the relay->direct cutover, which rejects the in-flight one-shot
settings.getTerminalQuickCommands with LogicalClientCutoverError while
connState stays 'connected'. The sheet then strands on "RPC interrupted
by connection migration" with an empty list until closed and reopened.

The read is side-effect-free, so replay it on cutover (capped at 5,
cancelled if the sheet closes or the client is replaced). Same failure
class and pattern as #9794 (capability probe) and #9796 (terminal
create).
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…e relay cutover (stablyai#9794)

* fix(mobile): retry session capability probe so tab-row actions survive relay cutover

The session screen learned host capabilities (quick commands, browser
screencast, agent history, query-reply input) from a single status.get
fired when the screen connected. Over relay, a relay-to-direct transport
cutover rejects every in-flight request while connState stays
'connected', and a request timeout does the same — so one transient
failure latched the capability flags false (or left them null on an
ok:false reply) and the quick-commands tab-row button stayed hidden
until the screen was remounted.

Replace the one-shot probe with startRuntimeCapabilityProbe: retry
promptly after a cutover (the replacement transport is already
authenticated) and with capped exponential backoff on other failures,
until a probe lands or the effect is cleaned up. Also export the
cutover-error predicate from stable-logical-rpc-client and reuse it in
worktree-create-capability instead of a local copy.

* fix(mobile): reset runtime gates before capability reprobe
enrick-dev pushed a commit to enrick-dev/orca that referenced this pull request Oct 15, 2026
…gration (stablyai#9798)

Opening the Quick Commands sheet right after connecting over relay races
the relay->direct cutover, which rejects the in-flight one-shot
settings.getTerminalQuickCommands with LogicalClientCutoverError while
connState stays 'connected'. The sheet then strands on "RPC interrupted
by connection migration" with an empty list until closed and reopened.

The read is side-effect-free, so replay it on cutover (capped at 5,
cancelled if the sheet closes or the client is replaced). Same failure
class and pattern as stablyai#9794 (capability probe) and stablyai#9796 (terminal
create).
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