!A voice memo on a night walk travels as light to a home workspace.
*Published: August 26, 2026*
*This is Part 7. Part 6 covers what transfers between agent runtimes. Part 5 covers the ecosystem.*
---
I run this operation on a MacBook Pro, an iPhone, an iPad and a pair of AirPods.
**The laptop stays home.** I go out. An idea arrives. I hold a button and talk into the AirPods for forty seconds. Back at a desk, it has a title, a category, a note and a reminder. Sometimes an agent has already picked it up.
That sentence took a lot of unglamorous engineering, three separate guards against a specific kind of nonsense, and one genuinely nasty afternoon with Apple's permission system. Here is the whole thing.
---
``` AirPods -> Voice Memos on the phone -> iCloud -> the Mac, about six seconds later -> local transcription -> a local model cleans it and gives it a title -> a reminder in a dedicated list -> a note in my vault -> the twice-daily intake -> a card on a board -> an agent -> a reviewer ```
Nine hops. Every one of the first six happens on my own machine with no network call. That was not a nice-to-have. If this thing is going to hear everything I mutter on a walk, it does not get to send that anywhere.
!An AirPods voice memo carries a faint waveform through a rainy night.
---
I record in Apple's own Voice Memos. Nothing custom, because I want the thing I use when I have one hand free and no attention to spare to be the thing that is already on the lock screen.
Apple does the syncing. **The file lands on the Mac about six seconds after I stop recording**, fully materialised, not a placeholder. I checked, because the whole pipeline depends on it and "iCloud will probably sync it" is not a design.
The Mac side reads Apple's own index database to find new recordings, strictly read-only, and never writes a byte back. That database is a live sync store. Writing to it out of band would not crash anything today; it would quietly desynchronise my voice memo library across my devices and I would find out weeks later. Not worth it for any convenience.
Apple, incidentally, keeps **no transcript of its own** anywhere I could find. The capability flag is set. Nothing is on disk, in the database, or in the search index. So the transcription is mine to do.
---
!Audio becomes organised work inside a local home lab.
Audio gets converted to a plain format, then transcribed by a speech model running on the machine. Then a small language model, also on the machine, does two passes:
1. Clean it up. Fix punctuation and obvious mishearings. Do not summarise, do not interpret, do not be helpful. Just make it readable. 2. **Title and categorise it**, from the cleaned text rather than the raw text, so a mangled word cannot end up in the filename.
Both models run locally. And here is the part worth stealing:
🔴 My local model runner lists cloud models identically to local ones. Four models in the list. Two of them are remote passthroughs. The only thing distinguishing them is a suffix in the name. Nothing else in the output tells you that picking one sends your audio transcript to somebody else's server.
So there is a guard that **refuses outright** to run if the configured model name carries that suffix. Not a warning. A refusal, with a test that proves it refuses.
That is three lines of code and it is the only thing standing between "runs locally" being true and being marketing.
<details> <summary>The other quiet trap: silent truncation</summary>
The local runner defaults to a context window of a few thousand tokens regardless of what the model actually supports, and it truncates past that without telling you. A long memo would get cut without a word. I would get a confident, clean, wrong transcript of the first two minutes.
The fix is one explicit setting. The lesson is that a default that silently truncates is worse than one that errors.
</details>
---
!Three physical safeguards stop silence and noise before they become work.
This started with a failure.
**Three seconds of silence made the speech model emit the word "You."** Not a bug in the model. It is doing its job, given nothing, and producing the most probable thing. But it meant a pocket tap became a reminder titled "You." A system that fills my task list with nonsense gets ignored within a week.
So there are three guards, and they run in a deliberate order.
One: a minimum duration. Anything under two seconds is refused **before transcription**, so a pocket tap never costs any compute at all.
Two: a minimum character count. A thirty-second recording of a quiet room passes the duration check comfortably and then produces "Thank you." Duration cannot catch that. Length can.
**Three: an average no-speech probability.** The speech model reports, per segment, how confident it is that there was no speech there. A long recording of traffic noise can produce a plausible *length* of complete garbage. Neither of the first two guards catches it. This one does.
Each guard catches a case the other two miss. That is the actual design principle, and it is why I did not just pick one and move on.
Both of the first two rejected genuine recordings on their first live run: one at eighty-three percent no-speech confidence, one that transcribed to a ten-character "Thank you."
**And this is the number I want to be honest about: the pipeline has so far created zero reminders under its schedule.** Every memo it has been offered, it correctly refused.
That looks like failure and it is not. It has been offered two real inputs and rejected both, for good reasons, and I would rather that than a task list full of "You." A guard that has never refused anything is not a guard, and a guard that refuses everything is broken. Mine has now done the first and not the second, which is the most you can say after two inputs.
The thing I have not done, and should: feed it one deliberately good memo and assert a reminder appears. **A known-answer test in the other direction.** Without that I only know it says no.
---
!A signed launcher keeps permission attached to the protected archive.
This is the part that took an afternoon and taught me the most.
On a Mac, a background job cannot read the Voice Memos folder without an explicit Full Disk Access grant. Fine. I went to give it one.
Attempt one: point the permission at a shell script.
It did not work. The reason is interesting. macOS grants access to a **code-signed program**, and a script is not a program, it is data. The thing actually opening the file is the interpreter. I checked what the system thought it was granting, and it reported the host as Apple's own shell. So my grant was, in effect, "let bash read everything," which the system will not honour for a script it has no signature for.
**A script cannot hold a permission grant, because a script is not the code that runs.**
Attempt two: write a tiny compiled launcher that starts Python.
Better idea. Still wrong. The obvious way to start another program replaces your own process image with it. By the time Python opens the file, **the signed program no longer exists as a running process.** The thing touching the file is plain Python, which has no grant. I had built a launcher that erased itself on the way to launching.
Attempt three: fork, then launch in the child, and wait in the parent.
That one worked. The signed program stays alive as the responsible process, and the child inherits its permission. You can verify it from the binary's own symbol table: it uses fork, exec **and** wait, not exec alone.
The launcher is signed ad hoc, with no developer team identity. That means there is no stable name for the system to pin the grant to, so it pins to a raw hash of the binary itself.
Which means: **every time I rebuild that binary, I revoke my own permission.**
I did this twice. Granted access, rebuilt the launcher to fix something, and could not work out why the grant had stopped applying. The permission was fine. The program it referred to no longer existed, because I had changed it.
⚠️ And two permissions that sound alike are not alike. A background job on this machine can read my reminders without any grant at all, and cannot read the voice memo folder without a specific one. Different subsystems, different rules. I assumed one implied the other and lost an hour.
---
**It cannot rename the memo in Voice Memos.** There is no scripting interface and no rename action in the app's automation surface. I checked every route. The generated title lives in the reminder and the note instead. Not ideal, and I would rather say so than pretend.
It cannot set real tags on the reminder. This one I got wrong twice and it is worth walking through.
First I said tags were impossible, because the command-line tool I use has no tag option. **That is a fact about one tool stated as a fact about the platform**, and I said it with confidence.
So I checked properly. The scripting interface has twelve properties on a reminder and none is tags. The public API has no tags. Then I tried the obvious hack: put a hash symbol in the title and let the app convert it.
It does not. And I did not assume that, I measured it: there are reminders on this machine with a hash in the title carrying **zero** tags, because a tag is a separate database row and is not derived from title text.
There is exactly one route that works, through the system automation framework, and it needs one saved shortcut that only I can create by hand. Until I make it, every reminder gets a category in square brackets at the front of its title instead. Searchable, tappable, and honestly not a tag.
---
!One voice signal becomes a well-organised card on a shared board.
The last hops are the boring, reliable ones.
Twice a day, a job gathers everything: the voice inbox, my raw-thoughts note, my overdue reminders, and the state of the boards. It writes one handover file.
⚠️ **It has to be a handover file**, because of another permission asymmetry: a job started from my own session can read my reminders, and the agent runtime cannot. So the job gathers and the agent decides. Permissions never sit on the agent's critical path, and the arrangement survives upgrades.
Then an agent reads that file and turns items into cards on a board, assigned to whichever specialist fits. A dispatcher picks up cards. A worker does the job. A reviewer on a different vendor checks it.
**That last chain is Part 8**, and it has the best material: what happens when a machine claims to have finished something, and how you find out whether it actually did.
---
All of this exists because the laptop stays home. So people ask me the same thing: when are you building the mobile app for the agent runtime?
I am not. I already have one, and I did not write it.
Claude Code runs the whole framework on the Mac, inside a terminal multiplexer. Remote control is on. The Claude app on my phone talks to that session.
So the thing in my pocket is not a thin client. No four buttons I decided to expose. **It is the actual system**, with every tool it has at the desk.
Out on a walk I can read what a worker produced. Move a card. Stop a run that is going nowhere. Ask the reviewer why it failed something and get the real reason instead of a status code.
That is a strange thing to sit with, honestly. I spent weeks on voice capture, three guards and a code-signing rabbit hole. The remote half was a setting on something I was already running.
⚠️ It is not magic. The Mac has to be awake and reachable. Anything needing the desktop app's own window still needs me at the desk.
But the gap between a thought on a walk and the system acting on it is now one message.
---
Guard against silence in more than one way. Duration, length and confidence each catch what the others miss. One guard produces a task list I stop reading.
**Check what "local" means in your own stack.** Mine lists remote models identically to local ones. Privacy was the point. So I made the check a refusal, not a warning, and wrote a test that proves it refuses.
On a Mac, permissions attach to signed programs, not to scripts. A background job reading protected files needs a real signed bundle that forks. And once you have granted it, stop rebuilding it.
**I would be honest about what it cannot do.** Two of the things I wanted, renaming the recording and real tags, are not available. Saying so is more useful than a system that half-does them.
---
*Part 8: six agents, one board, and the machinery that works out whether a claim is true.*
*This is Part 7 of the EMMI-AI Infrastructure series.* *Part 1 · Part 2 · Part 3 · Part 4 · Part 5 · Part 6*
*Built by Emanuel Covasa in County Leitrim, mostly while walking.*
#AI #Automation #macOS #Privacy #LocalAI #Whisper #PersonalAI #Neurodivergent #ADHD #BuildInPublic