Skip to content

fix(desktop): surface global-hotkey registration conflicts instead of swallowing them (swarm slice 1/N)#10193

Merged
Git-on-my-level merged 2 commits into
mainfrom
fix/ptt-chat-silent-failure-boundary
Jul 21, 2026
Merged

fix(desktop): surface global-hotkey registration conflicts instead of swallowing them (swarm slice 1/N)#10193
Git-on-my-level merged 2 commits into
mainfrom
fix/ptt-chat-silent-failure-boundary

Conversation

@Git-on-my-level

@Git-on-my-level Git-on-my-level commented Jul 21, 2026

Copy link
Copy Markdown
Collaborator

What

Converts a silent OS-boundary failure into a reported one. GlobalShortcutManager.registerHotKey swallowed the RegisterEventHotKey OSStatus with a bare NSLog, and registerAskOmi then logged "Registered Ask Omi shortcut" unconditionally — so when Carbon rejected the combo the failure was both silent and actively misleading (the log claimed success). This is the first, evidence-led slice of the PTT/chat silent-failure swarm (Lane A): it makes the swarm's primary metric — silent failures converted to reported ones — move by one.

Swarm context — this is slice 1 of N

Six independent read-only scouts investigated the swarm axes (synthesized findings in the commit/PR comments). This PR ships exactly one highest-confidence candidate. The rest are deliberately deferred:

Finding Disposition
S1 — Global hotkey registration swallowed This PR. Verified 3 ways; narrowest (single production file + additive telemetry); the only raw-NSLog OS-boundary failure site in the PTT/shortcut/audio paths.
S2 — Secure Event Input suppresses PTT Dropped after verification. PTT modifier-only activation uses NSEvent .flagsChanged, which SEI does not suppress (only .keyDown/.keyUp are). All four PTT presets are modifier-only, so the dominant path is unaffected. Filing the minority custom-key carve-out + the unrelated SEI barge-in sidebar as issues.
Audio retryOrGiveUp silent give-up 📋 Follow-up issue. Strong candidate (mirrors onSilentMicDetected, no enum extension), but broader (3 files, touches the live PTT capture path). Better as its own slice.
AgentBridge session-token refresh (chat_bridge) 📋 Follow-up issue. Textbook recordFallback case (credential switch), single call site, but has no hermetic unit-test seam today; verification needs a new tier: fault flow (clone chat-fault-5xx.yaml as status:401). The fault-injector scout confirmed fault flows belong after a Lane A fix.
S4 — hardcoded sampleRate = 16000 No patch — already resolved. Those three sites are constants fed to 16kHz-trained models/endpoints, not divisors. The real divide-by-zero boundary (resampledFrameCapacity) is already guarded + tested (AudioCaptureResampleCapacityTests.swift). #9649/#10105 were prior instances of that boundary. No third patch warranted.
Lane B fault vocab (audio/permission/display) 📋 Follow-up. omi-fault-inject.sh is HTTP-only by design; non-HTTP faults belong as bridge actions driving the app's own seams. Lands after a Lane A fix for the flow to prove.
Settings-UI conflict surfacing ("prompt for a different binding") 📋 Follow-up. The brief's full S1 fix is "surface a real conflict state in shortcut settings." This PR is the observability half; the user-visible settings half is a separate, strings-touching PR.

No ChatProvider.swift or FloatingControlBarWindow.swift changes. No notch typed chat. No speculative retries / timing tweaks / blanket fallbacks.

The change

  • GlobalShortcutManager: extract a pure HotKeyRegistrationOutcome classifier over the Carbon OSStatus (.registered / .alreadyInUse / .otherFailure). registerHotKey routes the failure branch to the incident path; the misleading success log is gated on .registered. Success-path branch logic is unchanged.
  • DesktopDiagnosticsManager: new recordHotkeyRegistrationFailed(osStatus:keycode:modifiers:isConflict:) wrapper around the existing private recordUserVisibleIssue. Three closed-set additions: hotkey_conflict to safeIncidentLabel; osstatus/keycode/modifiers to allowedIncidentExtraKeys and cloudIncidentSnapshotKeys (so the Carbon detail reaches both the snapshot and the Sentry attachment). These are labels/keys on an existing event, not new PostHog events or one-off counters.

Telemetry-contract choice: incident, not fallback

Per docs/agents/fallback-telemetry.md, recordFallback is for branches that switch provider/mode/correctness or fail open. A hotkey that won't register is a hard-terminal failure (no continue, nothing to switch to) → the incident mechanism (recordUserVisibleIssue → PostHog user_visible_issue + Sentry .warning), exactly like recordPTTSilentTurn. recordFallback is deliberately not used.

Evidence standard

  • Machine configuration: a Mac where another app (Raycast / Alfred / Magnet / BetterTouchTool / a screenshot tool / …) or a macOS System Settings → Keyboard → Shortcuts entry — even a disabled one — has already registered the Ask Omi combination globally. The default Ask Omi binding is ⌘O (keyCode 31, ShortcutSettings.defaultAskOmiShortcut), the universal File → Open combo and therefore unusually collision-prone.
  • API basis: Carbon/HIToolbox eventHotKeyExistsErr == -9878 (CarbonEvents.h). Hammerspoon issue Transcription dies every few seconds, device reconnects #1261 reproduces this exactly (RegisterEventHotKey failed: -9878 when the combo is owned by System Settings — including unticked entries — or another app). Pure machine axis, not our code.
  • Verification: GlobalShortcutManagerRegistrationTests (5 cases) — classifier purity (noErr.registered; -9878.alreadyInUse; -50/-9870.otherFailure) and the telemetry contract (the incident snapshot carries area=startup, failure_class=hotkey_conflict, phase=startup, and osstatus/keycode/modifiers survive the closed allowlist filter). Existing DesktopDiagnosticsManagerTests / ShortcutSettingsTests / PushToTalkShortcutActivationTests (27 cases) stay green. The real conflict cannot be staged on our machines (it depends on what else the user has installed), so the controllable-seam test is the prescribed verification per the swarm brief's evidence standard. A full named-bundle launch is not additionally informative for this slice: the success path is provably unchanged (if outcome == .registered ⟺ old if status == noErr), so the unit test already proves a normal registration does not trip a false-positive hotkey_conflict incident.
  • Expected telemetry/log outcome: at app launch on an affected machine, a desktop_health_event user_visible_issue with failure_class=hotkey_conflict (Sentry-captured on prod/beta, 60 s de-duped per area:failureClass), where today there is none. On unaffected machines: no new incident; the previously-unconditional "Registered Ask Omi shortcut" log now only prints on real success.

Product invariants affected

  • INV-CHAT-1 (path-based match only). GlobalShortcutManager.swift falls under the desktop/macos/Desktop/Sources/FloatingControlBar/** glob. This change adds registration-failure telemetry only; it does not touch the shared transcript, kernel projection, floating viewport, journal write-path, or introduce any second store — so the chat-continuity contract is unaffected and no guard-test update is needed.

Definition of Done

  • Behavior change covered by a behavioral test through a controllable seam
  • Focused tests green (5 new + 27 existing); module compiles (xcrun swift build via the test target's @testable import Omi_Computer)
  • swift-format + SwiftLint (0 violations / 2445 files) + check_desktop_test_quality.py clean
  • scripts/pr-preflight --suggest → INV-CHAT-1 path-match named below (contract unaffected)
  • Failure-Class declared below and validated with scripts/failure-class
  • No new TODO/FIXME/HACK; no new CI gate/ratchet/guide

Failure-Class: none

No existing failure class fits the silent-OS-boundary archetype (the registry covers backend/release/runtime contracts, none for hotkey or OS-boundary observability), and new cannot be bootstrapped pre-merge: a new class definition requires a non-empty evidence_prs list of merged PR numbers. This is the first instance of the swarm's silent-OS-boundary class; a follow-up can register FC-silent-os-boundary-failure once this PR lands and supplies the first evidence number.

Review in cubic

… swallowing them

RegisterEventHotKey returns eventHotKeyExistsErr (-9878) when another app — or a
macOS System Settings > Keyboard > Shortcuts entry, even a disabled one — already
owns the (keyCode, modifiers) pair globally. GlobalShortcutManager swallowed this
with a bare NSLog, and registerAskOmi then logged "Registered Ask Omi shortcut"
unconditionally, so the failure was both silent and actively misleading: the Ask
Omi shortcut (default Cmd+O, a heavily-owned combo) never fired and we had no
signal. This is a property of the user's machine, not our code.

Per docs/agents/fallback-telemetry.md this is a hard-terminal failure (no
provider/mode switch, nothing to fail open to), so it routes through the incident
mechanism, not recordFallback. Extract the OSStatus decision into a pure
HotKeyRegistrationOutcome classifier — the controllable seam, since the real
Carbon call cannot be made to return -9878 hermetically — and surface conflicts
via the existing recordUserVisibleIssue path (area=startup,
failure_class=hotkey_conflict, phase=startup). osstatus/keycode/modifiers are
preserved through the closed incident-extra allowlist and cloud snapshot keys.
The success-path branch logic is unchanged.

First evidence-led slice of the PTT/chat silent-failure swarm: converts a silent
failure into a reported one (the swarm's primary metric). User-visible conflict
surfacing in shortcut settings is intentionally a follow-up.

Verification: GlobalShortcutManagerRegistrationTests (5 cases) cover the
classifier purity (noErr / -9878 / other) and the telemetry contract (incident
snapshot fields + extras survive the allowlist filter). Existing
DesktopDiagnosticsManagerTests / ShortcutSettingsTests /
PushToTalkShortcutActivationTests (27 cases) stay green. The real conflict
condition cannot be staged on our machines — it depends on what else the user
has installed — so the controllable-seam test is the prescribed verification per
the swarm brief's evidence standard.

Machine configuration: a Mac where another app (Raycast/Alfred/Magnet/
BetterTouchTool/…) or a System Settings > Keyboard > Shortcuts entry (even a
disabled one) has registered the Ask Omi combination globally.
API basis: Carbon/HIToolbox eventHotKeyExistsErr == -9878 (CarbonEvents.h).
Expected telemetry: a desktop_health_event user_visible_issue with
failure_class=hotkey_conflict at app launch (Sentry-gated by !isNonProduction +
60s dedup), where today there is none.

Failure-Class: none

No existing failure class fits the silent-OS-boundary archetype, and `new`
cannot be bootstrapped pre-merge (a new class definition requires a non-empty
evidence_prs list of merged PR numbers). First instance of the swarm's
silent-OS-boundary class; registerable as FC-silent-os-boundary-failure once
this PR supplies the first evidence number.
@Git-on-my-level Git-on-my-level added the no-changelog-needed Skip desktop changelog enforcement for internal-only changes label Jul 21, 2026

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 7f4bafec1a

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +125 to +129
DesktopDiagnosticsManager.shared.recordHotkeyRegistrationFailed(
osStatus: Int(status),
keycode: keyCode,
modifiers: modifiers,
isConflict: outcome == .alreadyInUse)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Avoid reporting stale shortcut retries as conflicts

When a user re-enables Ask Omi by selecting a preset/custom shortcut, the existing settings flow sets askOmiEnabled = true before assigning the new askOmiShortcut, and that first setter posts askOmiShortcutChanged. This new failure branch can therefore capture a hotkey_conflict incident for the stale saved combo even though the immediately following registration for the user's chosen combo succeeds, polluting Sentry/PostHog with false conflicts. Please record the incident only after the final shortcut value is applied or suppress these intermediate re-registration failures.

Useful? React with 👍 / 👎.

…ailure

An independent review of the pure classifier landed in the prior commit found a
correctness regression at the noErr/no-ref boundary. classifyRegistration(noErr)
returned .registered unconditionally, so when RegisterEventHotKey reports noErr
but stores a nil EventHotKeyRef (a real Carbon edge), registerHotKey's else
branch recorded a registration failure while registerHotKey returned .registered
— and registerAskOmi then logged "Registered Ask Omi shortcut" for a shortcut
that will never fire. The failure was both recorded and claimed as success in the
same call. The pre-refactor code treated status==noErr with a nil ref as failure;
this restores that semantics.

Narrow fix: registration is successful ONLY when the Carbon status is noErr AND a
non-nil EventHotKeyRef is stored. classifyRegistration now takes ref-presence as a
second pure input (refPresent: Bool), keeping it the controllable test seam — the
real Carbon call cannot be made to return noErr with a nil ref hermetically. The
conflict flavor (eventHotKeyExistsErr) is preserved regardless of ref so the
isConflict telemetry discrimination is unchanged.

Behavioral regression coverage for the exact boundary is added
(testClassifyNoErrWithNilRefIsOtherFailure) and the existing pure-classifier
cases updated for the new refPresent parameter.

Verification: GlobalShortcutManagerRegistrationTests (6 cases) pass, including
the new noErr/nil-ref boundary case. make preflight (18/18 diff-scoped checks)
green; SwiftLint 0 violations; swift-format clean.

Failure-Class: none

No existing failure class fits the silent-OS-boundary archetype (same as the
prior commit): the available classes concern deploy/Cloud Run/dict-merge/module
boundaries, none a Carbon noErr/nil-ref edge. `new` cannot bootstrap pre-merge.
@Git-on-my-level
Git-on-my-level merged commit de8b737 into main Jul 21, 2026
25 of 27 checks passed
@Git-on-my-level
Git-on-my-level deleted the fix/ptt-chat-silent-failure-boundary branch July 21, 2026 07:37
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

no-changelog-needed Skip desktop changelog enforcement for internal-only changes

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant