Skip to content

fix(speech): treat aborted as benign; only abort when listening (#197) - #235

Merged
dzianisv merged 1 commit into
mainfrom
fix/197-phantom-voice-alert
Nov 13, 2026
Merged

dzianisv merged 1 commit into
mainfrom
fix/197-phantom-voice-alert

Conversation

@dzianisv

Copy link
Copy Markdown
Owner

Fixes #197

Ports the speech hunks from open PR #182 standalone — a mechanical port of already-reviewed hunks, not a fresh design. None of #182's other fixes are included, and #182's speech-errors.test.ts is not ported.

Diff summary

  • New src/lib/speech-errors.ts — pure, import-free helper isBenignSpeechError(code) returning true for 'no-speech' and 'aborted'. Single place classifying benign speech errors. Its doc comment states only what is documented: abort() emits aborted, and events are module-global.
  • src/lib/speech.ts — error handler uses isBenignSpeechError(event.error) instead of the inline 'no-speech' check, so a module-global aborted from another screen's cleanup no longer sets an error; unmount cleanup only calls abort() if this hook instance actually started listening, tracked with a listeningRef synced via useEffect (not assignment during render); the misleading abort() is a no-op when not listening comment is corrected in place.

Why

Backing out of a subagent session showed a phantom "Voice input failed" alert on the parent screen with the mic never used: the child screen's unmount cleanup called abort(), the module-global aborted event reached the parent's still-mounted error listener, and only no-speech was carved out. Real voice errors still surface — only no-speech and aborted are treated as benign.

Gate (local checkout, branch head a8b376e, base e844493)

  • npm ci — added 794 packages in 45s
  • npm run check:versions — Version metadata aligned: 0.4.17 (44)
  • npm run typecheck — clean (tsc --noEmit, exit 0)
  • npm test — 380 pass, 0 fail

Context: issue thread comment 6855175743 (context pack) and design-gap comment 6855230505 on #197. No release/tag from this PR — release is a separate act after merges land.

@dzianisv

Copy link
Copy Markdown
Owner Author

Stage 4 review — PASS

PR: #235 — fix(speech): treat aborted as benign; only abort when listening (#197)
Head SHA: a8b376e41d3ffab70d3d02b2a8e1acc43bb415df (verified by API readback and local checkout)
Test-count delta: 0 — test files 42 → 42 (no test files added or removed)

How reviewed

Read the full #197 thread, context pack (comment 6855175743), design-gap comment (6855230505), and the PR diff, then reviewed the real checkout at the head SHA. Note: ~/workspace/custom-skills/claude-code-review/SKILL.md does not exist on this VM (no matching skill found), so the review followed the task method directly: acceptance criteria checked one by one against the diff, each evidence claim independently validated.

Acceptance criteria — all verified

  1. No phantom alert on back-nav without voice use — two layers: unmount cleanup now skips abort() when this hook instance never started listening, and even if an aborted event arrives, it is classified benign and never reaches setError. (On-device confirmation remains a post-merge manual residual, as the pack notes — no e2e harness for the native speech module exists.)
  2. Real voice errors still alert — the else branch is unchanged: setError(event.message || event.error), and the alert consumer in app/session/[id].tsx is untouched.
  3. aborted benign, like no-speech — isBenignSpeechError returns true for exactly "no-speech" and "aborted" (src/lib/speech-errors.ts).
  4. Cleanup aborts only when this hook instance actually started listening — listeningRef is synced via useEffect on [listening], never written during render; the unmount cleanup reads listeningRef.current.
  5. No changes outside useSpeech / speech error handling — diff is exactly 2 files, +28/−4: new src/lib/speech-errors.ts, src/lib/speech.ts edits. No applicationId change, no other files touched.

Checks that passed

  • Misleading comment corrected — the false "abort() is a no-op when not listening" is replaced with the corrected wording.
  • Helper doc comment limited to documented claims — no unverified Kotlin "ALWAYS emits" assertion. The "aborted is the documented outcome of calling abort()" claim checks out against the library README: "You'll also receive an error event (with code "aborted") when calling .abort()" and error-code table "aborted — The user called ExpoSpeechRecognitionModule.abort()".
  • Gates re-run on the PR head by this reviewer (not the implementer's numbers): npm run typecheck clean (exit 0); check:versions aligned 0.4.17 (44); full suite npm test — 380 pass / 0 fail.

Findings

No findings. No validated defect, so nothing blocks merge.

Notes (not findings)

  • The speech.ts cleanup comment asserts the Kotlin-level detail that abort() emits the global event even when idle — Stage 3 already flagged that as reported-but-unverified. It does not affect correctness (the benign classification covers the event either way), so it is not a finding.
  • CI at observation time: Typecheck and unit tests ✅, test ✅, Secret scan (gitleaks) ✅; build, Unsigned iOS Simulator build, and activation-e2e still in progress; build-fdroid skipped. Combined status: pending.

Port of the speech hunks from open PR #182, standalone. isBenignSpeechError
classifies 'no-speech' and 'aborted' as benign (aborted is the documented
outcome of abort(); events are module-global, so another screen's cleanup
abort() used to surface a phantom 'Voice input failed' alert on the parent
session screen). Unmount cleanup now only calls abort() if this hook
instance actually started listening, tracked via a listeningRef synced in
useEffect.

Fixes #197
@dzianisv
dzianisv force-pushed the fix/197-phantom-voice-alert branch from a8b376e to c657b22 Compare November 13, 2026 14:38
@dzianisv
dzianisv merged commit 38316ae into main Nov 13, 2026
8 checks passed
dzianisv added a commit that referenced this pull request Nov 13, 2026
Merges the scoped auto-scroll fix for #155. Stage 4 verdict PASS (comment 6856376173) on the reviewed head 8c7747a; branch updated to a2146cf by a clean merge of main (#235 only, reviewed code unchanged); CI green on a2146cf. Device proof remains owed on the v0.4.18 build before it ships (owner phone check).
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.

Phantom 'Voice input failed' alert when navigating back from a child/subagent session

1 participant