10 KiB
Plan: meeting notes, and shipping this as an internal Mac app
Two separate pieces of work. The meeting-notes feature is buildable today. The packaging work is mostly not a coding problem — it's a signing, permissions and distribution problem, and one policy question that should be answered before any of it starts.
Nothing here is implemented yet.
Part 1 — Transcribing the other side of a meeting
Is it possible?
Yes. macOS 26 has two ways to capture audio that is being played, neither of which needs a virtual audio device like BlackHole or Loopback:
| Approach | API | Notes |
|---|---|---|
| Core Audio process tap | AudioHardwareCreateProcessTap + CATapDescription |
Confirmed present in the installed SDK (AudioHardwareTapping.h). Can tap one process — e.g. only Zoom — rather than everything. Preferred. |
| ScreenCaptureKit | SCStream with audio only |
pyobjc-framework-ScreenCaptureKit 12.2.1 is on PyPI. Simpler bindings, but conceptually "screen recording", and captures system-wide. |
Both need a TCC grant (Screen Recording / Audio Recording), which is currently not granted. Same one-time prompt as the other permissions.
Design sketch
Run it as a second, independent pipeline in the same process, not as a branch of the conversation pipeline. It has a different job, a different lifetime, and must not interfere with turn taking.
Zoom process audio ──► tap ──► Apple STT ──► transcript writer ──► notes/2026-08-07-standup.md
your microphone ──► tap ──► Apple STT ──┘
Points that matter:
- Tap the mic separately from the output. Two streams gives you speaker attribution for free — output is "them", mic is "you" — without any diarization model. This is the single biggest quality win available and it's nearly free.
- Reuse
apple_stt.py. It already transcribes accurately at 0.05–0.10 s per utterance, and needs no model download. - Long-form transcription needs chunking.
SFSpeechRecognizeris built for utterances, not hour-long meetings; recognition tasks end on their own. The notes pipeline will need to segment on silence and restart tasks, and that behaviour needs to be verified against a real long meeting before trusting it. This is the main technical unknown. - macOS 26 also ships
SpeechAnalyzer/SpeechTranscriber, which is designed for long-form audio and would be a better fit — but it's a Swift-only API and unreachable from pyobjc. See the packaging section; this is one of the stronger arguments for a Swift shell later.
Turning it on and off
Two triggers, and they're not equivalent:
- Automatic on Zoom. Detection is trivial — Zoom is running right now
(
/Applications/zoom.us.app, pid 89302) and installs its ownZoomAudioDeviceCore Audio driver. But process running is a poor signal: Zoom idles in the background for hours. Better to trigger on Zoom actually holding an active audio stream, or on a calendar event frommeta calendar— which knows when a meeting is genuinely scheduled. - By voice. Don't do this by pattern-matching transcripts. Claude is
already in the loop, so expose
start_meeting_notes/stop_meeting_notesas SDK MCP tools and let him call them. That handles "start taking notes", "actually stop that", and "are you recording?" without any string matching, and it's a handful of lines.
Before building this: the consent question
This records other people. Worth settling first, because it shapes the design and it's much cheaper to answer now:
- Meta already has an official AI notetaker with transcripts and
summaries, reachable from
meta calendar. If that covers the need, this feature is redundant — and it's already been through review. - Some jurisdictions require all-party consent to record a conversation. A personal tool that silently captures colleagues is a different thing from one that announces itself.
- Distributing a recorder internally is a compliance matter, not just an engineering one. This is the single biggest risk to the "share it with the team" goal, and it applies to the meeting-notes feature specifically — not to the voice assistant.
Concrete suggestions if it goes ahead: default it off, require an explicit per-meeting start, make it obvious when it's running, and keep transcripts local with a retention policy. Get a read from Privacy/Legal before it goes to anyone else's machine.
Recommendation: build it for your own use behind an off-by-default flag, and treat "ship the notes feature to the team" as a separate decision gated on that review. The voice assistant itself carries none of this baggage and can ship first.
Part 2 — Packaging as an internal Mac app
What's actually hard
Not the code. Three things:
1. Permissions are the whole reason to do this. You just granted Input
Monitoring to your terminal and noted it'll take effect on restart — that
fragility is the argument for a real app. TCC grants attach to a code-signed
bundle identity. Today the grants belong to your terminal, so they're shared
with everything else you run there and break when the terminal changes. A
signed .app with a stable bundle ID asks once, keeps it, and shows up in
System Settings under its own name. This app needs four: Microphone, Speech
Recognition, Input Monitoring, and (for meeting notes) Screen/Audio Recording.
2. Signing. security find-identity -p codesigning returns 0 valid
identities on this machine. Unsigned or ad-hoc-signed apps have their TCC
grants invalidated on every rebuild, which makes the app unusable in practice.
This needs a real signing identity from whoever owns Mac app distribution.
It also intersects with the binary-approval policy on managed Macs: a
hand-built .app handed to a colleague will be killed on launch unless it
comes through the sanctioned channel.
3. Distribution. Managed Software Center is installed — that's the internal channel, and the answer to "how do I share this". It also solves signing and approval, because packages that ship through it are already handled. The work is conforming to whatever that team requires, not inventing a mechanism.
Size: fixable, and the fix is cheap
The venv is 1.7 GB, which is a non-starter for distribution. Almost all of it is for engines that are no longer the default:
| Package | Size | Needed by |
|---|---|---|
| torch | 529 MB | mlx-whisper only |
claude_agent_sdk _bundled |
260 MB | its bundled CLI — we deliberately don't use it |
| mlx | 183 MB | mlx-whisper |
| llvmlite + numba | 156 MB | mlx-whisper |
| scipy + sympy | 171 MB | mlx-whisper (scipy also used by the resampler) |
| onnxruntime | 69 MB | Kokoro, Silero VAD |
| av | 45 MB | faster-whisper |
Switching the shipped default to Apple STT + Apple TTS + push-to-talk drops
Whisper, Kokoro and Silero. I verified this: importing only the Apple-path
modules pulls in scipy and nothing else heavy, whereas importing bot.py
pulls in all nine. A realistic app is 250–300 MB, most of which is the
Python runtime and pyobjc.
Two concrete prerequisites:
- Make the Whisper/Kokoro imports lazy.
bot.pyimports them at module scope today, so they'd be bundled even when unused. - Exclude
claude_agent_sdk/_bundled(260 MB). The app must call the installedclaudeCLI anyway — it's the only build that can reach the gateway — so Claude Code at Meta becomes a documented prerequisite, not something the app ships.
Shape of the app
A menu-bar app fits the interaction model: no window, hold a key to talk, a menu for voice/model/notes toggles, and an indicator showing when the mic is live and when notes are recording.
| Option | Effort | Trade-off |
|---|---|---|
A. Python + py2app + rumps |
Low | Reuses everything as-is. Bundles a Python runtime; py2app + pyobjc + code signing is fiddly but well-trodden. |
| B. Swift shell, Python core as a subprocess | High | Native menu bar and a much cleaner signing/TCC story. Also unlocks SpeechAnalyzer for long-form meeting transcription. |
| C. Stay a CLI, distribute as an internal package | Lowest | No TCC identity of its own, so the permissions problem stays. Fine for a handful of engineers, not for a team. |
Recommendation: A for a pilot, with B as the path if it gets real adoption. A gets it onto a few machines quickly and proves whether anyone wants it. B is justified once it needs to survive OS upgrades and support non-engineers, and its extra value is concentrated exactly where A is weakest — signing and long-form transcription.
Suggested sequence
- Decide the notes/consent question. Blocks part 1 only; everything else can proceed in parallel.
- Slim the dependencies. Lazy imports, exclude the bundled CLI, verify the Apple-only path runs with Whisper/Kokoro uninstalled. Do this first — it's pure cleanup with no external dependency, and it makes every later step smaller.
- Prototype the process tap against Zoom, behind an off-by-default flag, for your own use. Verify long-meeting chunking on a real call.
- Talk to whoever owns Managed Software Center about signing and packaging requirements before building the bundle. Their answer may dictate the app's shape, and finding that out after building option A would be expensive.
- Build the menu-bar bundle (option A), signed, and install it on your own machine. Confirm all four TCC grants survive a rebuild and a reboot — that's the acceptance test that matters.
- Pilot with two or three teammates before any wider push.
Open questions
- Who owns Mac app signing and Managed Software Center packaging?
- Does the official
meta calendarnotetaker already cover the meeting-notes need well enough to drop that feature entirely? - Should the app require Claude Code at Meta as a prerequisite (simple, 260 MB smaller, and the only thing that authenticates) or attempt to bundle a CLI?
- Is a menu-bar app the right shape, or would a Raycast/Alfred-style overlay suit how people actually work better?