uttrflow / uttrflow/uttrflow-swift

A release the keyboard tap never delivers leaves the microphone open until the 4-minute cap, and the key-state poll the docs describe no longer exists

Open
#609 0 comments 0 reactions 0 assignees View on GitHub
area:dictation bug help wanted P2
Dominant language
Swift
Stars
4
Forks
17
Avg merge
3h 32m
Merged PRs (30d)
277

Description

## What happens

`Docs/stuck-recording.md` ("The release event never arrives") says both shortcut monitors compare against the real key state every 250 ms, so a release that is never delivered is noticed within a quarter of a second.

That is only true of `CarbonHotkeyMonitor` (`Sources/UttrflowInput/CarbonHotkeyMonitor.swift:138-157`), which the dictation shortcut no longer uses. Since 4459aab ("Give the keyboard one source, and every shortcut one recogniser") the dictation shortcut is watched by `ActivationMonitor` over `SystemKeyboard` (`Sources/Uttrflow/AppDelegate.swift:572`), and `HeldModifierMonitor`, which held the poll, was deleted. Nothing in `ActivationMonitor`, `HotkeyRecogniser` or `DictationController` reads `CGEventSource.keyState` or `NSEvent.modifierFlags`; the recogniser's `isDown` changes only when a stroke arrives (`Sources/UttrflowCore/Keyboard/HotkeyRecogniser.swift:74-76`).

So in hold-to-talk, any key-up or flags-changed event the listen-only session tap does not receive leaves the pipeline in `.recording`:

- the tap being disabled while the key is held (#141's re-enable happens after the event is already lost, and the second disable is not re-enabled at all);
- secure event input turning on mid-hold, for example a password prompt taking focus, which withholds key events from other processes' taps (whether a modifier-only flags change, as the Fn hold uses, is withheld too needs checking on a real Mac).

The recording then runs until `DictationLimit` finishes it at 4 minutes and inserts everything it heard into whatever is focused.

## Why it matters

A microphone that stays open after the user let go is both a broken dictation and a privacy surprise: the user believes it stopped, walks away or starts talking to someone, and up to four minutes of that is transcribed and typed. The doc tells maintainers this case is already handled.

## How to reproduce / measure

- Code: `grep -rn "keyState\|modifierFlags" Sources/UttrflowInput Sources/UttrflowCore/Keyboard Sources/UttrflowPipeline` finds only `CarbonHotkeyMonitor`.
- Real machine (help wanted): hold the dictation shortcut, trigger a secure-input password prompt, release the key, and watch whether the recording ends.

## Acceptance criteria

- While a dictation hold is down, the real key state is reconciled on a timer (only while held, as `Docs/stuck-recording.md` requires for energy), and a release it finds is delivered to the controller once.
- A test drives the reconciliation with a fake key-state reader: a hold whose release never arrives is released within the poll interval.
- `Docs/stuck-recording.md` describes the monitors that exist.

Contributor guide

Open the contributing guide

Research direction

Start with Sources/Uttrflow/AppDelegate.swift:572, Sources/UttrflowCore/Keyboard/HotkeyRecogniser.swift:74-76, ActivationMonitor, and the existing CarbonHotkeyMonitor polling at Sources/UttrflowInput/CarbonHotkeyMonitor.swift:138-157. Trace how a held dictation shortcut reaches DictationController, then identify the timer and key-state reader seam needed for a fake-reader test. Done means a missed release is delivered within the poll interval, the test covers it, and Docs/stuck-recording.md matches the current monitors.

Written by the indexing model from the issue text.

Assessment

Tech stack
macos, swift
Domain
accessibility, desktop, documentation, testing-qa
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
55/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.