9 Commits
Author SHA1 Message Date
james.maesandClaude Opus 4.7 508cf924d0 docs(packages): per-package README files
Adds README.md to each of the 13 SPM packages (AgentCore, Bus, Config,
DevOverlay, Harness, Keychain, Logging, MCP, Memory, Replay, Shell, Vision,
Voice). Each covers purpose, key public types, deps, consumers, invariants
(pulled from CLAUDE.md + grep-gates + audit findings), test stack, and
notable files. Memory README explicitly marks the package functionally OFF
until vec0.dylib + Ollama models land.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 17:13:33 -05:00
james.maesandClaude Opus 4.7 14cff7b439 fix(test): InputMonitoringDenialTests — add missing HIDAccessProbe protocol method to MockProbe
`HIDAccessProbe` gained `isListenEventAccessGranted() -> Bool` (the
query-only relaunch path that doesn't re-prompt) but `MockProbe` in
`InputMonitoringDenialTests.swift` was never updated to conform — the
test target failed to compile entirely. Existence on develop indicates
the Shell package isn't covered by pre-merge CI.

Add the conformance with the same `granted` value as
`requestListenEventAccess()` since the tests don't differentiate the
two surfaces.

Verified: `swift test --package-path packages/Shell` compiles + runs
19 tests (1 skipped) green.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 15:21:17 -05:00
james.maesandClaude Opus 4.7 08125a46d8 chore(test): remove tautologies and rubber-stamps per tests-audit.md
Per `.planning/audit-2026-05-03/tests-audit.md` section 5, five
`XCTAssertTrue(true)` placeholder tests were identified across the tree
that are mathematically incapable of failing. This commit fixes or
removes each, conservatively. Files touched and disposition:

- App/Tests/AppTests/MCPRuntimeWiringTests.swift:260
  fixed: replaced trailing `XCTAssertTrue(true)` rubber-stamp at end of
  CR-02 channel-drain test with a load-bearing assertion that the drain
  task completed via `finish()`, not cancellation. The implicit `await
  drainTask.value` is the real gate; the new assertion ensures regressions
  that cancel-instead-of-finish surface.

- App/Tests/AppTests/WebviewBundleLoadTests.swift:60
  deleted: `test_assetsDirectoryGitignored_documentedInSmokeScript`. Body
  was `XCTAssertTrue(true)` with comment "intentionally has no assertion
  — it exists to document the split of responsibility". Documentation,
  not a test. Replaced with a top-level comment block documenting the
  same split (smoke-test-hud.sh is the runtime gate).

- packages/AgentCore/Tests/AgentCoreTests/ToolChoiceTests.swift:38
  fixed: removed trailing `XCTAssertTrue(true) // Test compiles → contract
  holds.` from `testLLMProviderStreamRequiresToolChoice`. The closure
  body itself is the contract — if the test compiles, the contract holds.
  Comment updated to make the compile-time semantics explicit.

- packages/Shell/Tests/ShellTests/InputMonitoringDenialTests.swift:36
  deleted: `test_localMonitorOnlyFallback`. Body was `XCTAssertTrue(true)`
  documenting that the real fallback test lives in HotkeyBindingTests.
  Replaced with a top-level comment pointing readers there.

- packages/Vision/Tests/VisionTests/PackageBoundaryTests.swift:14
  deleted: `testVisionModuleCompilesStandalone`. Body was
  `XCTAssertTrue(true)` with comment "compile is the assertion." VISION-03
  isolation is enforced by `scripts/check-vision-isolation.sh` and the
  Package.swift dependency graph, both of which are real gates. Folded
  the documentation into the `testPublicAPISurface` doc-comment so the
  explanation isn't lost.

HudStateEnumTests rubber-stamps (audit items #4–6 in section 5) were
left as-is. The voiceOverLabel literals are user-facing accessibility
text that VoiceOver actually speaks; they are load-bearing in the sense
the audit calls out as worth preserving ("replace with behavioral
assertions if the literal under test is load-bearing"). A regression
that breaks an a11y label IS a real regression. Conservative read; not
worth the blast radius of guessing wrong.

Pre-existing baseline issues unrelated to this batch:
- `packages/Shell/Tests/ShellTests/InputMonitoringDenialTests.swift`
  fails to COMPILE on a clean tree (MockProbe missing
  `isListenEventAccessGranted()`). Verified via `git stash` that the
  failure is independent of this commit. Not addressed here per the
  scope rules — flagged for follow-up.

Net test count: -3 tests (3 deleted, 0 added — the
MCPRuntimeWiringTests assertion replaces the tautology in-place).

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-04 12:34:56 -05:00
james.maes 435d345abf fix(wizard,shell): wizard re-resumes correctly after quit/reopen
The wizard had two persistence holes that made the user's quit-and-reopen
workflow surface the same stage(s) again:

1. Hotkey was in-memory only. WizardState.hotkey was @Published but
   never written to disk. Quit and reopen → state defaults to nil →
   firstUnresolvedStage returns .hotkey → wizard re-asks even though
   the user already bound one a moment ago.
2. Input Monitoring grant detection used the in-memory
   inputMonitoringProbed flag (also session-scoped), not the actual
   system grant state. Quit and reopen after granting → flag resets to
   false → wizard returns to TCC stage even though TCC remembers the
   grant.

Fixes:

- WizardState.refresh() now reads from system sources of truth on every
  invocation:
    apiKeyStored          ← Keychain (already correct)
    inputMonitoringGranted ← IOHIDCheckAccess (new — query-only, no prompt)
    hotkey                ← UserDefaults 'Jarvis.hotkey' (JSON-encoded)
- firstUnresolvedStage gates TCC on !inputMonitoringGranted (the actual
  grant) instead of !inputMonitoringProbed (the session UI flag).
- WizardState.hotkey has a didSet that persists to UserDefaults; the
  Stage 3 'Save Hotkey' button assigning to state.hotkey now hits disk
  too. Set to nil → key removed.
- HIDAccessProbe protocol gains isListenEventAccessGranted(), backed by
  IOHIDCheckAccess in production. Tests can inject deterministic grant
  state without TCC side effects.

Verified: check-app-builds clean. After scripts/dev-reset.sh --yes,
launching → wizard at apiKey. Entering key + granting Input Monitoring
+ binding hotkey + Quit + relaunch (fresh process) should land past
all three stages because each is recomputed from system state.
2026-05-02 07:33:33 -05:00
james.maes 5fd6e762e0 fix(wizard,shell): make hotkey recorder visible + clear instructions + show selected shortcut
Three fixes for the 'pick your hotkey' screen the user reported as
unusable:

1. ShortcutRecorderHostView (Shell): the NSView had no draw(_:) override
   and no mouseDown(_:) override, so it was both invisible AND uncllickable
   for focus purposes. The user had nothing to click on, and even if they
   guessed where the 40-px tall frame was, clicking didn't make it first
   responder, so the keyDown monitor never registered.
   - Add wantsLayer + cornerRadius for a visible rounded field.
   - Add mouseDown(with:) → window.makeFirstResponder(self).
   - Add draw(_:) that renders a placeholder ('Click to record shortcut…'),
     a focused-state hint ('Press a shortcut…'), and the recorded value
     once captured. Border + background change color when focused so the
     focus state is obvious.
   - Add setRecordedDisplay(_:) public setter so the SwiftUI parent can
     push the captured shortcut text in for rendering.

2. WizardStageHotkeyView (App): tells the user, in plain language, what to
   do — three numbered steps explaining click-to-record, modifier+key, and
   Esc-to-cancel. Adds a 'Selected: [⌘⇧J]' confirmation row with a Clear
   button so the user can see and reset the chosen shortcut before saving.
   Wraps text properly via .fixedSize(horizontal: false, vertical: true).

3. KeyboardShortcut.displayString (Shell): renders shortcut as Apple's
   menu-style glyph string ('⌘⇧J', '⌃⌥Space', etc.). Carbon import is
   required for kVK_* constants — added directly to KeyboardShortcut.swift
   per user direction. Modifier glyph order matches Apple convention
   (⌃ ⌥ ⇧ ⌘); unmapped key codes fall back to 'Key{n}' for debuggability.

Verified: check-app-builds.sh PASS; relaunched (PID 68314).
2026-05-02 07:21:26 -05:00
james.maes cd3b62f672 fix(01-CR-03): detect silent nil global hotkey monitor + wire bind-failed banner
Phase 1 REVIEW.md CR-03 (SHELL-06): `HotkeyBinder.bind` never checked
whether `store.installGlobalMonitor` returned nil — yet
`NSEvent.addGlobalMonitorForEvents` silently returns nil when Input
Monitoring was revoked between launches (common after a codesign
identity change). The result was the exact silent-no-op SHELL-06
exists to prevent: hotkey appears bound, global monitor is dead, no
banner surfaces. Related WR-06: `BannerContent.hotkeyBindFailed`
existed but was never enqueued from production code.

packages/Shell/Sources/Shell/HotkeyBinder.swift
- Add `HotkeyBinder.BannerSink` protocol (mirrors
  `InputMonitoringProbe.BannerSink` pattern).
- `bind(_:inputMonitoringGranted:bindFailedSink:onPress:)` now checks
  the returned token: if nil, sets `isDegraded = true` AND invokes
  `sink.enqueueHotkeyBindFailed()`. Local monitor install is still
  unconditional so the hotkey works while Jarvis is frontmost.

packages/Shell/Tests/ShellTests/HotkeyBindingTests.swift
- Add `DeniedGlobalMonitorStore` fake (returns nil for global install).
- Add `RecordingBindFailedSink` that counts enqueue calls.
- New tests:
  - `test_nilGlobalTokenMarksDegradedAndNotifiesSink` — SHELL-06
    regression guard for the silent-revoke path.
  - `test_deniedInputMonitoringDoesNotEnqueueBindFailed` — asserts the
    priority-2 inputMonitoringDenied banner is not mixed with the
    priority-3 hotkeyBindFailed banner.
  - `test_successfulBindDoesNotEnqueueBindFailed` — no spurious banner
    on the happy path.

App/AppDelegate.swift:233-260
- Add `HotkeyBindFailedSink` adapter (mirrors `AdHocBannerSink`
  pattern) so Shell can enqueue `BannerContent.hotkeyBindFailed` onto
  the `HUDBannerCoordinator` without importing App types.
- `bindHotkeyFromWizard()` now passes the sink into `hotkeyBinder.bind`
  — fixes WR-06 (hotkeyBindFailed preset no longer orphaned).
2026-04-23 16:13:11 -05:00
james.maesandClaude Opus 4.7 5d57d7b5a4 feat(01-04): build hand-rolled ShortcutRecorder + CollisionDetector
- ShortcutRecorderHostView: testable NSView with process(event:) drop-in;
  rejects modifier-only + shift-only keystrokes, aborts on Escape, records
  valid (keyCode + non-shift-only modifiers) per UI-SPEC Surface 2
- ShortcutRecorderView: NSViewRepresentable SwiftUI glue with @Binding
  shortcut + errorMessage, @MainActor-isolated Delegate protocol
- KeyCapView / KeyCapRowView: key-cap chip rendering ([⌘]+[⇧]+[J])
- CollisionDetector: Cmd+Shift+J → Chrome/Slack/VS Code warning,
  Option+Space → Alfred/Raycast warning, uncommon combos pass through
- Tests: 7 passing (modifier-only rejected, shift-only rejected, escape
  aborts, valid capture, cmdShiftJ collision, optionSpace collision,
  uncommon combo no collision)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 13:48:31 -05:00
james.maesandClaude Opus 4.7 37f235b8c8 feat(01-04): build Shell core — HotkeyBinder, InputMonitoringProbe, LaunchAtLogin, TCCAlertService
- KeyboardShortcut: Codable/Sendable/Equatable struct for keyCode + modifier mask
- HotkeyBinder: global+local NSEvent monitor pair with local-only degraded mode
  on Input Monitoring denial (D-06/D-07; SHELL-06)
- InputMonitoringProbe: IOHIDRequestAccess(kIOHIDRequestTypeListenEvent) with
  BannerSink callout on denial — silent no-op forbidden per SHELL-06/D-12
- LaunchAtLoginController: SMAppService.mainApp wrapper, macOS 13+ only
  (SHELL-05 scaffolded; no UI in P1)
- TCCAlertService: UI-SPEC Surface 6 hard-blocker NSAlert builder
- Tests: 9 passing (+1 skipped SMAppService live test behind JARVIS_ALLOW_SM_TESTS=1)
- Deleted placeholder source + test from Plan 01

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-04-22 13:44:09 -05:00
james.maes d8617ffe01 feat(01-01): add four SPM packages with Swift 6 strict concurrency
- packages/Keychain — zero SPM deps (Security.framework at link time only)
- packages/Logging — depends on apple/swift-log 1.5.3+; library product is
  named JarvisLogging to avoid collision with swift-log's own Logging module
- packages/Config — depends on Keychain + Logging (product JarvisLogging)
- packages/Shell — depends on Config + Logging; links AppKit,
  ServiceManagement, IOKit, Carbon for hotkey/launch-at-login surfaces

Every target (main + test) opts into .swiftLanguageMode(.v6) per D-04 so
data-race errors are compile-time failures, not warnings.

Placeholder sources (one per package) plus one trivial XCTest per package
per D-03 so each package builds standalone and has a passing test. Real
sources land in Plan 02 (Keychain/Config/Logging) and Plan 04 (Shell).

Bus/LLM/MCP/Voice/Memory packages NOT pre-created per D-01 — they arrive
in their owning phases (P2/P4/P5/P6/P7) to keep rebuild scope tight.
2026-04-22 12:21:07 -05:00