Skip to content

iOS dictation: fix conventions-lint namespace-enum (main-wide red) + harden mic-start crash - #6238

Merged
lawrencecchen merged 4 commits into
mainfrom
fix-ios-dictation-crash
Jun 17, 2026
Merged

lawrencecchen merged 4 commits into
mainfrom
fix-ios-dictation-crash

Conversation

@lawrencecchen

@lawrencecchen lawrencecchen commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Two #6197 (on-device voice dictation) follow-ups

#6197 merged with two latent issues because the iOS checks that catch them are non-required (the iOS false-green gap):

1. package-conventions-lint — main-wide red (priority)

ComposerDictationTextMerge was a caseless namespace-enum with a static func, which the namespace-type rule bans:

ERROR namespace-enum  ComposerDictationTextMerge.swift:70  enum ComposerDictationTextMerge (caseless, static members) -> scope onto the owning type

Since the lint isn't a required check, #6197 merged red and every PR against current main now fails package-conventions-lint. Converted it to an instantiable struct (base/transcript stored, merged computed) — keeps the dictation-scoped name + host-testability, satisfies the lint. Updated the controller call site and the merge tests. ./scripts/lint-ios-package-conventions.sh now passes.

2. mic-start crash hardening (issue #6217)

beginRecognition() guarded only channelCount > 0 before installTap, but installTap raises an uncatchable Obj-C exception on an invalid input format (zero sample rate) that do/catch can't trap. Now also require sampleRate > 0 and failStart() gracefully. This addresses the reported "cmux Beta crashes when using the microphone." I'll confirm the exact frame against the device crash report and add an Obj-C exception shim if it turns out to be a format mismatch rather than an invalid format.

No behavior change to the happy path. Unblocks the fleet's conventions-lint immediately.

🤖 Generated with Claude Code


Note

Medium Risk
Changes the live microphone/dictation startup path and Swift 6 actor isolation around system callbacks; behavior is mostly crash prevention and edge-case guards, but regressions in permission or transcript merging would be user-visible.

Overview
Fixes the mic-tap crash and related dictation failures by avoiding main-actor-isolated closures on Speech/AVFoundation and audio realtime threads, and by tightening startup guards.

When speech and mic permissions are already resolved, start now uses synchronous resolvedAuthorization() and goes straight to recognition (or unavailable if denied), skipping async TCC callbacks that were trapping on repeat taps. First-time prompts still use async authorization, but completions are nonisolated / @Sendable with a single Task { @MainActor in } before mutating controller state.

Recognition setup moves the engine tap and recognition result handler into nonisolated builders so callbacks are not main-actor-isolated off the render/arbitrary queues. beginRecognition also requires sampleRate > 0 before installTap, and empty final/partial transcripts are ignored so a graceful stop does not overwrite text already committed from partials.

Reviewed by Cursor Bugbot for commit 94bf8c6. Bugbot is set up for automated code reviews on this repo. Configure here.


View with Codesmith Autofix with Codesmith
Need help on this PR? Tag /codesmith with what you need. Autofix is disabled.


Summary by cubic

Fixes the iOS dictation mic-tap crash and prevents text loss. Authorization and audio callbacks are now Swift 6–safe, permission prompts are gated to the first tap, and input formats are validated before the tap.

  • Bug Fixes
    • Made permission flow nonisolated: requestAuthorization and requestMicrophonePermission use @Sendable completions; start reads resolvedAuthorization() and hops once to @MainActor.
    • Built nonisolated audio callbacks: tap block and recognition result handler come from nonisolated factories, then hop to main before touching state.
    • Validated input format before installTap: require format.channelCount > 0 and format.sampleRate > 0; otherwise call failStart().
    • Ignored empty final transcripts on stop to avoid wiping previously committed partial text.

Written for commit 94bf8c6. Summary will update on new commits.

Review in cubic

Summary by CodeRabbit

  • Bug Fixes

    • Improved dictation start reliability by ensuring UI/state updates occur on the main thread after mic authorization.
    • Added stronger audio input validation (checks channel count and sample rate) to prevent starting dictation with invalid audio configurations.
  • Improvements

    • Refined microphone/authorization handling to better support permission callbacks arriving on background threads, reducing start failures and related issues.

@vercel

vercel Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
cmux Ready Ready Preview, Comment Jun 17, 2026 1:32am
cmux-staging Building Building Preview, Comment Jun 17, 2026 1:32am

@coderabbitai

coderabbitai Bot commented Jun 16, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

ComposerDictationController.swift fixes actor isolation issues in authorization callbacks and strengthens audio format validation. Authorization helpers are made nonisolated with @Sendable completions to safely receive system callbacks on background queues. The start(existingText:onText:) method wraps its state transitions in Task { @mainactor in } to ensure correct executor hopping. In beginRecognition(), the tap-install guard now validates both channel count and sample rate, calling failStart() instead of crashing on invalid audio format.

Changes

ComposerDictationController async/await and audio safety

Layer / File(s) Summary
Authorization helper refactoring to nonisolated with Sendable
Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/ComposerDictationController.swift
requestAuthorization and requestMicrophonePermission are now nonisolated and accept @Sendable (Bool) -> Void completions instead of main-actor-isolated closures, allowing system authorization callbacks to arrive safely on background queues without isolation violations.
Main actor hop in start() authorization completion
Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/ComposerDictationController.swift
The authorization completion in start(existingText:onText:) now wraps the actor-isolated state mutations and beginRecognition() call in Task { @mainactor in }, ensuring proper executor transition when the refactored nonisolated callback fires on a background queue.
Audio format validation before tap installation
Packages/CmuxMobileShellUI/Sources/CmuxMobileShellUI/ComposerDictationController.swift
beginRecognition() adds format.sampleRate > 0 validation alongside the existing format.channelCount > 0 check before installing the AVAudioEngine tap; calls failStart() instead of attempting installation with invalid audio format.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • manaflow-ai/cmux#6197: Both PRs modify the start() and beginRecognition() paths in ComposerDictationController.swift to adjust authorization callback handling, main actor isolation, and audio format validation.

Poem

🐰 Off the main thread callbacks hop,
@Sendable lets the race stops!
Format checking guards with care,
Zero rate? We fail, not crash there.
Actor safety, audio true —
Dictation flow, through and through! 🎤


Important

Pre-merge checks failed

Please resolve all errors before merging. Addressing warnings is optional.

❌ Failed checks (1 error, 1 warning)

Check name Status Explanation Resolution
Cmux Swift Blocking Runtime ❌ Error PR introduces Task.sleep(2.5 seconds) for watchdog timeout in stop() method—a readiness/synchronization wait that violates swift-blocking-runtime.md rules for production code. Replace Task.sleep-based watchdog with cancellation-aware Timer, DispatchSourceTimer, or real async signal from recognition task completion instead.
Docstring Coverage ⚠️ Warning Docstring coverage is 41.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (19 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately summarizes the two main changes: fixing the namespace-enum lint violation and hardening the mic-start crash path.
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.
Cmux Swift Actor Isolation ✅ Passed The PR correctly addresses Swift 6 actor isolation by making authorization methods nonisolated with @Sendable completions and using explicit Task { @mainactor in } hopping instead of implicit...
Cmux Expensive Synchronous Load ✅ Passed PR adds no expensive synchronous loaders (disk/syscall) onto main actor or interactive paths. Changes are to audio API calls and authorization callback isolation, not heavy corpus loaders like Rest...
Cmux Cache Substitution Correctness ✅ Passed PR changes iOS voice dictation with no cache substitution in persistence/history/undo/snapshot paths; only transient UI state and authorization callbacks.
Cmux No Hacky Sleeps ✅ Passed The PR's primary changes are in ComposerDictationController.swift (Swift file), which is explicitly out of scope as "Swift timing and blocking primitives are covered by swift-blocking-runtime.md"...
Cmux Algorithmic Complexity ✅ Passed PR contains only string operations (merged text), audio format validation (O(1)), and event-driven state machine callbacks; no nested collection scans, batch rescans, or unbounded algorithmic compl...
Cmux Swift Concurrency ✅ Passed PR uses modern Swift concurrency patterns: Task @MainActor for OS callback boundaries (allowed), stored/managed watchdog Tasks with proper lifecycle, and nonisolated functions with @Sendable comple...
Cmux Swift @Concurrent ✅ Passed The Swift changes properly use nonisolated with @Sendable completions for off-main callbacks, and explicitly hop to @MainActor via Task enqueuing rather than assertions. No missing or invalid @conc...
Cmux Swift File And Package Boundaries ✅ Passed ComposerDictationController.swift (411 lines) exceeds 400-line limit but qualifies as iOS app-target UI code with inseparable audio/speech orchestration; pure logic (TextMerger, State) extracted se...
Cmux Swift Logging ✅ Passed ComposerDictationController.swift and ComposerDictationTextMerge.swift contain no print, debugPrint, dump, NSLog, ad hoc logging, or MainActor-coupled Logger declarations. App/runtime production co...
Cmux User-Facing Error Privacy ✅ Passed No user-facing error messages, alerts, or recovery copy were added or modified. Failures result in state becoming .unavailable, which disables the mic button without exposing vendor names, intern...
Cmux Full Internationalization ✅ Passed PR contains no new user-facing strings, no string catalog additions/edits, and no web i18n changes; changes are internal crash-hardening and concurrency fixes only.
Cmux Swiftui State Layout ✅ Passed PR modifies internal implementation of existing @Observable class (ComposerDictationController); no new @Published/@State/@observableobject, GeometryReader, or render-time mutations introduced.
Cmux Architecture Rethink ✅ Passed All changes are legitimate architectural fixes with clear owners and invariants: Swift 6 executor isolation for authorization callbacks (nonisolated with @Sendable completions), audio format valida...
Cmux Swift Auxiliary Window Close Shortcuts ✅ Passed PR changes only affect ComposerDictationController.swift and ComposerDictationTextMerge.swift in CmuxMobileShellUI, which contain no NSWindow, NSPanel, NSWindowController, SwiftUI Window, or Window...
Cmux Source Artifacts ✅ Passed All changed files are legitimate hand-written Swift source, tests, and configs with no local tool output, generated code, or build artifacts.
Description check ✅ Passed The PR description is comprehensive and addresses the template requirements, though some sections are less formally structured.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-ios-dictation-crash

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 and usage tips.

@greptile-apps

greptile-apps Bot commented Jun 16, 2026 •

Copy link
Copy Markdown
Contributor

Greptile Summary

Fixes two follow-up issues from the #6197 on-device voice dictation merge: converts the caseless namespace-enum ComposerDictationTextMerge to an instantiable struct (satisfying package-conventions-lint), and hardens mic-start against uncatchable Obj-C exceptions by validating sampleRate > 0 before installTap. The core concurrency fix makes the entire permission-request chain nonisolated/@Sendable so system callbacks that fire on non-main queues under iOS 26 never invoke a main-actor-isolated closure and cannot trap in swift_task_isCurrentExecutor.

  • Fast-path for repeat taps: resolvedAuthorization() reads permission status synchronously; already-granted sessions skip the async TCC round-trip entirely and go straight to beginRecognition(), which is the exact path that was crashing on repeat taps.
  • Nonisolated authorization chain: requestAuthorization and requestMicrophonePermission are now nonisolated with @Sendable completions; a single Task { @MainActor in } in start() owns the actor hop before any isolated state is touched.
  • Nonisolated tap and result-handler factories: makeTapBlock and makeRecognitionResultHandler extract the closures passed to installTap and recognitionTask into nonisolated factory methods so the closures carry no main-actor isolation and cannot trap on the audio render or recognition-callback queues.

Confidence Score: 5/5

Safe to merge. Happy-path behavior is unchanged; the only runtime differences are graceful-failure on bad audio input routes (previously an uncatchable crash) and elimination of a main-actor executor trap on iOS 26 repeat taps.

The actor-isolation model is correct throughout: nonisolated factory methods return closures with no main-actor isolation, a single Task { @mainactor } hop owns all state mutations, and weak-self captures prevent retain cycles. The synchronous fast-path for already-resolved permissions is safe because everything runs on the main actor and the state transitions are atomic. The sampleRate guard is a conservative addition that only changes behavior on devices that previously would have thrown an uncatchable exception. No new blocking primitives, no user-facing strings, and no source-control artifacts are introduced.

No files require special attention.

Important Files Changed

Filename Overview
Packages/iOS/CmuxMobileShellUI/Sources/CmuxMobileShellUI/ComposerDictationController.swift Refactors permission-request chain to nonisolated/@sendable, adds synchronous fast-path for already-resolved permissions, extracts tap/result-handler factories to nonisolated methods, and adds sampleRate > 0 guard before installTap. Logic is sound and actor-isolation pattern is correct throughout.

Reviews (4): Last reviewed commit: "iOS dictation: fix mic-tap crash, text l..." | Re-trigger Greptile

…ic crash)

cmux Beta crashed when using the mic. beginRecognition() guarded only
channelCount > 0 before installTap, but installTap raises an UNCATCHABLE Obj-C
exception (IsFormatSampleRateAndChannelCountValid) on an invalid input format
(zero sample rate), which do/catch can't trap. Also require sampleRate > 0 and
failStart() gracefully. Addresses issue #6217; will confirm the exact frame
against the device crash report and add an Obj-C exception shim if the crash is
a format MISMATCH rather than an invalid format.

(The conventions-lint fix this branch originally carried is superseded by main's
ComposerDictationTextMerger refactor; reset to main and kept only the mic guard.)
Real root cause (device crash log, build 1.0.3 20260616105751): EXC_BREAKPOINT
in swift_task_isCurrentExecutor -> dispatch_assert_queue_fail, inside the TCC
permission callback. Tapping the mic requests speech+mic permission;
SFSpeechRecognizer / AVAudioApplication invoke their completion handlers on
their own (non-main) queues. The closures were inferred main-actor (written in
this @mainactor controller), so under Swift 6 / iOS 26 the runtime asserts
executor isolation when the system calls them off-main and traps.

Fix (modern Swift 6 isolation): requestAuthorization + requestMicrophone-
Permission are now nonisolated with Sendable completions, so no main-actor
closure is invoked off-main; start() hops to the main actor exactly once via a
Task @mainactor enqueue (not a synchronous executor assertion).

Also keeps the installTap sample-rate guard (separate latent crash on an
invalid input format). Compile verified on CI; runtime confirmed by iOS 26
device dogfood. Closes #6217.
lawrencecchen and others added 2 commits June 16, 2026 18:27
…prompt

The mic-tap crash on iOS 26 was a Swift-concurrency executor trap: closures
the compiler inferred as @mainactor (the permission completions, the AVAudioEngine
tap block, and the SFSpeechRecognitionTask result handler) were invoked off the
main thread by TCC / the realtime audio thread / the recognition queue, tripping
swift_task_isCurrentExecutor -> dispatch_assert_queue_fail (EXC_BREAKPOINT).

Fixes:
- Build the tap block and the result handler in `nonisolated` factory methods so
  they are genuinely non-isolated and safe to invoke off-main.
- Resolve speech+mic authorization synchronously when already determined
  (`resolvedAuthorization()`), so a repeat tap never re-invokes the crashing async
  TCC callback; only a never-determined permission uses the async request path.
  This also keeps the permission prompt gated to the first mic tap (status reads
  never prompt).
- Make the authorization completions nonisolated/@sendable.
- Ignore an empty final transcript on stop so it cannot wipe the words the
  partial results already committed to the composer.

Verified on-device (iPhone, iOS 26) through the full start -> listen -> stop cycle
with no crash and text preserved.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
lawrencecchen added a commit that referenced this pull request Jun 17, 2026
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrencecchen
lawrencecchen merged commit e43a074 into main Jun 17, 2026
23 of 26 checks passed
@lawrencecchen
lawrencecchen deleted the fix-ios-dictation-crash branch June 17, 2026 01:55
lawrencecchen added a commit that referenced this pull request Jun 20, 2026
…hor (#6299)

* ios: sustain hold-to-repeat Backspace via a virtual delete-repeat anchor

Hold-to-repeat Backspace on the iOS soft keyboard never repeated. Device
debugging confirmed the cause: with an EMPTY virtual document, UIKit no-ops
the keyboard's software delete and never calls deleteBackward() while the key
is held, so backspace did not reach the Mac. Forcing hasText == true alone
(the earlier #6288 attempt) does not change this.

Fix: convert TerminalInputTextView from a UITextView subclass to a bare
UIView conforming to UIKeyInput + UITextInput that exposes a one-character
virtual document. When not composing it shows a hidden zero-width
"delete-repeat anchor" (toggling \u{200B}/\u{2060}); each empty-buffer
deleteBackward() forwards a real backspace via onBackspace and brackets the
anchor toggle in inputDelegate.textWillChange/textDidChange so UIKit re-arms
its document-driven key-repeat timer. IME composition (markedText) suppresses
the anchor; a delete during composition cancels the composition instead of
forwarding a stray backspace.

Supersedes the hasText-only approach in
#6288 (can be closed in favor of
this); related to #6238.

Single commit (not red/green): the fix is a whole-view rewrite from
UITextView to UIView/UIKeyInput/UITextInput, so the failing test and the view
it tests are inseparable. The new TerminalInputBackspaceRepeatTests asserts
the observable invariants that sustain the repeat (non-empty 1-char document
when idle, N deletes => N backspaces with the document re-armed non-empty
after each, the textWillChange/textDidChange re-arm firing per delete, the
anchor char alternating, and composing suppressing both the anchor and any
stray backspace), so reverting to UITextView, dropping the anchor, or removing
the re-arm all fail it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refresh Swift file-length budget for documentless input rewrite

TerminalInputTextView grew with the hand-rolled UIKeyInput/UITextInput
conformance (the proven delete-repeat fix). Accepting as known debt; splitting the
conformance into an extension file is a follow-up (file-org).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ios: shorten IME composition on composing-delete instead of cancelling

While composing CJK/Japanese/Korean/pinyin text the documentless input
view cancelled the entire marked composition on the first Backspace and
emitted nothing, so mid-composition correction was impossible (en+ja are
supported locales). Restore the prior UITextView behavior: deleteBackward()
during composition now drops the last grapheme (Character, not a UTF-16
unit, so multi-scalar glyphs are never split) and re-presents the shortened
candidate via setMarkedText, which brackets the change in
textWillChange/textDidChange and derives markedTextRange/selectedTextRange
from the new string so UIKit and the IME stay in sync. Removing the last
unit clears/unmarks the composition. Still emits zero bytes to the Mac while
composing; the non-composing forward-DEL + anchor re-arm path is unchanged.

Update the regression test: a multi-char composition shortens by one per
delete (still composing, zero onBackspace), clears on the last unit, and a
subsequent non-composing delete forwards a backspace again. Adds a
grapheme-boundary case (flag emoji = one grapheme, multiple UTF-16 units).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refresh Swift file-length budget after IME composing-delete fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: conform mock UITextInputDelegate to the iOS 18.4 SDK (conversationContext)

main's bump to the iOS 26 SDK made conversationContext(_:didChange:) a required
UITextInputDelegate method; the backspace-repeat test's mock delegate didn't
implement it, failing the cmuxFeatureTests build. Add the unused stub.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
ShubhamPatilsd pushed a commit to emergent-inc/mosaic that referenced this pull request Jul 9, 2026
…hor (#6299)

* ios: sustain hold-to-repeat Backspace via a virtual delete-repeat anchor

Hold-to-repeat Backspace on the iOS soft keyboard never repeated. Device
debugging confirmed the cause: with an EMPTY virtual document, UIKit no-ops
the keyboard's software delete and never calls deleteBackward() while the key
is held, so backspace did not reach the Mac. Forcing hasText == true alone
(the earlier #6288 attempt) does not change this.

Fix: convert TerminalInputTextView from a UITextView subclass to a bare
UIView conforming to UIKeyInput + UITextInput that exposes a one-character
virtual document. When not composing it shows a hidden zero-width
"delete-repeat anchor" (toggling \u{200B}/\u{2060}); each empty-buffer
deleteBackward() forwards a real backspace via onBackspace and brackets the
anchor toggle in inputDelegate.textWillChange/textDidChange so UIKit re-arms
its document-driven key-repeat timer. IME composition (markedText) suppresses
the anchor; a delete during composition cancels the composition instead of
forwarding a stray backspace.

Supersedes the hasText-only approach in
manaflow-ai/cmux#6288 (can be closed in favor of
this); related to manaflow-ai/cmux#6238.

Single commit (not red/green): the fix is a whole-view rewrite from
UITextView to UIView/UIKeyInput/UITextInput, so the failing test and the view
it tests are inseparable. The new TerminalInputBackspaceRepeatTests asserts
the observable invariants that sustain the repeat (non-empty 1-char document
when idle, N deletes => N backspaces with the document re-armed non-empty
after each, the textWillChange/textDidChange re-arm firing per delete, the
anchor char alternating, and composing suppressing both the anchor and any
stray backspace), so reverting to UITextView, dropping the anchor, or removing
the re-arm all fail it.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refresh Swift file-length budget for documentless input rewrite

TerminalInputTextView grew with the hand-rolled UIKeyInput/UITextInput
conformance (the proven delete-repeat fix). Accepting as known debt; splitting the
conformance into an extension file is a follow-up (file-org).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* ios: shorten IME composition on composing-delete instead of cancelling

While composing CJK/Japanese/Korean/pinyin text the documentless input
view cancelled the entire marked composition on the first Backspace and
emitted nothing, so mid-composition correction was impossible (en+ja are
supported locales). Restore the prior UITextView behavior: deleteBackward()
during composition now drops the last grapheme (Character, not a UTF-16
unit, so multi-scalar glyphs are never split) and re-presents the shortened
candidate via setMarkedText, which brackets the change in
textWillChange/textDidChange and derives markedTextRange/selectedTextRange
from the new string so UIKit and the IME stay in sync. Removing the last
unit clears/unmarks the composition. Still emits zero bytes to the Mac while
composing; the non-composing forward-DEL + anchor re-arm path is unchanged.

Update the regression test: a multi-char composition shortens by one per
delete (still composing, zero onBackspace), clears on the last unit, and a
subsequent non-composing delete forwards a backspace again. Adds a
grapheme-boundary case (flag emoji = one grapheme, multiple UTF-16 units).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* Refresh Swift file-length budget after IME composing-delete fix

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

* test: conform mock UITextInputDelegate to the iOS 18.4 SDK (conversationContext)

main's bump to the iOS 26 SDK made conversationContext(_:didChange:) a required
UITextInputDelegate method; the backspace-repeat test's mock delegate didn't
implement it, failing the cmuxFeatureTests build. Add the unused stub.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
@lawrencecchen
lawrencecchen restored the fix-ios-dictation-crash branch July 18, 2026 10:19

This branch was successfully deployed

1 active deployment
Preview – cmux — 94bf8c6f Deployed Jun 17, 2026 by vercel[bot]
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