Skip to content

feat(mobile): optional send-immediately setting for iPhone voice input - #14807

Open
Dwite wants to merge 5 commits into
pingdotgg:mainfrom
Dwite:t3code/voice-input-auto-send-ios
Open

Dwite wants to merge 5 commits into
pingdotgg:mainfrom
Dwite:t3code/voice-input-auto-send-ios

Conversation

@Dwite

@Dwite Dwite commented Oct 2, 2026 •

Copy link
Copy Markdown

Problem

iPhone voice input always stops at the draft. After dictation, the user confirms the transcript, waits, and then taps send. People who dictate short follow-ups and do not edit the text pay that second tap every time. The same request is in Ideas discussion #14719.

Change

Adds Settings → Keyboard & voice → Voice input → Send immediately on iPhone. It is off by default, so dictated text still lands in the composer for review.

With it on, a finished transcript goes through the normal send path:

  • New tasks and existing threads use their existing send checks. A blocked send stays in the composer as a draft and is not retried later.
  • The whole composer message is sent, including text and attachments that were already there.
  • The setting is read when recording starts. Cancelling, a failed transcription, or a draft that changed owner never sends.
  • Main now keeps dictation running after the composer leaves the screen (fix(mobile): keep dictation running across navigation behind an edge pill #15502). Send immediately only sends from a composer that is still on screen when the transcript lands. If the user left it, the text stays in the draft unsent.
  • The scheduled task prompt field also has dictation, but it has nothing to send. The setting does not apply there; the text stays in the field.

The setting is device-local. The settings row is renamed from Keyboard to Keyboard & voice because it now holds both. docs/user/composer.md gains one paragraph in the voice input section.

Surfaces: iPhone only, which is the only client with voice input. No contract, server, or provider changes.

Scope and approval

There is no maintainer approval for this yet. I am submitting it as a focused configuration option for an established capability.

  • What already exists: on-device voice input in the iPhone composer, documented in Voice input on iPhone.
  • What the option controls: whether a finished transcript is submitted through the existing send path, or left in the composer as it is today.
  • Why it stays within that capability: the default does not change, no new workflow or surface is added, and every existing send check still applies.

This is the iPhone half of #12893, which was closed because it also added browser speech recognition. The web changes are not in this PR. If you see opt-in sending as a product behavior choice that needs approval first, tell me and I will take it to #14719 instead.

Verification

Checked on this branch (81a4e6af9, merged with main at d72021099):

  • tsc --noEmit in apps/mobile passes.
  • The composer still sends through handleSend with no follow-up argument, the same call the send button makes, so a dictated message during a running turn follows the user's queue or steer preference.
  • vp test run src/features/voice-input src/features/threads src/features/settings src/persistence in apps/mobile: 465 tests pass in 54 files.
  • useVoiceInputController.test.ts (12 tests) renders the hook inside main's real VoiceInputProvider with a mocked recorder and transcriber. It covers default-off review, sending exactly once after the composer shows the transcript, and the cases that must not send: a blocked send, an empty or failed transcript, a cancel, a draft edited before the composer showed it, a screen that is not in front, and a composer that moved to another draft.
  • Format check passes. Lint on the changed files reports warnings and no errors.

The merge with #15502 was more than a conflict fix. Dictation moved from a per-composer controller to a shared session, so the send logic was ported to it: the hook marks a pending send when the transcript lands in a mounted, focused composer, then sends once that composer has rendered the transcript.

Captured for #12893 on the iPhone 17 Pro simulator, iOS 26.5, at the original commit f22247568:

iPhone settings before and after

Find the setting Default: off Enabled
Settings entry Review before sending Send immediately enabled

Before video · After video

Those checks covered default-off, toggling, and persistence after an app restart. The videos show the settings flow, not speech recognition.

Not checked:

  • The app was not rebuilt or run on a simulator after the merges with main, which now includes the Expo 58 update, orchestrator V2, and the shared dictation session (fix(mobile): keep dictation running across navigation behind an edge pill #15502). The captures above predate all three.
  • Real microphone capture and speech recognition were never exercised. The send path is covered by the unit tests with a mocked recorder and transcriber.
  • react-test-renderer moved from 19.2.6 to 19.3.0 to match the mobile React version on main.

Original change: GPT-6 in Codex (#12893). Split, rebase, and V2 merge: Claude Opus 5.5 in Claude Code.

🤖 Generated with Claude Code

Adds an opt-in "Send immediately" setting for iPhone voice input. It is
off by default, so dictated text still lands in the composer for review.

Split out of pingdotgg#12893 without the web voice input changes.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@github-actions github-actions Bot added size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list. labels Oct 2, 2026
@macroscopeapp

macroscopeapp Bot commented Oct 2, 2026 •

Copy link
Copy Markdown
Contributor

Approvability

Verdict: Approved at 81a4e6a

Macroscope's review found this PR approvable — This adds a device-local, off-by-default option to auto-submit completed iPhone dictation through the existing composer send paths. Existing review-before-send behavior remains unchanged, and focused tests cover the pending-transcription, focus, draft ownership, cancellation, and blocked-send cases.

You can add or adjust custom eligibility rules. Learn more.

@juliusmarminge juliusmarminge added the macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews label Oct 2, 2026 — with ChatGPT Codex Connector
@coderabbitai

coderabbitai Bot commented Oct 2, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository: pingdotgg/t3code/.coderabbit.yaml
  • Review profile: CHILL
  • Plan: Advanced
  • Run ID: 426cb413-f6e3-4604-a018-9fecf70787b8
📥 Commits

Reviewing files that changed from the base of the PR and between e4b8ea0 and 4ba7e45.

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml
📒 Files selected for processing (7)
  • apps/mobile/src/Stack.tsx
  • apps/mobile/src/features/settings/SettingsKeyboardRouteScreen.tsx
  • apps/mobile/src/features/threads/NewTaskDraftScreen.tsx
  • apps/mobile/src/features/threads/ThreadComposer.tsx
  • apps/mobile/src/features/voice-input/useVoiceInputController.ts
  • apps/mobile/src/persistence/mobile-preferences.ts
  • docs/user/composer.md
🚧 Files skipped from review as they are similar to previous changes (1)
  • apps/mobile/src/Stack.tsx

Included review availability: This review used your included allowance. Your plan provides up to 10 included reviews per hour; 9 remain after this review.


📝 Walkthrough

Walkthrough

Mobile settings now include an optional voice-input setting that can submit a committed composer draft immediately. The controller captures the setting when recording starts and checks the draft and input state before submission.

Changes

Voice input settings and submission

Layer / File(s) Summary
Preference and settings control
apps/mobile/src/persistence/mobile-preferences.ts, apps/mobile/src/features/settings/SettingsKeyboardRouteScreen.tsx, apps/mobile/src/Stack.tsx, apps/mobile/src/features/settings/SettingsRouteScreen.tsx, docs/user/composer.md
The preference shape and sanitizer accept voiceInputSendImmediately. The settings screen adds the “Send immediately” switch and updates its keyboard screen label. The user guide describes the setting.
Voice transcript submission
apps/mobile/src/features/voice-input/useVoiceInputController.ts, apps/mobile/src/features/threads/NewTaskDraftScreen.tsx, apps/mobile/src/features/threads/ThreadComposer.tsx, apps/mobile/src/features/voice-input/useVoiceInputController.test.ts, apps/mobile/package.json
The controller captures the preference when recording starts and queues the committed draft for submission when enabled. It submits only if the owner and draft text still match and input is enabled. New-task and thread callers gate their callbacks. Tests cover submission, blocked sends, cancellation, transcription failure, preference capture, and owner changes.

Priority: ⬇️ Low

Estimated code review effort: 3 (Moderate) | ~20 minutes

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant User
  participant VoiceInputController
  participant ThreadComposer
  participant handleSend
  User->>VoiceInputController: Confirm transcription
  VoiceInputController->>ThreadComposer: Call onSubmit when owner, draft text, and input remain valid
  ThreadComposer->>handleSend: Invoke when canSend is true
Loading

Suggested reviewers: juliusmarminge

Merge Risk: ⚪ Minimal · up to 4ba7e

No actionable issue remains that should block merging. Real microphone and speech-recognition behavior was not retested after the rebase, so normal device validation is still appropriate.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to 4ba7e

The option is off by default and retains existing send checks. However, automatic submission identifies the thread rather than the specific draft being recorded. A same-thread transition out of a queued-message edit could therefore submit a new agent request instead of saving the intended edit.

Retained concerns

  • Medium · reliability · inferred: Automatic submission is bound to environment/thread and text, not the actual draft or queued-run identity. A same-thread draft transition preserving text can remain valid to the voice controller while changing what submission does. In particular, recovery from a queued edit can move its text into the normal composer, allowing completion to enqueue new agent work instead of saving the original edit. Existing send guards limit exposure but do not enforce this identity invariant. This is an inferred execution-containment concern; the precise recovery interleaving was not demonstrated.
Security review details

Security Blast Radius

  • inferred — The identified failure mode is confined to draft and execution intent within the selected environment/thread. Resulting work inherits that thread's existing model, runtime and interaction mode; no new credential authority or cross-tenant route was established.

Security Findings and Attack Paths

  • inferred — A queued run starting or being cancelled can end edit mode and restore its text into an empty normal draft. If owner and text remain unchanged across that transition, voice completion can invoke the new automatic callback against the normal-send branch, potentially creating unintended additional agent work. This does not require a demonstrated remote attacker; it is a lifecycle failure affecting execution intent. The precise interleaving remains unverified.

Trust Boundaries and Controls

  • observed — Transcribed content reaches the existing caller submission paths. New tasks retain canStart and handleStart checks; threads retain canSend, per-thread in-flight suppression and state-layer content, attachment and model checks. These are counterevidence to a general send-control bypass, but do not bind submission to the captured queued-edit identity.

Resilience and Maintainability Implications

  • observed — Operation tokens and abort signals invalidate cancelled or superseded transcription. A global recording session prevents concurrent capture; failure and interruption paths avoid committing transcripts. Focus cleanup clears pending submission, and resource cleanup releases recordings and session ownership.

Hardening Proposals

  • proposed — Bind recording, commit and pending submission to the actual composer draft key and queued-run identity or generation. Invalidate automatic submission whenever that identity changes, including recovery that preserves identical text, and exercise queued-edit recovery through both commit and submission.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 8 files. (1 skipped: 1 … Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly and concisely describes the main change: an optional send-immediately setting for iPhone voice input.
Description check ✅ Passed The description includes all required sections. It explains the problem, implementation, scope, approval rationale, verification results, limitations, and UI evidence.
Full details: Docstring Coverage

Explanation

Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 8 functions across 8 files. (1 skipped: 1 unsupported.)

  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Autopilot is currently an internal CodeRabbit preview.


Comment @coderabbitai help to get the list of available commands.

Dwite and others added 4 commits October 3, 2026 13:35
…to-send-ios

# Conflicts:
#	apps/mobile/src/features/settings/SettingsKeyboardRouteScreen.tsx
#	apps/mobile/src/persistence/mobile-preferences.ts
Main added voice input to the scheduled task prompt field, which has
nothing to send. Make the controller's submit hook optional so "Send
immediately" only applies where a composer can send.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…to-send-ios

Main moved dictation into a shared session that keeps running after the
composer leaves the screen (pingdotgg#15502). Send immediately now rides on that
session: the hook marks a pending send when the transcript lands in a
composer that is still mounted and in front, and sends once that composer
has rendered the transcript. A transcript that lands anywhere else stays a
draft.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…to-send-ios

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

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

macroscope-review Opt PRs made by unvouched contributors in for Macroscope review. Vouched contributors auto-reviews size:L 100-499 changed lines (additions + deletions). vouch:unvouched PR author is not yet trusted in the VOUCHED list.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants