Skip to content

Count a long pinned take with almost no audio as empty - #1882

Merged
r3dbars merged 3 commits into
mainfrom
claude/transcription-speed-percentiles-yyabey
Sep 28, 2026
Merged

r3dbars merged 3 commits into
mainfrom
claude/transcription-speed-percentiles-yyabey

Conversation

@r3dbars

@r3dbars r3dbars commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Why

Before: #1880 moves a mic off the pinned recorder after two empty held takes in a row. Empty means "no speech" or "signal but no words". A recorder that delivers no audio at all ends a take as "recording too short" instead. The scoring skipped those as taps, so a recorder that never delivers audio would never trip the fallback.

After: a "recording too short" take counts as empty when the recorder was open for at least 1.5 s and less than 0.5 s of audio arrived. The 1.5 s is the same line isAccidentalStart already draws between a mis-tap and a stalled mic. Taps under 1.5 s still aren't scored. A healthy mic that started slowly still delivers most of its hold, so it isn't scored either.

This follows up the 1.1.66 empty-dictation reports. A PostHog check found 3 of 8 Macs with zero working dictations on 1.1.66:

This PR closes the one remaining gap that could hide a dead recorder.

Product Impact

  • Affects: dictation
  • Lane: dictation reliability
  • Why this matters: on a Mac where the recorder opens but delivers nothing, the person sees "too short" on every take and never gets moved back to the engine. With this change they're moved after two.

What changed

  • PinnedDictationSpeedPath.outcome gains heldSeconds and audioSeconds. recordingTooShort is .empty when held is at least accidentalStartMaximumPress (1.5 s) and audio is under deadRecorderMaximumAudio (0.5 s). Otherwise it's nil.
  • ParakeetPinnedDictationRecording.startedUptime is reset once the capture's start returns. Stop reads the time before the drain, so neither a queued start nor the drain counts as time the recorder was open (deep-review S1).
  • PinnedDictationSpeedPathTake carries heldSeconds and audioSeconds. audioSeconds is the take's recoveredRecordingTimeline.totalDurationSeconds (deep-review S2).
  • Tests and the Speech CLAUDE.md bullet are updated.

How I checked it

  • bash scripts/dev/linux-checks.sh (runs without Swift): 48 passed, 0 failed
  • python3 scripts/dev/check-source-pins.py --changed-only: PASS
  • bash build.sh --no-open (CI app-build, green on c45538f)
  • bash run-tests.sh (CI checks, green on c45538f)
  • bash run-integration-smoke.sh (CI spm-tests, green on c45538f; the Parakeet lifecycle smoke compiles this file)

Checks I could not run, and why:

  • This session has no Swift toolchain, so the build and tests ran in CI only.

Mac or hardware test still needed? If yes: the steps, the log line or UI change each step should show, and whether each step fails on main:

  • There's no easy way to make a healthy mic deliver nothing, so the logic is covered by the unit test. On a normal Mac, holding the key for 2 s or more while speaking should still paste, and a tap should still show "Hold the key while you speak".

Risk Review

  • Privacy / local-first behavior reviewed (no new telemetry)
  • New analytics properties or Sentry tags avoid the sanitizer's drop fragments (none added)
  • Checked the text-pin tests for every file I edited
  • Storage path or migration impact reviewed (same UserDefaults key)
  • Public-facing copy stays concrete and matches current product scope (no copy changes)
  • Release/update impact reviewed
  • Agent PRs got an independent deep review of the full diff (READY at c45538f)
  • UI changes include sanitized .agent-review/visuals/ evidence: not applicable, no UI changes
  • No private transcripts, audio, tokens, personal paths, or customer data are included

Notes

Agent handoff

COORD_DONE: BRIEF | this PR | long pinned takes with almost no audio now count toward the fallback | none | merge for 1.1.67 on a person's go | linux-checks, source pins, CI green | deep review READY

🤖 Generated with Claude Code

https://claude.ai/code/session_016DYGa1i8HDv497ewpWgaCM

A pinned recorder that never delivers audio ends each take as
"recording too short", which the speed-path scoring skipped as a tap.
Past the 1.5 s mis-tap limit it now counts as empty, so two of those in a
row move the mic back to the engine like two silent takes do.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016DYGa1i8HDv497ewpWgaCM
The held time now runs from the capture's start returning to Stop, so
a queued start or a slow drain no longer counts as the recorder being
open. A too-short take also needs under 0.5 s of audio, so a healthy
mic that started slowly on a short hold is never scored as dead.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_016DYGa1i8HDv497ewpWgaCM
@r3dbars r3dbars changed the title Count a held key that records no audio as an empty pinned take Count a long pinned take with almost no audio as empty Sep 27, 2026
@r3dbars
r3dbars marked this pull request as ready for review September 27, 2026 18:53
Speech CLAUDE.md: keeps main's warmup sentence (#1874) and this branch's
0.5 s rule for empty takes.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@r3dbars
r3dbars merged commit 23392cb into main Sep 28, 2026
8 checks passed
@r3dbars
r3dbars deleted the claude/transcription-speed-percentiles-yyabey branch September 28, 2026 02:24
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.

2 participants