Commit Graph
5 Commits
Author SHA1 Message Date
james.maesandClaude Opus 4.7 a9531e65e3 feat(round-4): menu-bar icon turns red when subsystems are degraded
Slice 4 of Round 4 — the visible polish. With Slices 1–3 the agent
preamble + non-dismissible banner cover the "agent must know +
user must see" loop, but a closed HUD with no banner visible can
still hide the warning. This slice paints the menu-bar icon red so
the warning is glanceable from anywhere on the screen.

Option A from the slice plan (single-writer-safe):

- `MenuBarIconController.applyHealth(_ health: OverallHealth)` paints
  `button.contentTintColor = NSColor.systemRed` for `.loud` and
  `.critical`, clears the tint for `.ok` and `.soft`. Template images
  recolor automatically with `contentTintColor` so no second icon
  asset is needed.
- `currentHealth: OverallHealth?` exposed read-only for tests.
- `AppDelegate.runBootHealth` calls `menuBarController?.applyHealth(
  snapshot.overallHealth)` after computing the degradation summary,
  so the icon updates on every probe sweep (cold boot + Status panel
  re-probe).

Single-writer invariant for HudState (HUD-08) is intact:
`contentTintColor` lives on `NSStatusItemButton`, not in HudState.
`scripts/check-single-writer-hudstate.sh` is green.

1 new test in MenuBarIconControllerTests:
  * test_applyHealthTintsRedForLoudAndCritical — exercises all four
    OverallHealth cases and asserts contentTintColor toggles correctly.

Boundary gates 18/18 green; App target compiles cleanly.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-11 15:59:48 -05:00
james.maes da42c079fd feat(06-05): AppDelegate production wiring + VoiceWiringTests (W1-W4)
- AppDelegate: import Voice, add voiceController/pushToTalk/muteWakeWord properties
- AppDelegate: installVoice() async method wires VoiceController to dormantVoiceContinuation
- AppDelegate: NullOrchestratorAdapter/NullTTSAdapter/NullBusEmitterAdapter (Phase 7 stubs)
- AppDelegate: AppDelegateBannerAdapter bridges VoiceBannerInterface→HUDBannerCoordinator
- MenuBarIconController: contextMenu is now public (for MuteWakeWord)
- VoiceWiringTests: W1-W4 type-level wiring verification
- NOTE: check-app-builds.sh fails on pnpm (pre-existing infra limit); Swift compiles clean
2026-04-27 13:56:34 -05:00
james.maesandClaude Opus 4.7 d7a192a446 fix(menu-bar): use popUpMenu instead of toggle-menu pattern
The previous click handler tried to render the right-click context menu
via:

    statusItem.menu = contextMenu
    sender.performClick(nil)
    statusItem.menu = nil

This is racy. NSStatusItem can latch the menu reference on a separate
runloop pass after we set it, so the post-click `nil` assignment doesn't
always take effect. Once the menu is latched, NSStatusItem bypasses the
button's action selector entirely — every left- AND right-click then
opens the context menu, and the HUD never gets summoned because
`onLeftClick?()` no longer fires.

User-observed symptom: "when I clicked on it (left or right mouse) it
showed the right hand menu no matter what" after the first right-click
of the session.

Fix: call `statusItem.popUpMenu(contextMenu)` directly. The menu shows
for this click only — no persistent assignment, no race. `popUpMenu`
was deprecated in macOS 10.14 with Apple recommending "use the menu
property instead," but that recommendation is exactly what produces
this bug. The function still works through Tahoe (verified
empirically). When/if Apple removes it, replacement is a manual
`NSMenu.popUp(positioning:at:in:)` against the status-item button.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-26 16:26:22 -05:00
james.maes ca17844883 feat(03-01): extend HudState enum 5→7 cases with voiceOverLabels
- Append .reconfiguring and .booting to App/Theme/HudState.swift;
  preserve Phase 1 declaration order + existing labels verbatim.
- Add voiceOverLabel arms 'Jarvis, reconfiguring audio' / 'Jarvis,
  starting up' per RESEARCH §HudState Enum Wire Format.
- MenuBarIconController: add forced exhaustive switch arm for the
  two new cases; both share .idle profile (no animation) with
  TODO for Phase 3/4 dedicated treatment per HUD-08 precedence.
- HudStateEnumTests: 6 methods covering case count, non-empty +
  unique voiceOverLabels, spec-verbatim labels, exhaustive switch.
2026-04-23 18:47:46 -05:00
james.maes f8da4b7c39 feat(01-03): add MenuBarIconController + MenuBarContextMenu + JarvisAppTests target
- MenuBarIconController (@MainActor) owns NSStatusItem, loads Icon-MenuBar-Template asset with isTemplate=true, re-anchors layer.frame after anchorPoint change (Q7 gotcha #2), transitions across 5 HudStates via CAAnimationFactory with 150ms crossfade + rate-limited VoiceOver announcements (3s limiter per UI-SPEC Surface 3 line 421)
- MenuBarContextMenu builds Setup… / Settings… (disabled) / Show Dev Overlay / Copy State Dump / Quit Jarvis (⌘Q), with a ClosureTarget trampoline for closure-based actions
- Left-click vs right-click disambiguation via NSApp.currentEvent.type inspection (D-16 / UI-SPEC Surface 3 lines 406-409)
- JarvisAppTests Xcode unit-test bundle target added via project.yml; scheme wires test action against JarvisAppTests
- MenuBarIconControllerTests covers state transitions, no-op-on-same-state, context-menu items, and HudState voiceOverLabels (4 tests pass)
- PBXCopyFilesBuildPhase for Contents/Helpers re-applied post-xcodegen (Plan 01 invariant — MCP-05 acceptance-criteria grep)
2026-04-22 13:14:09 -05:00