Skip to content

feat(capture): pre-flight microphone permission gate (silent-capture L0)#336

Merged
silverstein merged 1 commit into
mainfrom
feat/preflight-mic-permission-gate
Jun 16, 2026
Merged

feat(capture): pre-flight microphone permission gate (silent-capture L0)#336
silverstein merged 1 commit into
mainfrom
feat/preflight-mic-permission-gate

Conversation

@silverstein

Copy link
Copy Markdown
Owner

Closes the core of bead minutes-9bc8 (silent-capture warning). Catches a denied or unconfirmed microphone permission at record-start, instead of discovering it after a meeting is already lost. Motivated by the real incident where a re-signed dev app dropped its mic TCC grant and an 8-minute Zoom call recorded near-silence with no warning.

This is L0 of a layered capture-integrity model (the deterministic permission check). L1 callback-liveness and L2 statistical dead-stem detection are separate follow-ups.

What it does

  • Reads the microphone authorization status (which already existed but was never gated at record-start) and surfaces a block or warning through the pre-flight that both the CLI and desktop start paths already consume.
  • Typed block kind CapturePreflightBlockKind { CallRoute, Permission }. The native-call and palette bypass (which intentionally skips the no-system-audio-route block when native capture is available) now skips ONLY CallRoute blocks, via a shared core helper, so a Permission block can never be swallowed on the native-call path. That was the path the incident came from.
  • Recoverable-call degradation: a call with recoverable system audio plus a denied mic WARNS (and still captures the remote side) rather than blocking. Mic-only meeting, memo, dictation, and live transcript with a denied mic BLOCK.
  • NotDetermined and StaleOrRestartNeeded warn but never block (blocking would prevent the OS permission prompt that grants access).
  • The mic-only pre-flight is also wired into dictation and live transcript starts, so the gate is not meeting-only.
  • Pure, OS-free decision functions (unit-tested without a mic). Capability check, never an interactive prompt or TTY gate, no hangs for non-interactive callers.
  • Cross-platform safe: non-macOS reports NotNeeded and the gate is a no-op.

Deferred follow-ups (not in this PR)

  • A strict-complete-capture override config (block instead of warn on a degraded call).
  • A visible recording-state label for the degraded-call case (belongs with L1 indicator integrity).

Tests

792 core tests green (5 new), clippy clean on both feature sets, app and cli build. New tests cover recoverable-call warn vs mic-only block, block-kind tagging, the mic-only helper, and a regression test that a Permission block survives the native-call bypass.

@vercel

vercel Bot commented Jun 15, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
useminutes.app Ready Ready Preview, Comment Jun 15, 2026 11:28pm

…t recordings

Catches a denied/unconfirmed microphone permission at record-start instead of
discovering it after a meeting is lost (bead minutes-9bc8; the 8-min silent Zoom
call after a re-signed dev app dropped its mic TCC grant).

- Typed CapturePreflightBlockKind { CallRoute, Permission }; the native-call
  bypass (commands.rs + palette_dispatch.rs, via a shared core helper) now skips
  only CallRoute blocks, so a Permission block can never be swallowed on the
  native-call path.
- Call with recoverable system audio + denied mic warns (salvages the remote
  side) rather than blocking; mic-only meeting/memo/dictation/live blocks.
- NotDetermined / StaleOrRestartNeeded warn but never block (the OS prompt still
  needs to fire). Pure, OS-free decision functions; capability check, never an
  interactive prompt.
- Mic-only pre-flight wired into dictation and live transcript starts too.
- Cross-platform safe: non-macOS reports NotNeeded (no-op).
@silverstein silverstein force-pushed the feat/preflight-mic-permission-gate branch from 2311099 to 99bcf26 Compare June 15, 2026 23:27
@silverstein silverstein merged commit 3e48d46 into main Jun 16, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant