Once I started running multiple coding agents against the same repository, they began stepping on each other: two agents editing the same file, or duplicating work neither knew the other had started. The obvious fix is to let them talk, but every approach I reached for dragged in a daemon, a server, or a polling loop that quietly burned tokens checking “any messages yet?” on every turn.
adm is my answer: agent-to-agent messaging that’s a single Go binary backed by SQLite, with no daemon, no server, and no network. Agents can send direct messages, broadcast announcements, signal which files they’re touching, and see who else is online. Crucially, the messaging layer is provider-agnostic. It doesn’t care whether the agent on the other end is Claude, Codex, or Gemini. That neutrality is what let me actually explore different multi-agent topologies without rebuilding the plumbing each time.
The idea I’m proudest of: passive, poll-free delivery
The thing that makes adm feel right is that agents never poll. Messages ride in through the agent’s own hook system. On Claude Code, a PostToolUse hook delivers pending messages after each tool call and a PreToolUse hook warns before editing a claimed file. The agent just does its work, and relevant messages appear in its context at natural tool boundaries. No status checks, no wasted turns, no “still nothing” loops.
Built like a small piece of real infrastructure
- At-least-once delivery, by design. Sends land as
pending, getofferedwith a batch token, thendeliveredonce acknowledged. If a hook crashes mid-delivery, the same messages are re-offered on the next sync. Message IDs are stable, so duplicates are trivial to detect. I chose obvious correctness over clever exactly-once handwaving. - File claims that warn instead of block. An agent can claim
src/auth/*.goto flag intent, but claims are soft signals; they never prevent another agent from editing. Coordination, not locking. - Genuinely fast. Sub-10ms per invocation (≈1ms p50 for sync and send on an M1). I stress-tested it with 24 concurrent agents pushing 120 messages and saw zero message loss.
- Zero-ceremony state. Everything lives in
.agents/adm/at the repo root: a WAL-mode SQLite database and per-agent delivery tokens. WAL plus a 5-second busy timeout is what lets two dozen agents hammer the same database concurrently without lock errors, and schema migrations are additive so older binaries can still read newer databases (they ignore unknown columns). Upgrades never need manual steps. - Session-based identity and an audit trail. Identity resolves from a session file or an
ADM_AGENTenv var rather than being passed on every call, and every mutation is written to an audit log, so when something coordinates strangely across agents, the history is actually reconstructable. - Path normalization for claims. File claims are glob patterns matched through a dedicated normalization layer, so
./src/auth/login.goandsrc/auth/login.goresolve to the same thing regardless of how a given agent refers to it. - Lightweight Go packaging. The whole thing is one statically-linked binary you can drop on any machine or sandbox. The release installer detects OS/arch and verifies a SHA-256 checksum. That portability is half the reason it was so easy to experiment with.
What it taught me and why I moved on
adm works, but building it end to end is how I learned it was the wrong form factor for what I actually wanted: a command center for orchestrating many agents at once. Two problems became obvious only after the whole thing existed.
First, a CLI gives you almost no visibility into agent trajectories. When several agents are streaming output simultaneously, a terminal is the wrong surface. You can’t watch what each one is doing, approve changes, or reason about the swarm as a whole.
Second, passive hook delivery only works if every agent has hooks. At the time, Codex didn’t, so I had to fall back to polling, which predictably never worked well. The delivery design I was most proud of had a hard dependency on a feature that didn’t exist everywhere.
So I rebuilt the idea properly as goldengoose, a desktop workspace for running and coordinating coding agents in parallel with the visibility a CLI can’t give and, most importantly, automatic wake-on-message: you message another agent, and if it’s idle, the system triggers a fresh completion to wake it. No polling, no babysitting, no hook prerequisite. I wrote that one in Rust, which made the runtime more performant and memory-safe than this Go version.
This was the first project where I felt, in my gut, that I’d built the wrong thing the right way, and the deep work here on delivery semantics, presence, and systems-level design in Go is exactly what made the better version possible.
