Skip to content

fix(ios): show Mac handoff for Cloud requests the phone cannot answer (HQ-14) - #34088

Draft
teamleaderleo wants to merge 5 commits into
feat-cmux-nextfrom
hq-6236-14-cloud-requests-mac-handoff-20261103
Draft

teamleaderleo wants to merge 5 commits into
feat-cmux-nextfrom
hq-6236-14-cloud-requests-mac-handoff-20261103

Conversation

@teamleaderleo

@teamleaderleo teamleaderleo commented Nov 3, 2026 •

Copy link
Copy Markdown
Collaborator

HQ #6236 / brief 14 — Cloud requests should have an honest phone handoff

Starting ref: manaflow-ai/cmux@6c3fe3648ba4b44e7bf38c4982a665ec2d5d5712 (feat-cmux-next, 2026-11-03). Research and owner follow-up: HQ #6236, Cloud permission owner #30584, atomic VM mode owner #30588.

Why

The Cloud cloud-acpmux adapter emits _acpmux/permission_pending to the iPhone, yet cloud_acp.rs supports only read/prompt/cancel/detach; it refuses _acpmux/permission_respond and any question-answer call, deliberately avoiding transfer of Mac approval authority to a VM. The shared iPhone chat view nevertheless draws Face ID Allow, Deny, and Reply instead controls; questions have enabled Answer and Decline. A person away from their desk sees a usable-looking decision that the transport refuses.

This isolated change

  • Explicit AgentChatStore.canAnswerPendingRequests (defaults true for existing/local Mac chats; false only when binding a Cloud chat); any programmatic answer/decline and Reply instead go through that guard.
  • The Cloud permission bar and transcript show Open this Cloud chat on your Mac to answer; Cloud questions show their real prompt and option labels read-only. Local Mac answer UX, signed Face ID flow and Cloud text prompt/cancel stay as they were.
  • Add cloudPermissionRequiresMacHandoff Swift Testing regression with the existing mock adapter: attempts to allow/deny/reply instead retain the pending request and never queue a replacement prompt.

Scope: iPhone UI and shared chat-store presentation policy only. No VM-mode policy, auth, Rust hostd, entitlement, or server changes. This is a short-term disclosure; implementing an actual Cloud-scoped approval receipt belongs to #30584, with its atomic VM authorization dependency #30588. Avoid relabeling this as remote permission support.

Verification and explicit limits

  • Source-reviewed the original code and the changed code against exact NEXT head; five edited files, one new regression test. Tests are committed but have not run in this GitHub-only session. This runner has Swift on Linux, but no Xcode/iOS simulator/paired phone. No real APNs, multi-Mac, physical gesture, or Cloud VM approval observed.
  • Required before merge: compile/run CmuxMobileAgentChatTests on an authorized iOS simulator and visually verify the two Cloud pending cards; verify a local Mac chat still offers signed Allow/Deny and its question form.
  • Manual negative acceptance: cloud-acpmux receives a pending permission/question, phone shows the exact content but no actionable answer; opening the same session on Mac permits policy-compliant decision and phone history updates on replay. Revoke/change account, background/resume and re-tap an old push; no previously authenticated request is actionable in another account.

This PR intentionally stays draft for native validation and owner review.


Autofix with [code]smith
Open in [code]smith · Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Fixes the iPhone chat UI showing dead-end decision controls for Cloud requests that the transport refuses to answer. Cloud chats now display a read-only "Open this Cloud chat on your Mac to answer" notice for pending permissions and questions, while local Mac chats keep their existing signed Allow/Deny and question-answer flow.

  • Adds AgentChatStore.canAnswerPendingRequests (defaults true; false when binding a Cloud chat) and guards all programmatic answer/decline/reply-instead paths behind it.
  • Adds a Swift Testing regression asserting Cloud permission attempts keep the request pending and never queue a replacement prompt.

Written for commit 8a76fb2. Summary will update on new commits.

View guided diff Turn on auto-fix

@teamleaderleo teamleaderleo left a comment

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

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

Independent code review — mobile Cloud permission/question handoff (HQ #6236 brief 34 continuation)

Exact reviewed head: 8a76fb2b0f7dc3de2c9c0cf8474046f50d413268 (draft, five files). Verdict: source-level safety improvement, with one narrow P2 presentation follow-up and missing negative tests. No device/simulator capture or live Cloud permission was run.

What the changed path does correctly:

  • LiveCloudChats.swift:159–168 constructs Cloud chat stores with canAnswerPendingRequests: false. The public AgentChatStore(source:) default remains true for local-Mac chats.
  • AgentChatStore.swift:418–432, 472–504 refuses Cloud permission responses, question answers/declines, and Reply Instead (which otherwise first sends a deny), leaving the pending ask and queue alone.
  • AgentChatScreen.swift:679–710 replaces Cloud's bottom permission actions with Mac handoff text, and suppresses the Reply Instead composer label. ChatRowView.swift:119–160 removes the transcript's Allow/Deny link and replaces a pending Cloud question with static prompts and options.
  • PermissionCard only renders an action when showChoices is present; passing nil genuinely removes the secondary entry point. The native-row renderer sends permission/question kinds through the SwiftUI hosted views.

P2 follow-up — incomplete read-only question choices: The new Cloud question branch prints item.options, but ignores item.allowsOther (ChatRowView.swift:151–159). The existing actionable QuestionCard explicitly adds an Other response when allowsOther is true. A pending Cloud question that permits custom text therefore looks like an options-only question on the phone, even though the Mac has an additional response. Show a static “Other — write your own answer on Mac” row for this condition; it remains read-only on phone.

Regression gaps: New cloudPermissionRequiresMacHandoff covers denial of a Cloud permission and Reply Instead, but does not exercise pendingQuestion answer/decline refusals, a custom-Other question, or prove local-Mac permission answers still reach their source. Add a fixture verifying (1) Cloud answer and decline emit zero AgentClientIntent requests and retain the ask; (2) local-Mac store still sends; (3) read-only Other is named on screen.

Broader design consideration: Keep the current fail-closed stopgap while the paired-Mac cloud-acpmux carrier lacks answer RPCs. Future phone approval should be enabled by negotiated per-carrier capabilities (permission and question separately), rather than a permanent device-level Bool. Do not enable those actions until owner identity, session routing, and reply refusal propagation are proved.

Execution evidence: cross-file source/UX-path review only. I did not build an iOS simulator, trigger a real permission, change permissions, or push into the owner lane. No P1 blocking source error found at this head.

This branch was previously deployed

1 inactive deployment
ci-routing-agent — 8a76fb2b Deployed Nov 3, 2026 by teamleaderleo via wait for same-key tree publisher #106987
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

risk:normal Risk tier (docs/ci/risk-tiers.md)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant