Skip to content

Dictation key: Right Option starts on press again - #2014

Merged
r3dbars merged 3 commits into
mainfrom
claude/project-thread-g2a4v5
Oct 5, 2026
Merged

r3dbars merged 3 commits into
mainfrom
claude/project-thread-g2a4v5

Conversation

@r3dbars

@r3dbars r3dbars commented Oct 5, 2026 •

Copy link
Copy Markdown
Owner

Requested by Justin · project thread

Why

Before: on main and in the 1.1.70 draft, the default dictation key is Right Option on Hold or tap. That registers it as Push to Talk. Option-M (meetings) also uses Option, so every Right Option press went through the 0.14 s chord delay (modifierChordDelay, DispatchQueue.main.asyncAfter) before dictation started. In 1.1.69, Right Option was the hands-free key and started on press (#1858). So each hold now starts about 0.14 s later. start_latency_ms doesn't show it, because its clock starts at startDictation, after the delay.

After: a Push to Talk key on a shared modifier starts on press. If a key was typed in the last second, or the press would stop a take, it still waits out the chord delay. A key that goes down inside the 0.14 s window after the press (Option-M, Right Option+E) is a combo: the detector sends .comboInterrupted and the start is dropped through abandonDictationStartForModifierCombo, the same path hands-free uses. A key later in the hold is just typing during the hold, the same as on main, and never drops the take.

Product Impact

  • Affects: dictation
  • Lane: dictation reliability
  • Why this matters: every hold of the default key starts about 0.14 s sooner. This undoes the start-speed regression that the one-key change brought into 1.1.70. Justin approved it for 1.1.70 in the project thread.

What changed

  • PhysicalShortcutMatcher.swift: new PushToTalkModifierComboWindow. It wraps HandsFreeModifierComboTracker, but a keyDown counts as a combo only inside the window.
  • ContextCaptureEngine.swift:
    • The Push to Talk flagsChanged press on a shared modifier uses firesSharedModifierOnPress and fires on press.
    • It arms the window, and a combo inside the window clears the active Push to Talk key and sends (.dictationPushToTalk, .comboInterrupted).
    • The window's flagsChanged only ends tracking. It never consumes the release, so the normal release and tap logic still run.
    • It's reset wherever the hands-free tracker is.
  • ContextCaptureEngine+DictationKeys.swift:
    • The Push to Talk press records the session it started.
    • handlePhysicalDictationPushToTalkComboInterrupted drops that session, or the queued start if the press only queued one.
    • The hands-free press and combo handlers moved here unchanged, so ContextCaptureEngine.swift stays at its 842-line baseline. shouldAcceptHotkeyAction went from file-private to internal for that move.
  • Tests/PhysicalShortcutMatcherTests.swift: a combo inside the window, a key late in a hold (it must not drop the take), a key after release, and a key after a tap reset.
  • Sources/Capture/AGENTS.md: docs and the manual-check line.

Known and documented: on a built-in or wired mic, the start click plays on key press. So Right Option+M or Right Option+E after a 1 s pause clicks and briefly shows the island before the start is dropped. 1.1.69's Right Option hands-free key did the same.

How I checked it

  • scripts/dev/agent-preflight.sh
  • bash scripts/dev/linux-checks.sh (runs without Swift): 65 passed, 0 failed
  • Selected checks from .agents/test-matrix.yml for the files changed (CI checks: green on 4d0461d)
  • bash build.sh --no-open (CI app-build: green on 4d0461d)
  • bash run-tests.sh (CI build-and-test: green on 4d0461d)
  • Performance budget passed
  • bash run-integration-smoke.sh (not touched)
  • swift test (CI spm-tests: green on 4d0461d)
  • bash run-e2e-smoke.sh
  • Manual check: see below (goes on the 1.1.70 Mac test list)

Checks I could not run, and why:

  • Nothing compiled locally. This is a Linux cloud session with no Swift toolchain, so CI is the build.

Mac or hardware test still needed? Yes, on the default key (Right Option, Hold or tap):

  1. Hold Right Option, say a sentence, release. It pastes, and the start click comes on press. On main it comes about 0.14 s after the press.
  2. Hold Right Option, talk, press an arrow or Esc mid-hold, keep talking, release. It still pastes.
  3. Tap Right Option quickly. It keeps listening, and the next press stops and pastes (unchanged).
  4. Press Right Option+M quickly. The meeting starts and no dictation is left running.
  5. Type a word, then within a second press Right Option+E. You get é and no dictation.

Risk Review

  • Privacy / local-first behavior reviewed (no new data)
  • No new analytics properties or Sentry tags
  • check-source-pins.py --changed-only: PASS
  • No storage impact
  • No user-facing copy change
  • Release/update impact: fixes a regression in the unpublished 1.1.70; Justin approved it for 1.1.70
  • Independent deep review of the full diff: the first review found the whole-hold combo bug (fixed in 2a34745); re-review on the fixed head: READY
  • No UI change
  • No private data

Notes

Found during the dictation speed pass (/mnt/project-files/speed/2026-10-05/dictation.md). Review verdicts: /mnt/project-files/reviews/next-release/2014.md.

Agent handoff

COORD_DONE: GREEN | this PR | Right Option push-to-talk fires on press, combo only inside 0.14 s | none | none (approved for 1.1.70) | linux-checks, CI green on 4d0461d, deep review READY | lanes used: Codex=n/a (cloud session); Claude=n/a (cloud session); Local=n/a; Windows=n/a | Mac steps 1-5 in the 1.1.70 test

🤖 Generated with Claude Code

https://claude.ai/code/session_01DEL5vVKCBRYCvxaT5GS1nA

The one-key change made Right Option (the default dictation key) Push to
Talk. Push to Talk on a modifier that Option-M also uses waited out the
0.14 s chord delay before every start, so each hold began 0.14 s later
than Right Option did in 1.1.69, where it fired on press.

Push to Talk on a shared modifier now fires on press like the hands-free
key: unless a key was typed in the last second or the press would stop a
take. A combo key while it's held (Option-M, Right Option+E) sends
.comboInterrupted and the start is dropped quietly through
abandonDictationStartForModifierCombo, the same path hands-free uses.

The hands-free press handlers move to ContextCaptureEngine+DictationKeys
so ContextCaptureEngine.swift stays under its line baseline.

Claude-Session: https://claude.ai/code/session_01DEL5vVKCBRYCvxaT5GS1nA
@r3dbars r3dbars self-assigned this Oct 5, 2026
Review of the first commit: the Push to Talk combo tracker stayed armed
for the whole hold, so Esc, an arrow or Return a minute into a hold
would have dropped the take and its audio with no message.

PushToTalkModifierComboWindow counts a key as a combo only inside the
0.14 s chord window after the press, the window the delayed press used
to wait out. A key later in the hold is typing, as on main. Tests cover
inside the window, late in a hold, after release, and after a tap reset.

Claude-Session: https://claude.ai/code/session_01DEL5vVKCBRYCvxaT5GS1nA
@r3dbars
r3dbars marked this pull request as ready for review October 5, 2026 22:50
@r3dbars
r3dbars merged commit c9bd24d into main Oct 5, 2026
12 checks passed
@r3dbars
r3dbars deleted the claude/project-thread-g2a4v5 branch October 5, 2026 22:50
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