# A Markdown File IS the Message

**2026-03-29** · 8 min read · `ai`, `development`, `claude`, `architecture`, `infrastructure`, `craft-built`

I run 7+ AI terminals across different projects. They all stop and wait for me at different times. I didn't build a notification system to manage them. I noticed the tool I already lived in could just be the interface

---

Multiple AI terminals. All waiting. And I was at a carwash.

THROVE was mid-build on a fitness app. CRAFT was adding CI workflows. DARINBUILDS.COM was reviewing content. Three Directors coordinating above them. All running Claude Code, all paused at different points asking me things - and I'm sitting there watching the brushes go, phone in hand, wondering why managing an AI fleet means being chained to a laptop.

For a while I'd been thinking about it as a building problem. Some notification system. Some API layer. Some clever way to route Claude's questions to my phone. More stuff to build.

But I was already living in Obsidian. I'd been using it for months as the knowledge graph behind Craft - notes linking decisions across every project. And Obsidian already gives me markdown files with checkboxes and a clean UI on my phone. So the question flipped. What if the system just talked to me through that? Checkboxes for decisions. Tables for context. The terminals' running work wrapped in background the way my notes already were.

I didn't need to build an interface. I was already standing in one.

A markdown file IS the message. Frontmatter for routing. Checkboxes for options. A Reply field for free text. Both sides understand markdown natively. No translation layer. No API. No dumbing anything down.

That's the whole thing, honestly. Everything else is just plumbing.

So I went home and built it that night.

Not a script - the whole thing. The watcher that wakes the fleet. The worker-to-director-to-CTO chain of command. The routing, the notes it writes back to my phone, the logic that tells a stalled terminal from a working one. One sitting, because once I'd seen that the interface was already in my hands, it stopped being a build problem and turned into a one-night problem. The hard part wasn't the code. It was noticing - and after that I just couldn't not make it.



## One Word Woke Up the Fleet

I opened Obsidian on my phone and typed "pulse."

One word in a markdown file, dropped into `CTO/outbox/`. iCloud synced it to my laptop in a couple seconds. On the laptop, a watcher script had been sitting idle:

```bash
fswatch -0 --latency 60 "$INBOX" | while read -d '' file; do
  CTO=$(cmux list-workspaces 2>/dev/null | grep "CTO" | grep -v "WATCHER" | awk '{print $1}')
  cmux send --workspace "$CTO" "/director:cto-pulse" 2>/dev/null
  cmux send-key --workspace "$CTO" enter 2>/dev/null
done
```

No server. No webhook. Just `fswatch` with a 60-second debounce and a laptop that stays awake with `caffeinate`.

I tried `fswatch` without the debounce first. Event-driven, elegant - and completely wrecked by iCloud. Every sync triggers metadata updates before the actual content lands. `.DS_Store`, extended attributes, partial writes. Three false wakes before the real event, and the race condition between wake and re-arm meant I'd miss the actual file while recovering from a ghost one.

The fix wasn't switching to polling. Actually it was the opposite - a longer debounce - 60 seconds. Aggressive enough that the file is fully landed by the time the handler fires. Feels slow though. But it's never wrong, and that matters more.

The elegant version lost to the boring one. That keeps happening in this whole thing, and I've stopped fighting it.

> "Zero tokens while idle. Wakes within seconds when a file lands"

---

## The AI Fleet Doesn't Read Every Screen

First thing it does is run `cmux list-notifications`. One command. Returns who's waiting, who finished, who needs a decision. No token burn scanning seven terminals - just a clean list of who actually needs attention.

Then targeted reads only for those. `cmux read-screen --workspace workspace:10 --lines 50`. CRAFT's last message: "Want me to do a full read of the files I haven't covered yet?" That's a decision for me, not for the machine.

So the CTO writes a note. Not a raw terminal dump - I tried that, it's unreadable on a phone. Not a summary either - summaries decay. Come back three hours later and you've forgotten what "story 3" meant. What actually works: bold labels, one fact per line, a recommendation, and checkbox options where each one maps to the exact text that gets typed into the terminal.

That note lands in `CTO/inbox/CRAFT.md`. iCloud syncs it to my phone. I check a box. I move the file to `CTO/outbox/`. The move IS the submit. No button. No confirmation. A file in the outbox is a sent message.

But here's the part that makes it actually useful vs. just clever - stalled detection.

The CTO classifies what it finds:

- **Waiting:** `cmux list-notifications` flagged it. Terminal needs input.
- **Stalled:** notification-quiet, but context shows an obvious next step. A terminal that just created a backlog story and stopped isn't done - it has unfinished work and just... didn't keep going. The CTO reads the screen, recognizes it, and suggests the next move.
- **Working:** mid-execution. Leave it alone.

A dumb relay forwards questions. The CTO reads the room. And it reads it with stuff I already had - a notification list, a screen - not some judgment engine I had to invent. That's what makes this feel like management instead of babysitting though.

---

## Three Tiers, Adjacent Layers Only

The fleet has a chain of command:

```
Darin (Obsidian)
  <-> CTO terminal
    <-> Directors (THROVE DIRECTOR, CRAFT DIRECTOR...)
      <-> Workers (THROVE, CRAFT, DARINBUILDS.COM...)
```

Directors manage Workers. CTO only talks to Directors. When the pulse ran this morning, it saw THROVE working and THROVE DIRECTOR idle. Worker busy, Director idle - that's fine. Director is letting the Worker cook.

But THROVE stalled with THROVE DIRECTOR idle? That's a dropped ball. CTO flags the Director, not the Worker. It never reaches over a Director's head without asking me first.

Each layer talks to adjacent layers only. That's it. No framework. The routing is YAML frontmatter - every message has a `type` field (`pulse`, `kill`, `dm-message`, `feedback`) and a `to:` field for dispatch. Conventions on top of files I already had, not a system bolted on top.

---

## This Is What Shows Up on My Phone

Real note from today. This is exactly what it looks like in Obsidian:

```markdown
---
from: DARINBUILDS.COM DIRECTOR
workspace: workspace:5
project: darinbuilds.com
status: pending
created: 2026-03-29T18:25
type: pulse
to: DARINBUILDS.COM DIRECTOR
---

## DARINBUILDS.COM DIRECTOR - dm-handoff gap

**Fixed:** Director acknowledged the mistake, invoked craft-direct,
and is now spinning up a Worker terminal.

**Root cause:** The dm skill ends after conversation completes,
but never tells the Director to start craft-direct. Director
improvises - chose craft-story-new (do it myself) instead of
craft-direct (spawn a Worker and supervise).

**The gap:** No handoff step between "DM complete" and "start
directing implementation."

**My recommendation:** Add a Phase 4 to dm-handoff where CTO
sends craft-direct after processing dm-complete. Keeps
orchestration where it belongs.

---

- [ ] Fix in dm-handoff (sends `Update dm-handoff Phase 4`)
- [ ] Fix in dm skill (sends `Update dm skill instead`)
- [ ] Both (sends `Do both`)

**Reply:**

```

That's root cause analysis with a recommendation and three options. Each checkbox shows exactly what gets sent via `cmux send`. I can check a box or type in Reply - free text goes verbatim.

And none of that is a custom UI. Those are just Obsidian's checkboxes - the same ones I'd been using in my notes for months. The interface was sitting there the whole time.

What did I actually type to generate that? One word. "pulse."

---

## Oh, and the Drag Story

I wanted to review next week's workout schedule. I dragged `dm-message.md` from `CTO/create/` into the outbox, wrote one line: "Review next week's generated workouts and send me the schedule."

The system expects me to duplicate the template and leave the original in place. I never do that. I drag it every time. And every time, CTO wakes up, processes my message, then checks `CTO/create/` and finds the template missing. So it recreates it.

That cleanup step - verify all templates exist, recreate any that are missing - exists because of me. The system adapted to how I actually use it instead of how I was supposed to use it. That's the kind of thing you can't plan. It just emerges when you actually live in the system.

THROVE Director got the message, curated the workouts, sent them back. I left notes on two that felt too grip-heavy back to back, approved the rest. Straight to production, all from my phone.

By the time the car was done, three terminals were unblocked and shipping code.

---

## The Stack Is Boring on Purpose

iCloud is the file sync - but any sync works. The watcher is `fswatch` with a long debounce. The terminal bridge is `cmux`. The routing is YAML frontmatter on markdown files.

Obsidian is the one piece that's not interchangeable though. It's a knowledge graph. Every decision note links back to the project that spawned it. Every archived conversation is backlinked. When the CTO writes a recommendation, it can search across every past decision for that project. Take away the knowledge graph and you've got a message relay. Keep it and you've got something that actually gets smarter over time.

No servers. No databases. No APIs. Just files in folders with conventions on top. The crystallized agents I use to [give the fleet eyes](/writing/we-have-eyes) run the same way.

The thing I keep coming back to: I wasn't trying to invent a protocol. Files are already a protocol. Both sides - me and Claude - already understand markdown. The insight wasn't technical. It was noticing something that was already there.

And that's the part I want you to take. The whole system is a night's work, sure - but only because I stopped trying to build the interface and noticed I was already holding it. You're holding something too. A notes app, a sync, some tool you already live in that both you and the machine already understand.

What are you already using that could do the same thing?

---

## Resources

- **[Obsidian](https://obsidian.md)** - The knowledge graph that powers the vault. Free for personal use, syncs via iCloud.
- **[cmux](https://cmux.com)** - Terminal multiplexer with workspace management, screen reading, and notification API.
- **[Claude Code](https://claude.ai/claude-code)** - The AI terminals running in each workspace.
- **[THRØVE](/projects/throve)** - Fitness coaching app built by the `THROVE` terminal. Live at [throve.fit](https://throve.fit).
