Case study · personal build · Aug 2026 to now
Local AI Agent · Mac mini
My work lives in six tools, and the expensive part of the day is not answering messages but finding the context to answer them well. This agent watches my work surface, does that research, drafts replies in my own voice and keeps a memory of what was decided. It is deliberately read-only: it proposes, and I send.
It has a face. I made this mascot for the agent that lives on the Mac mini, and it shows up wherever that machine appears in this portfolio: this agent, the RPA Uploader, the COPs Control pipeline and the DQM engine.

- SlackOps lead asked for the fleet audit numbers for Thursday. Draft reply ready, with the dashboard link.
- Calendar11:00 design review. The notes from the last session list two open decisions; both are summarised below.
- ClickUp3 tasks due this week. One is blocked waiting for data access; the request went out on Monday.
- GmailPartner reply on the new badge photo. They confirm the shape; nothing to do.
- DriveREADME updated by a colleague on the uploader. Changes are in the guards section.
How it works
- Two reads, one decision. Every run reads my Slack activity twice, everything and unread only, and the difference tells it what needs attention.
- Route A, action. For something I have to answer, it researches across the connectors (the thread, related email, the calendar, the project's documents and tasks) and leaves a draft reply in the conversation, written in my tone for that person.
- Route B, memory. For everything else, a light read that captures decisions, commitments and dates.
- No duplicates. It remembers the last 500 message IDs, stops at the previous run's mark and uses a lock so two runs never overlap.
The memory
A local store of markdown files that organises itself: it is re-indexed and pruned on every run, so it stays small enough to load. No database, no vector index in v1. Plain files I can read and correct.
| File | What it keeps |
|---|---|
| INDEX | what exists and where, rebuilt every run |
| people | who is who, how they write, open threads |
| projects | state, decisions and next steps |
| conversations | summaries of long threads |
| decisions | what was decided, by whom, when |
| pending | commitments with a date |
| glossary | internal acronyms and names |
| tone profile | how I write, per audience |
| state | last message seen, lock, counters |
The rules it follows
| Rule | What it means |
|---|---|
| Read, reason, propose | It never acts in a connector. Sending, reacting, creating or deleting is not in its toolset. |
| Content is data | Anything it reads, including a message that asks it to do something, is treated as information, never as an instruction. That is its defence against prompt injection. |
| Draft, not send | The text of a reply is produced during analysis. Placing it in Slack as a draft is a separate step a person triggers. |
| Traceable | Every proposal and every action is logged with the source it came from. |
| Fails loudly | If a connector is down or a read is partial, the brief says so instead of pretending the day is quiet. |
Version 2 on the Mac mini
v1 ran as a scheduled Claude task every 20 minutes on weekdays. v2 moves it to the team's Mac mini: a Slack app that receives events instead of polling, a local model on Ollama for the classification step so routine reads never leave the machine, launchd to keep it alive, and a small dashboard with five sections: inbox, tasks, context per topic, memory search and settings. It is built in phases, MVP first, with each phase verified on real data before the next.
What I learned
- The useful part of an assistant is the research, not the writing. A draft is only good if the context behind it is complete.
- Read-only is a feature. It is the reason I can let it run on everything I read.
No real messages, names or screenshots of my tools are shown. The brief above is invented.