# https://github.com/silverstein/minutes Project Manual

Generated at: 2026-07-17 21:41:22 UTC

## Table of Contents

- [Project Overview & Architecture](#page-overview)
- [Audio Capture, Transcription & Live Pipelines](#page-recording)
- [AI Agent Surfaces, MCP Server & Skills](#page-agents)
- [Platform Operations, Permissions & Known Failure Modes](#page-platform)

<a id='page-overview'></a>

## Project Overview & Architecture

### Related Pages

Related topics: [Audio Capture, Transcription & Live Pipelines](#page-recording), [AI Agent Surfaces, MCP Server & Skills](#page-agents)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [README.md](https://github.com/silverstein/minutes/blob/main/README.md)
- [crates/core/src/lib.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/lib.rs)
- [crates/core/src/pipeline.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/pipeline.rs)
- [crates/core/src/markdown.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/markdown.rs)
- [crates/core/src/config.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/config.rs)
- [crates/core/src/capture.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/capture.rs)
- [crates/core/src/transcribe.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/transcribe.rs)
- [crates/core/src/diarize.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/diarize.rs)
- [crates/core/src/summarize.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/summarize.rs)
- [crates/core/src/mcp.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/mcp.rs)
- [crates/core/src/recall.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/recall.rs)
- [crates/core/src/live_transcript.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/live_transcript.rs)
- [crates/core/src/voices.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/voices.rs)
- [crates/core/src/identity.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/identity.rs)
- [crates/core/src/permissions.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/permissions.rs)
- [tauri/src/index.html](https://github.com/silverstein/minutes/blob/main/tauri/src/index.html)
- [tauri/src-tauri/src/main.rs](https://github.com/silverstein/minutes/blob/main/tauri/src-tauri/src/main.rs)
- [docs/architecture/frontmatter-schema.md](https://github.com/silverstein/minutes/blob/main/docs/architecture/frontmatter-schema.md)
- [schema/meeting.schema.json](https://github.com/silverstein/minutes/blob/main/schema/meeting.schema.json)
- [Cargo.toml](https://github.com/silverstein/minutes/blob/main/Cargo.toml)
</details>

# Project Overview & Architecture

## Purpose and Scope

Minutes is a cross-platform meeting recorder and meeting-intelligence engine that turns captured audio into structured, attributable Markdown notes. The product combines native audio capture, speech-to-text transcription, speaker diarization, identity/voice matching, and LLM-driven summarization into a single pipeline, then exposes the resulting corpus to end users through a Tauri desktop app and to external agents through an MCP (Model Context Protocol) server. The pipeline-from-recording-to-structured-markdown flow is described by community users as the project's defining quality. Source: [README.md:1-40]()

The repository hosts three first-class deliverables:

1. A Rust workspace of `crates/*` libraries that implement the core pipeline.
2. A Tauri-based desktop application under `tauri/` that wraps the pipeline in a native UI (menubar/tray, settings, onboarding, Recall chat panel).
3. A JSON Schema for meeting output plus design and plan documents under `docs/`.

The latest released version is v0.22.1, a bugfix release that primarily repaired a `CGEventTap` leak in the macOS permission-monitor probe that could freeze input system-wide. Source: [Cargo.toml:1-40]()

## Repository Layout

The Rust workspace is organized as a set of focused crates rather than one monolithic binary, which is why feature work tends to land in a single module without rippling outward. The `core` crate holds the bulk of the domain logic and is the canonical entry point for non-UI consumers. Source: [crates/core/src/lib.rs:1-60]()

A representative slice of the layout:

| Path | Responsibility |
| --- | --- |
| `crates/core/src/pipeline.rs` | Orchestrates capture → transcribe → diarize → summarize |
| `crates/core/src/capture.rs` | Audio capture, including `--call` and platform-specific backends |
| `crates/core/src/transcribe.rs` | Speech-to-text engine dispatch |
| `crates/core/src/diarize.rs` | Speaker turn detection and clustering |
| `crates/core/src/summarize.rs` | LLM-driven summarization, including the `apple` engine (v0.21.0+) |
| `crates/core/src/markdown.rs` | Renders meeting output as Markdown |
| `crates/core/src/mcp.rs` | Model Context Protocol server surface |
| `crates/core/src/recall.rs` | Recall chat-panel / Q&A over past meetings |
| `crates/core/src/live_transcript.rs` | Real-time in-app transcription session state |
| `crates/core/src/voices.rs` | Voice enrollment embeddings, `voices.db`, blended profiles |
| `crates/core/src/identity.rs` | Self-name normalization, `identity.name` resolution |
| `crates/core/src/permissions.rs` | macOS Input Monitoring / Screen Recording probes |
| `crates/core/src/config.rs` | TOML config schema and resolution |
| `tauri/src/index.html` | Frontend onboarding/settings UI |
| `tauri/src-tauri/src/main.rs` | Tauri runtime bootstrap |

Source: [crates/core/src/lib.rs:1-120](), [tauri/src-tauri/src/main.rs:1-80]()

## Pipeline Architecture

At the center of the system is `pipeline.rs`, which sequences the heavy stages in a deterministic order so that downstream stages always see consistent inputs. Audio is captured first (microphone, system audio, or `--call`), then transcribed, then diarized, then summarized, and finally rendered to Markdown with frontmatter. Source: [crates/core/src/pipeline.rs:1-120]()

The Markdown output is governed by a JSON Schema and a prose companion document, which together fix the on-disk shape of every meeting file. The schema's role is to make meetings machine-legible for downstream tools (Recall, MCP, editors). Source: [schema/meeting.schema.json:1-80](), [docs/architecture/frontmatter-schema.md:1-60]()

Each stage is pluggable through an engine-dispatch pattern. The same shape is reused for transcription, summarization, and speaker mapping, which is why engine-name errors such as `Unknown engine: auto` (#155) and `Unknown engine: apple` (#487, v0.21.0 follow-up) recur — every new engine must be registered in every dispatch table, not just the one it ships in. Source: [crates/core/src/summarize.rs:1-120](), [crates/core/src/diarize.rs:1-120]()

A simplified view of the runtime data flow:

```mermaid
flowchart LR
  A[Audio capture<br/>capture.rs] --> B[Transcribe<br/>transcribe.rs]
  B --> C[Diarize<br/>diarize.rs]
  C --> D[Identity & voice match<br/>identity.rs / voices.rs]
  D --> E[Summarize<br/>summarize.rs]
  E --> F[Markdown render<br/>markdown.rs]
  F --> G[(meetings/*.md)]
  G --> H[Recall chat<br/>recall.rs]
  G --> I[MCP server<br/>mcp.rs]
  H --> J[Tauri UI]
  I --> K[External agents]
```

## Platform Support, Permissions, and Integrations

The pipeline is platform-aware at the capture and permissions layers only; everything above the audio frame is portable Rust.

- **macOS** uses CoreAudio for capture and `kCGHIDEventTap` / Screen Recording probes in `permissions.rs`. The probe path is what caused the v0.22.1 hotfix: a permission check was minting a new tap every 15s without invalidating it, exhausting the system-wide tap table and freezing input. Source: [crates/core/src/permissions.rs:1-160]()
- **Windows** uses ConPTY for the embedded terminal and a separate quit path through `PtyManager::kill_session`; joining the ConPTY reader thread can block on Windows 11 if EOF is never observed (#261). Live-transcript status reporting (`live_transcript::session_status`) can also return zeroed counters on Windows during a healthy session (#258). Source: [crates/core/src/live_transcript.rs:1-120]()
- **Linux** is expected to use PipeWire monitor sources for system audio; auto-detection of `*.monitor` sources for `record --call` is tracked as a feature (#62). Source: [crates/core/src/capture.rs:1-160]()

On top of the core, two user-facing surfaces expose the same meeting corpus:

- **Recall** is an in-app chat panel over past meetings. Community issues track its evolution from an embedded terminal toward a native panel (#362), text-selection regressions caused by inherited `user-select: none` (#490), and richer voice-enrollment onboarding flows (#492, #496).
- **MCP** exposes meeting data and tools to external agents and is the primary integration point for non-UI consumers.

Identity and voice attribution cut across the pipeline. `identity.rs` normalizes the configured self-name (`Settings → General → Your Name`), but solo recordings without a matched calendar event currently bypass that path (#491). `voices.rs` maintains the embedding store (`voices.db`) used by Levels 2 and 3 of attribution; making enrollment a first-class feature is tracked as an epic (#496). Speaker attribution can also override `identity.name` when a different name is discussed in-transcript (#245), a known limitation of the current LLM mapping stage.

## Operational Notes

Configuration is TOML-driven and resolved centrally in `config.rs`, so engine selection (`summarization.engine = "apple"`), identity, and capture defaults all live in one place. The `--call` flag, `record` subcommand, and MCP transport are the three primary entry points and share the same pipeline code path, which is why a fix in `permissions.rs` or `pipeline.rs` benefits every surface simultaneously. Source: [crates/core/src/config.rs:1-120](), [crates/core/src/pipeline.rs:1-120]()

---

<a id='page-recording'></a>

## Audio Capture, Transcription & Live Pipelines

### Related Pages

Related topics: [Project Overview & Architecture](#page-overview), [Platform Operations, Permissions & Known Failure Modes](#page-platform)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [crates/core/src/capture.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/capture.rs)
- [crates/core/src/transcribe.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/transcribe.rs)
- [crates/core/src/streaming.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/streaming.rs)
- [crates/core/src/streaming_whisper.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/streaming_whisper.rs)
- [crates/core/src/streaming_diarize.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/streaming_diarize.rs)
- [crates/core/src/diarize.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/diarize.rs)
</details>

# Audio Capture, Transcription & Live Pipelines

The audio subsystem of `minutes` turns raw microphone and system-audio streams into structured, speaker-attributed transcripts in three cooperating stages: **capture** (device selection and per-platform recorders), **batch transcription** (file-to-text via pluggable engines), and **live streaming** (incremental transcript + diarization for the Tauri app and CLI).

## 1. Audio Capture (`capture.rs`)

`capture.rs` owns the microphone and "call" (system audio) capture paths and is the single entry point invoked by the CLI (`minutes record`, `minutes record --call`) and the Tauri front-end.

- Microphone capture is platform-native; on macOS a Core Audio tap is used, on Linux a PulseAudio / PipeWire source, and on Windows a WASAPI client (Source: [crates/core/src/capture.rs]()).
- System-audio capture on macOS is delegated to the bundled `system_audio_record` helper. Issue #494 documents a regression where the 0.22.0 helper was compiled against macOS 26 SDK symbols and crashed on macOS 15; the helper is expected to signal readiness before capture begins (Source: [crates/core/src/capture.rs]()).
- On Linux, `#62` requests PipeWire `*.monitor` auto-detection so `minutes record --call` works without manual device selection (Source: [crates/core/src/capture.rs]()).
- The Windows path interacts with `PtyManager` for any subprocess-based capture; issue #261 documents a hang in `PtyManager::kill_session` where the ConPTY reader thread never sees EOF and blocks process shutdown (Source: [crates/core/src/capture.rs]()).
- Permissions: on macOS, Input Monitoring and Microphone entitlements are probed by a separate permission monitor. Issue #488 showed that a permission probe that created a `kCGHIDEventTap` every 15s without invalidating it eventually exhausted the system event-tap table and froze WindowServer; v0.22.1 fixes this by probing once per process (Source: [crates/core/src/capture.rs]()).

## 2. Batch Transcription (`transcribe.rs`)

`transcribe.rs` exposes a synchronous transcription pipeline that consumes an audio file and returns a `Transcript` (segments, timestamps, language, engine metadata).

- Engines are dispatched through a `transcription.engine` config key (`whisper`, `mlx-whisper`, `parakeet`, `apple`, `auto`); missing dispatch entries surface as `Unknown engine: <name>`, which is the same bug class reported in #487 for `summarization.engine = "apple"` speaker mapping (Source: [crates/core/src/transcribe.rs]()).
- Output is normalized into a stable `Segment { start, end, text, speaker? }` schema before being handed to the diarization or summarization stages (Source: [crates/core/src/transcribe.rs]()).
- Long files are chunked; each chunk is decoded independently so partial failures don't abort the whole job (Source: [crates/core/src/transcribe.rs]()).

## 3. Speaker Diarization (`diarize.rs`)

`diarize.rs` maps time-stamped segments to speaker labels.

- It consumes a `Transcript` plus an optional voice-enrollment database (`voices.db`) and produces `Segment { speaker: String }` annotations (Source: [crates/core/src/diarize.rs]()).
- L1 (LLM-based) speaker mapping dispatches on the same engine key as summarization; #487 shows that adding `apple` to summarization without updating this dispatcher yields `Unknown engine: apple` (Source: [crates/core/src/diarize.rs]()).
- L2/L3 embedding-based attribution blends enrolled voice profiles with on-the-fly clustering; #496 tracks the productization of this engine (UI, MCP tool, passive enrollment) (Source: [crates/core/src/diarize.rs]()).
- #245 documents a known failure mode where the LLM mapper overwrites `identity.name` with a name mentioned in the transcript itself (Source: [crates/core/src/diarize.rs]()).

## 4. Live Streaming Pipeline (`streaming.rs`, `streaming_whisper.rs`, `streaming_diarize.rs`)

The streaming modules provide incremental, low-latency transcripts surfaced to the desktop UI and to `live_transcript::session_status()`.

- `streaming.rs` defines the session lifecycle: `start`, `push_audio`, `poll_lines`, `stop`, plus a `SessionStatus { line_count, duration_secs, last_error }` struct (Source: [crates/core/src/streaming.rs]()).
- `streaming_whisper.rs` runs an incremental Whisper decoder over a rolling audio window; partial hypotheses are emitted as soon as a stable token boundary is reached (Source: [crates/core/src/streaming_whisper.rs]()).
- `streaming_diarize.rs` runs embedding-based clustering in parallel and rebinds speaker labels as more audio arrives (Source: [crates/core/src/streaming_diarize.rs]()).
- Issue #258 reports that on Windows the in-app (Tauri) live session reports `line_count: 0, duration_secs: 0.0` even while audio is flowing; this is a status-projection bug, not a capture bug — the underlying transcript pipeline still produces correct output (Source: [crates/core/src/streaming.rs]()).
- Voice enrollment during a live session is governed by #492: the onboarding "10-second test recording" is mislabeled because it produces no enrollment artifact and offers no post-Stop feedback (Source: [crates/core/src/streaming_whisper.rs]()).

### Pipeline Overview

| Stage | Module | Trigger | Output |
|-------|--------|---------|--------|
| Capture | `capture.rs` | `minutes record` / Tauri "Record" | WAV / PCM chunks |
| Batch transcription | `transcribe.rs` | File complete | `Transcript` |
| Diarization | `diarize.rs` | After transcription | `Transcript` w/ speakers |
| Live decode | `streaming_whisper.rs` | Rolling window | Partial segments |
| Live diarization | `streaming_diarize.rs` | Audio chunk | Speaker-bound segments |
| Session status | `streaming.rs` | UI poll | `SessionStatus` |

## Cross-Cutting Concerns

- **Self-name normalization** (`#491`): configured `identity.name` is only applied when calendar attendees are resolved, leaving solo recordings — including the onboarding test — unrenamed. The fix belongs in `transcribe.rs` / `diarize.rs` post-processing (Source: [crates/core/src/diarize.rs]()).
- **Permissions on macOS** are a first-class dependency of `capture.rs`; the v0.22.1 fix in `capture.rs` (probe once per process) is required for any long-running recording session to remain safe (Source: [crates/core/src/capture.rs]()).
- **Cross-platform parity**: capture has three backends (macOS / Linux / Windows) and currently the most divergent failure surface; transcription and diarization are platform-agnostic but inherit engine-availability differences (e.g. `apple` is macOS-only) (Source: [crates/core/src/transcribe.rs]()).

---

<a id='page-agents'></a>

## AI Agent Surfaces, MCP Server & Skills

### Related Pages

Related topics: [Project Overview & Architecture](#page-overview), [Platform Operations, Permissions & Known Failure Modes](#page-platform)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [crates/mcp/src/index.ts](https://github.com/silverstein/minutes/blob/main/crates/mcp/src/index.ts)
- [crates/mcp/src/capabilities.ts](https://github.com/silverstein/minutes/blob/main/crates/mcp/src/capabilities.ts)
- [crates/mcp/src/paths.ts](https://github.com/silverstein/minutes/blob/main/crates/mcp/src/paths.ts)
- [crates/mcp/src/autoInstall.ts](https://github.com/silverstein/minutes/blob/main/crates/mcp/src/autoInstall.ts)
- [crates/mcp/src/captureRelay.ts](https://github.com/silverstein/minutes/blob/main/crates/mcp/src/captureRelay.ts)
- [.claude-plugin/marketplace.json](https://github.com/silverstein/minutes/blob/main/.claude-plugin/marketplace.json)
</details>

# AI Agent Surfaces, MCP Server & Skills

## Overview

Minutes exposes its recording, transcription, summarization, and recall pipelines to AI coding agents through a Model Context Protocol (MCP) server bundled under `crates/mcp/`. This server, together with the Claude plugin marketplace manifest, is what makes Minutes usable *from inside* an agent loop rather than only from the desktop app or CLI. The agent surface is the third leg of the product alongside the Tauri desktop app and the `minutes` CLI binary.

The MCP server is a TypeScript process (`crates/mcp/src/index.ts`) that registers a fixed set of tools, reads the same on-disk state the CLI writes, and proxies capture controls to a running Minutes desktop session via a local relay. A community design thread calls out that the embedded terminal currently used for Recall is "developer-friendly" and asks for a native in-app chat panel; the MCP path is the underlying mechanism both that terminal and a future in-app chat would call. Source: [crates/mcp/src/index.ts:1-80]().

## MCP Server Entry Point and Tool Registration

`index.ts` is the server entry. It wires the MCP transport (typically `stdio` so Claude Desktop, Claude Code, Cursor, and other agents can spawn it as a subprocess) and delegates tool listing to `capabilities.ts`. The server is intentionally thin: it does not re-implement capture or transcription logic, it routes calls to either the local CLI binary or the capture relay. Source: [crates/mcp/src/index.ts:80-160]().

`capabilities.ts` is the single source of truth for what an agent can do. Each capability maps a tool name to a typed handler and a JSON schema for arguments. Categories typically include:

| Capability group | Example tools | Backed by |
|---|---|---|
| Capture control | `start_recording`, `stop_recording`, `pause_recording` | `captureRelay.ts` → desktop session |
| Recording inventory | `list_recordings`, `get_recording` | CLI + on-disk markdown |
| Transcript / summary | `get_transcript`, `get_summary`, `search` | CLI + recall index |
| Identity & voices | `list_voices`, `enroll_voice` (planned) | `voices.db`, CLI |
| Recall chat | `recall_query`, `recall_followup` | local LLM provider |

The voice-enrollment epic (#496) explicitly notes that "there's no MCP tool" for active enrollment yet — `capabilities.ts` is the file that will gain `enroll_voice` and related tools once the product surface lands. Source: [crates/mcp/src/capabilities.ts:1-120]().

## Filesystem Resolution and Cross-Platform Paths

Because an MCP server may be launched by an agent with a different working directory, `PATH`, or sandbox than the user's shell, every filesystem access goes through `paths.ts`. This module resolves the Minutes data directory, the recordings folder, the voices database, the config file, and the desktop socket used by the relay, honoring the same XDG / `~/Library/Application Support` / `%APPDATA%` conventions the rest of Minutes uses. Source: [crates/mcp/src/paths.ts:1-90]().

`paths.ts` is also where the server distinguishes between a "user-level" install (shared across projects) and a "project-level" install (scoped to a repo's `.claude/` or `.cursor/`). This distinction drives `autoInstall.ts`. Source: [crates/mcp/src/paths.ts:90-160]().

## Auto-Install and Agent Discovery

`autoInstall.ts` makes Minutes installable into an agent with one command rather than a multi-step manual edit. On first invocation, the server detects which agents are present (Claude Desktop, Claude Code, Cursor, Continue, etc.) by checking well-known config paths and offers to register itself. Registration writes a JSON entry pointing at the bundled `mcp` entry script, using paths resolved via `paths.ts`. Source: [crates/mcp/src/autoInstall.ts:1-110]().

The server manifest for Claude is published at `.claude-plugin/marketplace.json`, which lists Minutes as a plugin with the MCP server as its primary capability, plus optional skills (slash commands, prompt snippets) for common workflows like "summarize today's meetings" or "enroll my voice". Source: [.claude-plugin/marketplace.json:1-40]().

## Capture Relay and Live State

The trickiest capability is live capture control, because recording state lives inside the running Tauri desktop process. `captureRelay.ts` solves this with a small local IPC: the MCP server speaks HTTP (or a named pipe on Windows) to a relay endpoint the desktop app exposes when running, so tools like `start_recording` can drive an active session. If no desktop session is running, the relay returns a structured "no live session" error that `capabilities.ts` translates into an actionable agent message. Source: [crates/mcp/src/captureRelay.ts:1-140]().

This relay is also how a future in-app Recall chat panel (#362) would talk to the same agent surface — the chat UI becomes another client of `capabilities.ts`, sharing tool definitions, error envelopes, and authentication with the MCP server. Source: [crates/mcp/src/captureRelay.ts:140-220]().

## Skills vs. Tools

The repository distinguishes **tools** (structured MCP function calls defined in `capabilities.ts`) from **skills** (declarative prompt/command bundles declared in `.claude-plugin/marketplace.json`). Tools are for *actions* the agent can take; skills are for *behaviors* that guide the agent when Minutes context is present. This separation keeps the MCP surface small and stable while still allowing richer agent personas. Source: [.claude-plugin/marketplace.json:40-120]().

## Known Gaps Reflected in Community Issues

- A native in-app Recall chat panel replacing the embedded terminal (#362) would reuse `capabilities.ts` and `captureRelay.ts`.
- Voice enrollment lacks an MCP tool and a desktop UI path (#496, #492).
- Onboarding copy misleads users about enrollment feedback (#492), which a `recall_followup`-style skill could clarify.
- Selectable text in any in-app Recall surface must override the body's `user-select: none` (#490) regardless of whether the surface is a terminal, webview, or chat panel.

These items all sit on top of the same `crates/mcp/` substrate documented above.

---

<a id='page-platform'></a>

## Platform Operations, Permissions & Known Failure Modes

### Related Pages

Related topics: [Audio Capture, Transcription & Live Pipelines](#page-recording), [AI Agent Surfaces, MCP Server & Skills](#page-agents)

<details>
<summary>Related Source Files</summary>

The following source files were used to generate this page:

- [crates/core/src/macos_permissions.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/macos_permissions.rs)
- [crates/core/src/device_monitor.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/device_monitor.rs)
- [crates/core/src/health.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/health.rs)
- [crates/core/src/config.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/config.rs)
- [crates/core/src/pid.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/pid.rs)
- [crates/core/src/hotkey_macos.rs](https://github.com/silverstein/minutes/blob/main/crates/core/src/hotkey_macos.rs)
- [crates/recorder/src/capture.rs](https://github.com/silverstein/minutes/blob/main/crates/recorder/src/capture.rs)
</details>

# Platform Operations, Permissions & Known Failure Modes

## Scope and Purpose

Minutes is a cross-platform recording and summarization tool that depends on privileged OS facilities: microphone capture, screen/audio capture on macOS via `system_audio_record`, global hotkeys, and process management. "Platform Operations" is the layer that probes, negotiates, and surfaces the state of those facilities so that the rest of the app can degrade gracefully when the host denies permission. "Known Failure Modes" is the companion discipline of identifying the bugs that arise when this layer is mis-implemented — most notably the macOS event-tap leak that produced v0.22.1.

Source: [crates/core/src/macos_permissions.rs:1-40]()

## Permission Probes

The macOS permission module exposes three discrete probes, each returning a structured status rather than a boolean:

- `microphone_status()` — checks `AVCaptureDevice.authorizationStatus(for: .audio)`.
- `screen_capture_status()` — checks Screen Recording permission required by `system_audio_record`.
- `input_monitoring_runtime_issue()` — performs a live `kCGHIDEventTap` probe because macOS only returns the Input Monitoring grant state at runtime.

Source: [crates/core/src/macos_permissions.rs:42-180]()

Each probe is intentionally read-only. The app never tries to *trigger* the OS prompt from background code — it only reads the current state and reports it via `health.rs` so the UI can show a "Permission needed" banner. This separation is what enables the app to distinguish "user has not been asked yet" from "user has denied".

Source: [crates/core/src/health.rs:30-95]()

## Event-Tap Lifecycle and the v0.22.1 Fix

The Input Monitoring probe has historically been the most fragile piece of platform code. The probe must register a temporary `kCGHIDEventTap`, attempt to enable it, then disable and release it. If any of those steps leak — for example, calling `CGEventTapCreate` but never pairing it with a matching `CFRelease` — the tap is retained for the lifetime of the process and counts against the system-wide tap table.

Community issue #488 reported that long-running Minutes instances accumulated **512+ disabled taps**, exhausting the macOS event-tap table and breaking system-wide screenshot tools. v0.22.1 fixed the leak by ensuring each probe invalidates its tap in the failure path. Follow-up #501 then addressed an adversarial-review edge case: the first probe invocation can legitimately time out and produce a transient error, so caching that single failure as a permanent "Input Monitoring denied" verdict would force a spurious app restart. The fix memoizes successful probes but treats the initial probe result as advisory only.

Source: [crates/core/src/macos_permissions.rs:120-200]()

## Device Monitor and Hotkey Wiring

The device monitor tracks audio device hot-plug events so the recorder can rebind when a user's default microphone changes mid-session. It surfaces a small enum (`DeviceEvent::{Added, Removed, DefaultChanged}`) rather than raw OS notifications, keeping the recorder free of platform-specific code.

Source: [crates/core/src/device_monitor.rs:1-60]()

Global hotkey registration on macOS is isolated in `hotkey_macos.rs`, which registers a `kCGEventTap` for the configured `Cmd+Shift+R` style shortcut. Because hotkeys also consume from the same event-tap table, the same discipline — register, enable on success, disable and release on every exit path — applies here as in the permission probe.

Source: [crates/core/src/hotkey_macos.rs:25-110]()

## Process Identity and the Quit Hang

Single-instance enforcement uses a PID file guarded by `pid.rs`. On startup the app writes its PID; on shutdown it removes the file. On Windows, the related `PtyManager::kill_session` bug (community #261) hangs the quit path because the ConPTY reader thread never observes EOF and `join` blocks forever. The known mitigation is to detach the reader with a timeout rather than join it directly, allowing the tray-quit path to complete even if the helper is wedged.

Source: [crates/core/src/pid.rs:1-50]()

## Cross-Platform Audio Capture Failure Modes

| Platform | Back-end | Typical failure | Mitigation |
|----------|----------|-----------------|------------|
| macOS 15 | `system_audio_record` (ScreenCaptureKit) | Helper built for macOS 26 crashes before signaling readiness (#494) | Pin deployment target; verify helper version on launch |
| macOS 26 | `system_audio_record` | Same binary as above | OK |
| Linux | PipeWire `*.monitor` source | Manual device setup required | Auto-detect monitor sources in `capture.rs` (#62) |
| Windows | WASAPI loopback | Live transcript status returns 0 lines / 0:00 (#258) | Use session-start timestamp instead of per-line counter |

Source: [crates/recorder/src/capture.rs:80-160]()

## Health Reporting Surface

`health.rs` aggregates the permission statuses, device-monitor state, and recorder state into a single snapshot returned to the desktop UI and to the CLI (`minutes doctor`). When any required permission is missing, the health snapshot carries a `restart_required` flag so the UI can distinguish "grant and retry" from "restart needed after granting".

Source: [crates/core/src/health.rs:60-140]()

## Operational Guidance

1. Always run `minutes doctor` after a fresh install — it exercises every probe above and reports a single aggregate status.
2. On macOS, if Input Monitoring was just granted, do **not** rely on a cached "denied" verdict from a prior session; the probe must be re-run, which is exactly the invariant #501 enforces.
3. Treat any `kCGHIDEventTap` allocation as resource that must be released on every control-flow path, including panic unwinds — this is the lesson behind v0.22.1.
4. On Windows, prefer detached process termination over `join()` to avoid the quit-hang class of bugs.

Source: [crates/core/src/macos_permissions.rs:1-200](), [crates/core/src/health.rs:1-150]()

---

<!-- evidence_pipeline_checked: true -->
<!-- evidence_injected: true -->

---

## Pitfall Log

Project: silverstein/minutes

Summary: Found 34 structured pitfall item(s), including 4 high/blocking item(s). Top priority: Security or permission risk - Security or permission risk requires verification.

## 1. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Developers should check this security_permissions risk before relying on the project: CGEventTap leak: long-running app accumulates 512+ disabled taps, exhausting the system tap table (breaks screenshots system-wide)
- User impact: Developers may expose sensitive permissions or credentials: CGEventTap leak: long-running app accumulates 512+ disabled taps, exhausting the system tap table (breaks screenshots system-wide)
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/488

## 2. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Developers should check this security_permissions risk before relying on the project: Input Monitoring probe: don't cache a transient first-probe timeout as a false "restart needed"
- User impact: Developers may expose sensitive permissions or credentials: Input Monitoring probe: don't cache a transient first-probe timeout as a false "restart needed"
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/501

## 3. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/362

## 4. Security or permission risk - Security or permission risk requires verification

- Severity: high
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: packet_text.keyword_scan | https://github.com/silverstein/minutes

## 5. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: Recall chat panel text is not selectable or copyable (inherits body `user-select: none`)
- User impact: Developers may fail before the first successful local run: Recall chat panel text is not selectable or copyable (inherits body `user-select: none`)
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/490

## 6. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: system_audio_record in Minutes 0.22.0 is built for macOS 26 and crashes on macOS 15
- User impact: Developers may fail before the first successful local run: system_audio_record in Minutes 0.22.0 is built for macOS 26 and crashes on macOS 15
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/494

## 7. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.18.13
- User impact: Upgrade or migration may change expected behavior: v0.18.13
- Evidence: failure_mode_cluster:github_release | https://github.com/silverstein/minutes/releases/tag/v0.18.13

## 8. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.18.14
- User impact: Upgrade or migration may change expected behavior: v0.18.14
- Evidence: failure_mode_cluster:github_release | https://github.com/silverstein/minutes/releases/tag/v0.18.14

## 9. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.19.0: Sherpa SOTA engine + sensitivity enforcement
- User impact: Upgrade or migration may change expected behavior: v0.19.0: Sherpa SOTA engine + sensitivity enforcement
- Evidence: failure_mode_cluster:github_release | https://github.com/silverstein/minutes/releases/tag/v0.19.0

## 10. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.20.0: Dictation at your cursor, in-app Documents, honest degradation
- User impact: Upgrade or migration may change expected behavior: v0.20.0: Dictation at your cursor, in-app Documents, honest degradation
- Evidence: failure_mode_cluster:github_release | https://github.com/silverstein/minutes/releases/tag/v0.20.0

## 11. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this installation risk before relying on the project: v0.21.0
- User impact: Upgrade or migration may change expected behavior: v0.21.0
- Evidence: failure_mode_cluster:github_release | https://github.com/silverstein/minutes/releases/tag/v0.21.0

## 12. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/492

## 13. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/62

## 14. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/490

## 15. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/496

## 16. Installation risk - Installation risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a installation risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/494

## 17. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.host_targets | https://github.com/silverstein/minutes

## 18. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: Configured "Your Name" is ignored for solo recordings — self-name normalization is gated behind calendar attendees
- User impact: Developers may misconfigure credentials, environment, or host setup: Configured "Your Name" is ignored for solo recordings — self-name normalization is gated behind calendar attendees
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/491

## 19. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: Native in-app chat panel for Recall (replace embedded terminal for non-developer users)
- User impact: Developers may misconfigure credentials, environment, or host setup: Native in-app chat panel for Recall (replace embedded terminal for non-developer users)
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/362

## 20. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: Onboarding: "10-second test recording" reads like voice enrollment but isn't — no post-Stop feedback and no desktop enrollment path
- User impact: Developers may misconfigure credentials, environment, or host setup: Onboarding: "10-second test recording" reads like voice enrollment but isn't — no post-Stop feedback and no desktop enrollment path
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/492

## 21. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: PipeWire auto-detection for --call on Linux
- User impact: Developers may misconfigure credentials, environment, or host setup: PipeWire auto-detection for --call on Linux
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/62

## 22. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: [Epic] First-class voice enrollment (active + passive + on-device)
- User impact: Developers may misconfigure credentials, environment, or host setup: [Epic] First-class voice enrollment (active + passive + on-device)
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/496

## 23. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this configuration risk before relying on the project: speaker mapping fails with "Unknown engine: apple" when summarization.engine = "apple" (v0.21.0 #439 follow-up)
- User impact: Developers may misconfigure credentials, environment, or host setup: speaker mapping fails with "Unknown engine: apple" when summarization.engine = "apple" (v0.21.0 #439 follow-up)
- Evidence: failure_mode_cluster:github_issue | https://github.com/silverstein/minutes/issues/487

## 24. Configuration risk - Configuration risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a configuration risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/487

## 25. Capability evidence risk - Capability evidence risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: README/documentation is current enough for a first validation pass.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: capability.assumptions | https://github.com/silverstein/minutes

## 26. Runtime risk - Runtime risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Developers should check this runtime risk before relying on the project: Minutes v0.22.0
- User impact: Upgrade or migration may change expected behavior: Minutes v0.22.0
- Evidence: failure_mode_cluster:github_release | https://github.com/silverstein/minutes/releases/tag/v0.22.0

## 27. Maintenance risk - Maintenance risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a maintenance risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/silverstein/minutes

## 28. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: downstream_validation.risk_items | https://github.com/silverstein/minutes

## 29. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: no_demo
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: risks.scoring_risks | https://github.com/silverstein/minutes

## 30. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/488

## 31. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/491

## 32. Security or permission risk - Security or permission risk requires verification

- Severity: medium
- Evidence strength: source_linked
- Finding: Project evidence flags a security or permission risk. Review the linked source before relying on this workflow.
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: community_evidence:github | https://github.com/silverstein/minutes/issues/501

## 33. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: issue_or_pr_quality=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/silverstein/minutes

## 34. Maintenance risk - Maintenance risk requires verification

- Severity: low
- Evidence strength: source_linked
- Finding: release_recency=unknown。
- User impact: May increase setup, validation, or first-run risk for the user.
- Evidence: evidence.maintainer_signals | https://github.com/silverstein/minutes

<!-- canonical_name: silverstein/minutes; human_manual_source: deepwiki_human_wiki -->
