fix(ios): surface new global/ensemble chat create failures instead of hanging#5
Merged
Merged
Conversation
Starting a new GLOBAL or ENSEMBLE chat could spin on "Creating…" forever. The Mac legitimately declines some creates (ensemble mode disabled; global not shared while the workspace allowlist is empty), but `send` only fires the create callback when the action is ACCEPTED — so a denial never reached NewChatBootstrapView, which never stopped spinning and never showed why. - createEmptyThread now propagates failure via send's onAck (onCreated(nil) on a denied/failed ack); the generic send is unchanged. - NewChatBootstrapView shows a failure state with the Mac's reason (model.lastActionMessage) + a Try Again button, and clears the latched requestedKey so retry actually re-requests. Verified: swift build + 69 Kit tests + iOS app build (iPhone 17 sim). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
boggspa
added a commit
that referenced
this pull request
Jun 18, 2026
Build 21 ships the merged iOS work: turn-based guest participation (PR #3, Mac-side bridge), active-chat/sidebar/Settings-sheet state preservation across settings changes (PR #4), and surfaced new global/ensemble chat create failures with retry instead of an infinite spinner (PR #5). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
boggspa
added a commit
that referenced
this pull request
Jun 23, 2026
Roadmap #5, compounds with #1. Wrap MarkdownMessage in React.memo with a comparator that re-renders only when the rendered output can change: the markdown content, the chat scope (appChatId), or the identity registry <AgentMention> reads (chat.providerMetadata.agentIdentities). The `chat` prop is the whole currentChat — a new object on every coalesced flush — so a reference compare would defeat the memo; comparing the identity-relevant slice instead means a per-frame transcript re-render only re-renders the ONE message that streamed, not every visible bubble. Also useMemo the block split so a re-render not caused by a content change doesn't re-run the O(n) string scan. renderToStaticMarkup output unchanged; markdown + transcript tests green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
boggspa
added a commit
that referenced
this pull request
Jun 25, 2026
…e-inject, actionable denial, Settings signpost (PR5) Make agents aware the image tools (image_edit/svg_rasterize/image_generate) exist, without heavy prompt injection (the user's point #5). Tool descriptions were already rich (Layer 1); this adds the prompt + UI layers: - PromptComposition: the write-capable cloud preamble now names the image tools (one line). Bumped the preamble version v2->v3 so existing resumable sessions re-learn it once. Added promptNeedsImageToolsHint + a resumed-session re-inject: when the full preamble is suppressed (resumed gemini/claude/codex) and THIS turn is image-related, re-inject just the image-tools note. Skipped on global/plan runs (which never get the tools). - index.ts: image tools are gated as File changes, so a read-only/plan preset denied them with a generic 'File changes denied by TaskWraith.' — now an actionable message naming the write-capable-preset (and key) requirement. - SettingsPanel: the MCP bridge card now states it enables the image tools for Grok & Cursor (Claude/Codex/Gemini get them natively; image_generate also needs a key). 35 PromptComposition tests (incl. 5 new); typecheck node+web clean. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Symptom
On iOS you can't start a new global chat or a new ensemble chat — tapping the option spins on "Creating…" forever (a regular workspace chat works).
Root cause
The create→navigate path is the same for all three variants. The difference: the Mac legitimately declines some creates —
ensembleModeEnabled === false(src/main/index.ts:16493),src/main/RemoteWorkspaceAllowlist.ts:359),…but
RemoteSessionModel.sendonly fires the create callback when the ack is accepted (RemoteSessionModel.swift:2615). A declined create therefore never reachedNewChatBootstrapView, which kept spinning on "Creating…" with no error and no retry. That silent hang is exactly what "can't start" looks like.Fix (iOS)
createEmptyThreadnow propagates failure throughsend'sonAck→onCreated(nil)on a declined/failed ack (the genericsendis untouched, so no blast radius on other actions).NewChatBootstrapViewgains a failure state: it shows the Mac's actual reason (model.lastActionMessage, e.g. "Ensemble mode is disabled on your Mac.") + a Try Again button, and clears the latchedrequestedKeyso retry re-requests.So instead of an infinite spinner you now see why it didn't start and can retry.
Important
This makes the failure visible + actionable; it does not override the Mac-side policy gates. If the reason shown is "ensemble mode disabled" → enable Ensemble mode on the Mac; if it's the global-allowlist message → share at least one workspace to the device. If the revealed reason is something unexpected (e.g. global fails even with workspaces shared), that surfaced message will tell us exactly what to chase next.
Verification
swift build+ 69 Kit tests + iOS app build (iPhone 17 sim) — all green.🤖 Generated with Claude Code