Three v0.1 AppDelegate UX holes wired in one branch (sequential commits, each touches App/AppDelegate.swift + adjacent App-target files).
#89 — Status panel + DevOverlay open on app start (closed in dca0695)
Debug builds default startupSurfacesMode = .diagnostic, which opens HUD + Status (top-right) + DevOverlay (bottom-right, Logs tab). Release builds default .minimal. Skipped during the first-launch wizard and under XCTest hosts. DevOverlayView gained an initialTab: parameter so the panel can open straight to Logs; DevOverlayWindow exposes setFrameOrigin(_:) + frameSize for AppDelegate positioning.
#88 — InputMonitoring TCC banner v1 hole (closed in 48fb8b9) BannerContent.inputMonitoringDenied is now nonDismissible: true — user must not be able to click past a denial that breaks global hotkeys (silent no-op is the failure mode the banner exists to prevent). New HUDBannerCoordinator.dismiss(id:) clears a banner programmatically without poisoning dismissedThisLaunch. AppDelegate.reprobeInputMonitoring fires on NSApplication.didBecomeActiveNotification and handles both transitions: was-denied → now-granted dismisses the banner and rebinds the global hotkey under fresh TCC; was-granted → now-denied re-enqueues. IOHIDCheckAccess is query-only so the foreground hook never prompts.
#87 — PTT (VOICE-13) wired (closed in 112f584) WizardState.pttHotkey is a new persisted shortcut under Jarvis.pttHotkey (independent of the summon-Jarvis hotkey). Ships unbound — CLAUDE.md hotkey hygiene forbids defaulting because Cmd+Shift+J and Option+Space collide with too many tools. AppDelegate.rebindPTTFromState is called from three sites: installVoice tail (after PushToTalk is constructed), the foreground TCC re-grant path, and the Settings Save/Clear handlers. Settings UI gains a "Push-to-talk hotkey" section with the same Save/Clear pattern as the summon hotkey, fed by a separate onPTTHotkeyChanged callback.
Audio-graph isolation (re: Voice CRIT lane)
PTT.bind installs NSEvent.addGlobalMonitorForEvents callbacks only — it does NOT touch the audio graph. The press path routes through VoiceController.pttDown, which uses the audio graph the wake-word DAG already owns. So this lane is safe to land in parallel with fix/voice-crit-v0.1 (wake-word fix). No feature flag needed.
All 17 grep-based boundary gates green (including check-no-modal-presentation.sh and check-single-writer-hudstate.sh)
swift test --package-path packages/DevOverlay — 14 tests pass (no regression from initialTab: addition)
swift test --package-path packages/Voice — 26 tests pass (no regression from PTT call-site addition)
New tests added:
3 in HUDBannerCoordinatorTests — programmatic dismiss-by-id clears non-dismissible banners, doesn't poison the id, no-op on unknown id.
3 in WizardStateTests — PTT hotkey defaults to nil, persists round-trip, independent of summon hotkey.
Manual cold-launch sanity (Debug archive): HUD + Status (top-right) + DevOverlay (bottom-right, Logs tab) all visible; toggle off InputMonitoring → banner appears non-dismissible; toggle on in System Settings → banner clears on foreground; bind PTT in Settings → hold-to-talk activates STT.
Notes
DO NOT merge until the user has manually verified the three flows above.
The PR branch was created from a fresh develop checkout — clean of the worktree's fix/voice-crit-v0.1 state.
No package-level API removals; DevOverlayView.init, DevOverlayWindow.init, SettingsView.init, and SettingsWindowController.open all extended with default-valued parameters so existing call sites stay valid.
## Summary
Three v0.1 AppDelegate UX holes wired in one branch (sequential commits, each touches `App/AppDelegate.swift` + adjacent App-target files).
- **#89 — Status panel + DevOverlay open on app start** (closed in dca0695)
Debug builds default `startupSurfacesMode = .diagnostic`, which opens HUD + Status (top-right) + DevOverlay (bottom-right, Logs tab). Release builds default `.minimal`. Skipped during the first-launch wizard and under XCTest hosts. `DevOverlayView` gained an `initialTab:` parameter so the panel can open straight to Logs; `DevOverlayWindow` exposes `setFrameOrigin(_:)` + `frameSize` for AppDelegate positioning.
- **#88 — InputMonitoring TCC banner v1 hole** (closed in 48fb8b9)
`BannerContent.inputMonitoringDenied` is now `nonDismissible: true` — user must not be able to click past a denial that breaks global hotkeys (silent no-op is the failure mode the banner exists to prevent). New `HUDBannerCoordinator.dismiss(id:)` clears a banner programmatically without poisoning `dismissedThisLaunch`. `AppDelegate.reprobeInputMonitoring` fires on `NSApplication.didBecomeActiveNotification` and handles both transitions: was-denied → now-granted dismisses the banner and rebinds the global hotkey under fresh TCC; was-granted → now-denied re-enqueues. `IOHIDCheckAccess` is query-only so the foreground hook never prompts.
- **#87 — PTT (VOICE-13) wired** (closed in 112f584)
`WizardState.pttHotkey` is a new persisted shortcut under `Jarvis.pttHotkey` (independent of the summon-Jarvis hotkey). Ships unbound — CLAUDE.md hotkey hygiene forbids defaulting because Cmd+Shift+J and Option+Space collide with too many tools. `AppDelegate.rebindPTTFromState` is called from three sites: `installVoice` tail (after PushToTalk is constructed), the foreground TCC re-grant path, and the Settings Save/Clear handlers. Settings UI gains a "Push-to-talk hotkey" section with the same Save/Clear pattern as the summon hotkey, fed by a separate `onPTTHotkeyChanged` callback.
## Audio-graph isolation (re: Voice CRIT lane)
PTT.bind installs `NSEvent.addGlobalMonitorForEvents` callbacks only — it does NOT touch the audio graph. The press path routes through `VoiceController.pttDown`, which uses the audio graph the wake-word DAG already owns. So this lane is safe to land in parallel with `fix/voice-crit-v0.1` (wake-word fix). No feature flag needed.
## Test plan
- [x] `bash scripts/check-app-builds.sh` — App target compiles cleanly (Debug, arm64)
- [x] All 17 grep-based boundary gates green (including `check-no-modal-presentation.sh` and `check-single-writer-hudstate.sh`)
- [x] `swift test --package-path packages/DevOverlay` — 14 tests pass (no regression from `initialTab:` addition)
- [x] `swift test --package-path packages/Voice` — 26 tests pass (no regression from PTT call-site addition)
- [x] New tests added:
- 3 in `HUDBannerCoordinatorTests` — programmatic dismiss-by-id clears non-dismissible banners, doesn't poison the id, no-op on unknown id.
- 3 in `WizardStateTests` — PTT hotkey defaults to nil, persists round-trip, independent of summon hotkey.
- [ ] Manual cold-launch sanity (Debug archive): HUD + Status (top-right) + DevOverlay (bottom-right, Logs tab) all visible; toggle off InputMonitoring → banner appears non-dismissible; toggle on in System Settings → banner clears on foreground; bind PTT in Settings → hold-to-talk activates STT.
## Notes
- DO NOT merge until the user has manually verified the three flows above.
- The PR branch was created from a fresh `develop` checkout — clean of the worktree's `fix/voice-crit-v0.1` state.
- No package-level API removals; `DevOverlayView.init`, `DevOverlayWindow.init`, `SettingsView.init`, and `SettingsWindowController.open` all extended with default-valued parameters so existing call sites stay valid.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
Three v0.1 AppDelegate UX holes wired in one branch (sequential commits, each touches
App/AppDelegate.swift+ adjacent App-target files).#89 — Status panel + DevOverlay open on app start (closed in
dca0695)Debug builds default
startupSurfacesMode = .diagnostic, which opens HUD + Status (top-right) + DevOverlay (bottom-right, Logs tab). Release builds default.minimal. Skipped during the first-launch wizard and under XCTest hosts.DevOverlayViewgained aninitialTab:parameter so the panel can open straight to Logs;DevOverlayWindowexposessetFrameOrigin(_:)+frameSizefor AppDelegate positioning.#88 — InputMonitoring TCC banner v1 hole (closed in
48fb8b9)BannerContent.inputMonitoringDeniedis nownonDismissible: true— user must not be able to click past a denial that breaks global hotkeys (silent no-op is the failure mode the banner exists to prevent). NewHUDBannerCoordinator.dismiss(id:)clears a banner programmatically without poisoningdismissedThisLaunch.AppDelegate.reprobeInputMonitoringfires onNSApplication.didBecomeActiveNotificationand handles both transitions: was-denied → now-granted dismisses the banner and rebinds the global hotkey under fresh TCC; was-granted → now-denied re-enqueues.IOHIDCheckAccessis query-only so the foreground hook never prompts.#87 — PTT (VOICE-13) wired (closed in
112f584)WizardState.pttHotkeyis a new persisted shortcut underJarvis.pttHotkey(independent of the summon-Jarvis hotkey). Ships unbound — CLAUDE.md hotkey hygiene forbids defaulting because Cmd+Shift+J and Option+Space collide with too many tools.AppDelegate.rebindPTTFromStateis called from three sites:installVoicetail (after PushToTalk is constructed), the foreground TCC re-grant path, and the Settings Save/Clear handlers. Settings UI gains a "Push-to-talk hotkey" section with the same Save/Clear pattern as the summon hotkey, fed by a separateonPTTHotkeyChangedcallback.Audio-graph isolation (re: Voice CRIT lane)
PTT.bind installs
NSEvent.addGlobalMonitorForEventscallbacks only — it does NOT touch the audio graph. The press path routes throughVoiceController.pttDown, which uses the audio graph the wake-word DAG already owns. So this lane is safe to land in parallel withfix/voice-crit-v0.1(wake-word fix). No feature flag needed.Test plan
bash scripts/check-app-builds.sh— App target compiles cleanly (Debug, arm64)check-no-modal-presentation.shandcheck-single-writer-hudstate.sh)swift test --package-path packages/DevOverlay— 14 tests pass (no regression frominitialTab:addition)swift test --package-path packages/Voice— 26 tests pass (no regression from PTT call-site addition)HUDBannerCoordinatorTests— programmatic dismiss-by-id clears non-dismissible banners, doesn't poison the id, no-op on unknown id.WizardStateTests— PTT hotkey defaults to nil, persists round-trip, independent of summon hotkey.Notes
developcheckout — clean of the worktree'sfix/voice-crit-v0.1state.DevOverlayView.init,DevOverlayWindow.init,SettingsView.init, andSettingsWindowController.openall extended with default-valued parameters so existing call sites stay valid.🤖 Generated with Claude Code