Repository navigation
fix(mobile): start an explicitly chosen agent even when it is hidden in Settings - #27773
brennanb2025 wants to merge 5 commits into
Conversation
…in Settings Follows #27766: hiding an agent only hides it from pickers and the automatic pick. The phone no longer refuses a hidden agent on the task-page workspace create, the new-workspace sheet, or a source-control AI button's saved agent, and a chosen agent is no longer cleared when it becomes hidden. An older host that still refuses hidden agents shows its own message. Re-recorded the settings.task-workspace and settings.workspace-submit RPC goldens with rpc:record.
…irmed create Only an agent the user picked may start while hidden: both create screens now re-run the automatic pick from the settings read at create time unless the user chose the agent. The new-workspace sheet lists a hidden current choice like the task picker does. A launch-route create the host can't settle (agent_session_operation_unknown or _expired, which an older host also sends when it refuses a hidden agent) now reads "Couldn't confirm the workspace was created. Check your workspaces before trying again." instead of the raw code.
Reverts the generic "Couldn't confirm the workspace was created" mapping in the create retry, so resend, expiry and cutover replies behave as on main. Instead, the new-workspace sheet recognises the one case it can explain: a user-picked agent that the Settings just read hide, refused with agent_session_operation_unknown by a computer from before hidden agents could start. That computer created nothing, so the sheet names the agent and the next step.
… offer Matches the desktop Settings switch, now Shown/Hidden: the new-tab menu's empty row and the AI button's no-agent line name hidden agents and the next step.
…the desktop's empty-menu wording The create retry marks a failure that answered a resend (replayed: true), and the new-workspace sheet only names a hidden agent when the refusal answered the first send. The empty new-tab menu now reads "No Agents to Show".
Status: ready for review (head 4447ca5)This is the phone half of #27766 (hiding an agent only hides it from pickers). It does not depend on #27766 being merged first; against an older computer it keeps today's behaviour plus one plain-words line (below). Review. Three rounds with fresh reviewers each round, every finding fixed or listed:
Readiness. CI green on 4447ca5 (6 pass, 0 fail, including the mobile verify job with the test-typecheck ratchet). Merges cleanly with current main 93e0b80, and main has changed none of this PR's files since its base, so the CI result stands. No file over the line limit (largest touched mobile file 344 of 379), no line-limit disables, type assertions carry SAFETY comments. Live QA (iOS simulator through
New phone strings were checked on screen: "No Agents to Show" and "No AI agent on this workspace host is both installed and shown in Settings. Install one there, or show one in Settings on your computer." Not verified. P1 (above). The sheet's "hidden" line against a computer running an Orca from before #27766 was covered by a unit test, not live. Side note seen in QA: hiding the default agent also resets the default to Auto (desktop behaviour, unchanged here). |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info
📝 Walkthrough
Priority: ➖ Normal Merge Risk: ⚪ Minimal · up to The reviewed agent-selection and workspace-creation changes have no identified issue requiring a fix before merge. Normal checks remain appropriate. Pre-merge checks |
|
|
Closing: disabled agents should be refused at launch, because Orca stops maintaining their status hooks (Brennan, 10-12). Main's refusals stay. |
ELI5
On the phone, choosing an agent you've hidden in Settings now starts it, the same as on the desktop since #27766. Hiding an agent only controls whether it shows up in menus. As Brennan put it: "the disabled button is just for showing it in the UI or not".
#27766 removed the "agent is disabled" refusals from the host and the desktop window. It left the phone app alone because the phone's own checks are pinned by its recorded-RPC test fixtures. This PR removes those phone-side checks and re-records the fixtures they changed.
What Changed
Every row assumes the agent is hidden in Settings on the computer.
Still the same: the phone's agent pickers list only visible agents (a choice you already made stays listed so it shows as selected), and the automatic pick (default agent, then the first detected agent) still skips hidden agents. Both create screens re-run that automatic pick from the settings they read at Create, so a phone whose copy of Settings is older than the computer's still can't start a hidden agent you didn't choose. An AI button whose saved agent isn't installed on that host still shows "The saved agent for this action is not available on this workspace host." rather than quietly using a different agent.
Because the pickers never list a hidden agent, the create screens reach this path less often: an agent chosen before it was hidden, or a selection made when the screen opened. The AI button with a hidden saved agent is the everyday case.
Wording: the desktop Settings switch now reads Shown/Hidden (#27766), so the phone stops calling agents "enabled". No phone string called an agent "disabled". These changed:
Older computers (mixed versions): a phone with this change, paired with a computer that doesn't have #27766 yet, gets that computer's refusal. It never fails silently or shows a raw code:
worktree.create, which answers with the sentence.runtime_error).agent_session_operation_unknown, and for a terminal agent that computer created nothing. The sheet has just read that computer's Settings at Create, so it recognises this case when all of these hold: you picked the agent, those Settings hide it, the reply is that code, and it answers the first send rather than a resend. It then shows "Orca on your computer won't start Claude while it's hidden in Settings there. Show Claude in Settings on your computer, or pick another agent." Every other reply on that route is shown exactly as on main.An older phone talking to a newer computer still refuses on its own until the phone updates. No request or reply shapes change.
Mechanism
workspaceAgentOverridden).use-mobile-tasks-workspace-create-actions.tsxsends a passed agent as is and otherwise re-runs the automatic pick from the settings it reads at create. The refusal and its selection reset are removed.use-new-workspace-create-submit.tsnow takesagentOverriddenfrom the agent selection. It sends your pick as is and otherwise re-runs the automatic pick from the settings it reads at create. The refusal and its selection reset are removed. The sheet's agent list keeps a hidden current choice, as the task picker does.use-new-workspace-create-submit.tsalso maps the one refusal above to the sentence naming the agent (hiddenAgentRefusedMessage). The shared create retry (worktree-create-retry.ts) now marks a failure that answered a resend (replayed: true), so a resend is never relabelled. That is its only change; what it shows is unchanged.new-worktree-agent-selection.tsandworkspace-agent-selection.ts: the selection logic that clears a choice you made now drops it only when that agent isn't detected on the host, not when it is hidden. The task-page picker keeps your current choice in its list so it can show it as selected.mobile-source-control-launch-agent.ts: a saved agent starts if this host has it, hidden or not. With no saved agent, the shared automatic pick (pickSourceControlLaunchAgent) runs as before and still skips hidden agents. The phone resolves the saved agent itself rather than through that shared helper. That way this PR behaves correctly whether it merges before or after feat(agent-launch): hiding an agent only hides it from pickers #27766, which changes the same helper.isWorkspaceAgentEnabled, which nothing uses now, and the two copies of the phone's refusal string.Recorded-RPC fixtures (goldens): re-recorded with the harness's own
pnpm --dir mobile rpc:recordover the whole set. 9 of 793 files changed, none added or removed, all in the two families built around this refusal. The recording adapters choose Claude while the host's settings hide Claude:settings-task-workspace-{fulfilled,refused,transport-error}andmatrix-settings.task-workspace-settings.get-1: these used to end on the phone's refusal. They now reach the repo's setup prompt, which is the step the adapter's own comment says this family exists to cover.matrix-settings.task-workspace-create-settings.get-1: in the variants where the settings read fails, the phone used to fall back to its seeded settings (Claude hidden) and refuse. It now sendsworktree.createwith Claude.settings-workspace-submit-{fulfilled,refused,transport-error}andmatrix-settings.workspace-submit-settings.get-1: these used to end on the refusal plus a reset of the selection to Codex. They now recordworktree.creategoing out withstartupAgent: "claude". The scenarios script no reply to that create, so the submit is still pending at the last step and teardown records acleanupstep for the closed connection. I didn't add a scripted create reply: it would derive a new reply-variant fixture and new parity verdicts for a create path other families already cover.agentOverridden: true), so these recordings pin the explicit-choice rule. The automatic re-pick is pinned by unit tests.Why
The phone is a client of the same host rules. After #27766 the host starts a hidden agent when asked. The phone refusing first would leave the phone the only place where hiding still locks an agent out. Removing the checks, instead of adding a "launch anyway" path, keeps one meaning for hiding everywhere: menus and automatic picks skip the agent, and an explicit choice starts it.
Linked Issue
Follows #27766 (desktop and host). No separate issue.
Visual Proof
Live phone QA on 4447ca5 (iOS simulator, paired to a hidden desktop test copy running #27766's final head; real Claude/Codex; settings changed only in the desktop Settings UI).
New-tab menu when no agent can be offered:
AI button when no saved agent is set and none can be picked:
Claude picked on the sheet, then hidden on the computer: Create still starts Claude, and the "won't start Claude while it's hidden" line correctly does not show (that line is only for a computer running an Orca from before #27766):
Testing
pnpm --dir mobile rpc:record(whole set) thenrpc:diff: exactly the 9 goldens above moved. Re-recording the committed tree reproduces them byte for byte. Re-recording after each review round moved nothing further.pilot-recordings,family-recordings),pilot-mutants,family-mutants,rpc-recording-through-bridge,derived-goldens,adapter-seam, the C1/C5 page-closure anddivergence-classestests, plus the unit tests for every changed module and the create route (use-new-workspace-create-submit,use-mobile-tasks-workspace-create-actions,worktree-create-launch-operation,agent-launch-mobile-replay,mobile-agent-launch-architecture,worktree-create-retry,blank-workspace-create,source-workspace-create,pr-ai-triage-launch,new-worktree-agent-selection,workspace-agent-selection,NewWorktreeModal,use-new-workspace-runtime-context,use-mobile-session-terminal-create-agent-launch,use-mobile-tasks-provider-view-projection,host-workspace-list-action-error).tsc --noEmit) and the mobile test-typecheck ratchet (scripts/check-tests-typecheck-ratchet.mjs, which CI's mobile job runs): pass.orca-ci-checks --no-typecheck: 21 of 23 pass, and the changed-code quality gate is among them. The two failures are not from this diff.typecheck:cifails locally in its bun runner (Script not found "check"), as feat(agent-launch): hiding an agent only hides it from pickers #27766 also hit.audit:code-quality:nativereports import cycles in mobile files this PR doesn't touch (transport/host-client-hooks.ts,transport/client-context.tsx,terminal/document/*). This PR only removes imports, so it can't add a cycle.Tests changed:
new-worktree-agent-selection.test.tsandworkspace-agent-selection.test.ts: each screen's selection logic keeps a user pick that is hidden in Settings.use-new-workspace-create-submit.test.ts(new): the computer has hidden the sheet's default agent since the sheet loaded. An untouched automatic pick is re-picked to another agent, and the same agent picked by the user starts. A third case: an older computer refuses that user-picked hidden agent with onlyagent_session_operation_unknown, and the sheet shows the sentence naming Claude. Two negative cases (an automatic pick, and a user pick that is shown in Settings) show the code exactly as on main.worktree-create-launch-operation.test.ts,agent-launch-mobile-replay.test.ts: the two cases whose failure answers a resend now also expectreplayed: true. Nothing else about them changed.use-mobile-tasks-workspace-create-actions.test.ts(new): the same two cases for the task-page create. The re-pick case pins create's fallback when no agent is passed; it also passes without this PR. The drawer's one-line change, passing its agent only when you picked it, is checked by typecheck and reading; the drawer can't render under the test runner without React Native.pr-ai-triage-launch.test.ts: new test. An AI button whose saved agent is hidden sends that agent to the host, and an older host's refusal comes back to the user in the host's words. It fails on the old code, which refused on the phone before sending anything.I manually tested these changes locally
Automated tests added/updated, or explained why not below
Review
Agent skill upstream boundary
Notes
Checklist
N/Awith reason (after shots from live phone QA above)pnpm lint,pnpm typecheck,pnpm test, andpnpm buildpass (or CI will cover; local preferred)