fix(vision): v0.1 cluster — T2 honesty + pendingFrame leak + TCC revoke observation #152

Merged
KofTwentyTwo merged 3 commits from fix/vision-v0.1 into develop 2026-05-13 11:30:10 +00:00
KofTwentyTwo commented 2026-05-12 21:39:16 +00:00 (Migrated from github.com)

Summary

Closes the three v0.1 Vision audit findings from .planning/audit-2026-05-12/vision-audit.md:

  • Closes #1 (HIGH) — AgentOrchestrator no longer claims T2 is available when only MissingT2Provider is wired. New hasRealT2Provider init flag (default false) gates the evaluatePostResponse(t2Available:) argument; production wires false until a real T2 sidecar lands (#84). Low-confidence T1 responses now correctly stay on T1 instead of escalating into MissingT2Provider and dying with t2ProviderUnavailable.
  • Closes #2 (MEDIUM) — FrameAttachController gains releaseAfterRejectedSubmit() (routed through the SOLE discardFrame() emission site, so the D-15 single-emission-site grep gate still holds). AppDelegate.handleChatSubmit / handleChatCancelAndSubmit call it whenever they consumed a pending frame and the orchestrator returned .rejected. JPEG bytes no longer sit in actor memory past the D-15 byte-release window.
  • Closes #3 (MEDIUM) — CameraCapture.startWatchers() adds an AVCaptureSessionWasInterruptedNotification observer that yields .midSessionRevoked (macOS quirk: the iOS-only InterruptionReason key is API_UNAVAILABLE(macos), so any interruption surfaces the banner). AppDelegate.installVision wires an NSApplication.didBecomeActiveNotification observer that re-checks AVCaptureDevice.authorizationStatus(for: .video) and calls becameAuthorized() on a non-authorized → .authorized flip — giving the previously-orphaned method a production caller.

Test plan

  • swift test --package-path packages/Vision — 75 tests, 0 failures (2 skipped real-hardware suite)
  • swift test --package-path packages/AgentCore — 243 tests, 0 failures
  • bash scripts/check-app-builds.sh — App target compiles cleanly
  • All 18 boundary gate scripts — green
  • Manual verification: revoke camera in System Settings while Jarvis is running → cameraRevoked banner should appear (requires real-hardware/TCC manual run)
  • Manual verification: deny camera at first launch → grant in System Settings → bring Jarvis to foreground → presence + frame-attach should resume (requires real-hardware/TCC manual run)

New / modified tests

  • AgentOrchestratorVisionDispatchTests.testLowConfidenceT1StaysOnT1WhenT2ProviderIsMissing — wires real MissingT2Provider + default hasRealT2Provider: false + low-confidence T1; asserts the turn finishes cleanly (one .turnEnd, no .error).
  • FrameAttachControllerTests.testReleaseAfterRejectedSubmitClearsPendingFrame — confirm-then-release sequence; asserts pending slot cleared and idempotent.
  • CameraCaptureMidSessionRevokeTests.testInterruptionYieldsMidSessionRevoked — posts a synthetic wasInterruptedNotification; asserts the degradation stream yields .midSessionRevoked.
  • CameraCaptureTCCTests.testBecameAuthorizedOpensSessionAfterRegrant — seeds .denied, flips probe to .authorized, asserts becameAuthorized() opens the session.

Notes for review

  • The audit's F-V3 fix originally suggested classifying interruption reasons; macOS doesn't ship the reason key (AVCaptureSessionInterruptionReasonKey is API_UNAVAILABLE(macos) per AVFoundation headers). Implementation diverges: on macOS any interruption yields .midSessionRevoked. Commit body documents the quirk.
  • F-V1's hasRealT2Provider flag is plumbed through AgentOrchestrator.init and defaulted false, so existing test sites and production both stay on T1 by default. Flip to true when a real T2 provider replaces MissingT2Provider (issue #84).

🤖 Generated with Claude Code

## Summary Closes the three v0.1 Vision audit findings from `.planning/audit-2026-05-12/vision-audit.md`: - **Closes #1 (HIGH)** — `AgentOrchestrator` no longer claims T2 is available when only `MissingT2Provider` is wired. New `hasRealT2Provider` init flag (default `false`) gates the `evaluatePostResponse(t2Available:)` argument; production wires `false` until a real T2 sidecar lands (#84). Low-confidence T1 responses now correctly stay on T1 instead of escalating into `MissingT2Provider` and dying with `t2ProviderUnavailable`. - **Closes #2 (MEDIUM)** — `FrameAttachController` gains `releaseAfterRejectedSubmit()` (routed through the SOLE `discardFrame()` emission site, so the D-15 single-emission-site grep gate still holds). `AppDelegate.handleChatSubmit` / `handleChatCancelAndSubmit` call it whenever they consumed a pending frame and the orchestrator returned `.rejected`. JPEG bytes no longer sit in actor memory past the D-15 byte-release window. - **Closes #3 (MEDIUM)** — `CameraCapture.startWatchers()` adds an `AVCaptureSessionWasInterruptedNotification` observer that yields `.midSessionRevoked` (macOS quirk: the iOS-only `InterruptionReason` key is `API_UNAVAILABLE(macos)`, so any interruption surfaces the banner). `AppDelegate.installVision` wires an `NSApplication.didBecomeActiveNotification` observer that re-checks `AVCaptureDevice.authorizationStatus(for: .video)` and calls `becameAuthorized()` on a `non-authorized → .authorized` flip — giving the previously-orphaned method a production caller. ## Test plan - [x] `swift test --package-path packages/Vision` — 75 tests, 0 failures (2 skipped real-hardware suite) - [x] `swift test --package-path packages/AgentCore` — 243 tests, 0 failures - [x] `bash scripts/check-app-builds.sh` — App target compiles cleanly - [x] All 18 boundary gate scripts — green - [ ] Manual verification: revoke camera in System Settings while Jarvis is running → `cameraRevoked` banner should appear (requires real-hardware/TCC manual run) - [ ] Manual verification: deny camera at first launch → grant in System Settings → bring Jarvis to foreground → presence + frame-attach should resume (requires real-hardware/TCC manual run) ## New / modified tests - `AgentOrchestratorVisionDispatchTests.testLowConfidenceT1StaysOnT1WhenT2ProviderIsMissing` — wires real `MissingT2Provider` + default `hasRealT2Provider: false` + low-confidence T1; asserts the turn finishes cleanly (one `.turnEnd`, no `.error`). - `FrameAttachControllerTests.testReleaseAfterRejectedSubmitClearsPendingFrame` — confirm-then-release sequence; asserts pending slot cleared and idempotent. - `CameraCaptureMidSessionRevokeTests.testInterruptionYieldsMidSessionRevoked` — posts a synthetic `wasInterruptedNotification`; asserts the degradation stream yields `.midSessionRevoked`. - `CameraCaptureTCCTests.testBecameAuthorizedOpensSessionAfterRegrant` — seeds `.denied`, flips probe to `.authorized`, asserts `becameAuthorized()` opens the session. ## Notes for review - The audit's F-V3 fix originally suggested classifying interruption reasons; macOS doesn't ship the reason key (`AVCaptureSessionInterruptionReasonKey` is `API_UNAVAILABLE(macos)` per AVFoundation headers). Implementation diverges: on macOS any interruption yields `.midSessionRevoked`. Commit body documents the quirk. - F-V1's `hasRealT2Provider` flag is plumbed through `AgentOrchestrator.init` and defaulted `false`, so existing test sites and production both stay on T1 by default. Flip to `true` when a real T2 provider replaces `MissingT2Provider` (issue #84). 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Sign in to join this conversation.