How Don't Be Michael turns a room full of independent
claudeprocesses into a collaborating, self-coordinating team with persistent memory, a shared blackboard, and a "god" orchestrator that runs the floor.
This document is the design source of truth for the agent-collaboration layer. It
sits alongside SPEC.md (terminal/event plane) and
DESIGN.md (visual system). Code is the source of truth for what's
built; this is the source of truth for what we're building toward.
Each spawned agent is a real claude CLI process with a filesystem, a system
prompt, and a hook lifecycle. We layer four classic patterns on top:
| Behaviour the user asked for | Pattern (the name) |
|---|---|
| Per-agent memory file made at spawn, that the agent reads and updates | Agent long-term memory (MemGPT/Letta-style self-managed memory) |
| Writing a requirement into another agent's file | Stigmergy — coordinating by modifying a shared environment |
| A shared plan multiple agents edit | Blackboard architecture (Hearsay-II) |
| "Check after finishing every task" | Mailbox / actor model — drain an inbox at a lifecycle point |
| A "god" agent that runs the floor and clarifies for others | Orchestrator / supervisor (LangGraph-supervisor-style) |
The umbrella term is a multi-agent system (MAS) with autonomous agent loops. The closest academic analogue to this app is Stanford's Generative Agents (Park et al., 2023): Sims-style avatars in a 2D world with a memory stream, retrieval, reflection, and planning.
- Git as the coordination/audit layer, single committer. Everything the
hive knows is files in one local git repo. To avoid
.git/index.lockcorruption with many concurrent agents, only the Electron main process commits. Agents never call git — they write plain files. (Research: GitHub Desktop's commit-queue pattern; lazygit/git-retry backoff.) - Single-writer-per-file. Each agent writes only inside its own
agents/<id>/directory. Cross-agent delivery happens by the router (main process) moving messages from a sender'soutbox/into a recipient'sinbox/. No file is ever written by two processes. - God-mode autonomy, native HITL. A privileged god agent (lives in Michael's room) adjudicates cross-agent traffic. Routine requests (clarifications, data asks, plan tweaks) it resolves itself and the system keeps running fully autonomously. Critical items (destructive ops, spend, scope changes, unresolvable conflicts) route to the god, who surfaces them to the human natively in his own Claude Code session — there is no separate approval queue. Tool-permission prompts are the HITL gate.
- Memory: markdown first. Per-agent
memory.md+ shared blackboard, with a SQLite FTS index when keyword recall isn't enough. A heavyweight vector layer (Letta/Mem0/Zep) is not needed at 5–15 agents and is architecturally wrong here (they want to own the agent runtime; our runtime is theclaudeCLI). Optional future upgrade: MemPalace over MCP (validate its retrieval first — its public benchmarks are overstated per independent audit). - Autonomous loop =
Stophook. An agent that finishes drains its inbox via aStophook that returns{"decision":"block","reason":…}to keep it working, guarded bystop_hook_activeto prevent infinite loops.
Lives under <harnessHome>/hive/, a git repo committed only by the main process.
hive/
PROTOCOL.md # the agent-facing contract (how to remember + message)
registry.json # roster: every agent, role, capabilities, status, seat
board.md # shared blackboard / co-authored plans
tasks.json # task ledger (id, assignee, spec, status, result ref); god writes it alone
triage/ # one file per team member filing, written once; god works them oldest first
.office-id # random id the app's board guard keys its private state on
log.jsonl # append-only event feed (drives the UI activity stream)
agents/<agentId>/
identity.md # who am I, my role, my capabilities (read at start)
memory.md # my long-term memory (I read at start, append as I learn)
inbox/ # messages delivered TO me — <ts>-<msgid>.json
inbox/.done/ # processed messages (kept for audit, not deleted)
outbox/ # messages I want to SEND — router drains these
cursor.json # { lastProcessed: <msgid> } — avoids reprocessing
Design rules that make this robust:
- One JSON file per message, written via temp-file + atomic
rename— never a co-edited shared mailbox file (those conflict under git). - Append-only
log.jsonl; consumers track their own cursor. board.mdis the one genuinely co-edited file — it goes through the god agent (single scribe) to avoid conflicts.
Borrow the one useful idea from FIPA-ACL/KQML — the speech act — and drop the LISP syntax. Seven semantic fields:
Anti-livelock rules: only request/query/propose obligate a reply (pure
inform/done are terminal); every reply increments hops; past a hop cap the
god agent escalates instead of letting two agents loop forever; re-seeing a
processed id is a no-op (idempotent via cursor).
agent B mid-task needs something from agent C
│ writes agents/B/outbox/<msg>.json (act:request, to:C)
▼
┌─────────────────────── main process (the harness) ───────────────────────┐
│ Router watches every outbox/ │
│ → deliver to agents/C/inbox/ (to:"human" → routed to the god proxy; │
│ the god surfaces critical calls natively in its own session) │
│ → append to log.jsonl → git commit (single committer, retry+backoff) │
└──────────────────────────────────────────────────────────────────────────┘
│ delivered to C's inbox
▼
agent C finishes its current turn → Stop hook fires
│ hook POSTs to the hive socket; main process checks C's inbox
│ unread messages? → reply {"decision":"block","reason": <messages>}
▼
agent C keeps working: reads the messages, acts, replies via its own outbox
The same hook socket drives the avatars: PreToolUse/PostToolUse payloads move
an agent to the right station (replacing today's mockEvents.ts / PTY-scraping).
A fixed, always-on agent seated at desk-ceo (Michael's room), character: michael, flagged isGod. It is an ordinary claude process — the intelligence
— while the main process is the mechanism (git, sockets, routing). It owns:
- Roster & routing (
registry.json): who exists, their capabilities, status. - Adjudication: read each outbound request; resolve routine ones itself (answer clarifications, route to the right specialist with a self-contained task spec), escalate only critical ones. This is "god mode."
- Blackboard scribe: the single writer of
board.md, so shared plans never conflict. - Task ledger (
tasks.json): assign, track, retry, checkpoint. The single writer oftasks.jsontoo: team members file what they need intriage/and tell god when their work moves (docs/designs/card-lifecycle.md section 10).
Its escalation policy (what counts as "critical") lives in its system prompt and is the primary control surface — tune the prompt, not the code.
- Phase 0 — Foundation ✅:
hive.tson-disk layer + spawn injection (identity, protocol, env) + IPC to read hive state. Agents are hive-aware: they read their memory/inbox at task start and send via outbox; the router delivers; everything is committed and visible. - Phase 1 — Autonomy ✅:
hooks.tsUDS server +cth-hookshim (attached per agent via--settings) +Stop-loop so agents drain their inbox automatically and keep running (guarded bystop_hook_active+ cursor); hook events stream to the renderer to drive avatars. - Phase 2 — God mode ✅: the god agent auto-spawns into Michael's room
(
desk-ceoreserved). Nothing is typed into his terminal at start: his startup instructions orient him, and the standup sent when the office opens gives him his first turn. The router routesto:"human"traffic to the god (the human's proxy); there is no separate approval queue — human-in-the-loop is native to each agent's Claude Code session (permission prompts). Idle agents are woken when they hold unread inbox messages. - Phase 3 — Semantic memory ✅ (CLI integration):
memory.tswraps the MemPalace CLI (not MCP, by decision). The harness keeps one shared palace underharnessHomeand points every agent'sMEMPALACE_PALACE_PATHat it. The palace holds company knowledge only: each agent's own notes are its own and are never mined (owner, 2026-10-16), so an agent asks a teammate what it knows instead, and searches company knowledge by meaning withmempalace search. Detect-and-degrade: a no-op whenmempalaceisn't installed (markdown memory still works). Default modelminilm(light, for low-RAM Macs);embeddinggemmais the multilingual opt-in. It has no switch since 2026-10-19 (owner): it runs whenever MemPalace is installed, and Settings, Memory & Knowledge shows whether it is ready and sets its search language. Michael's Memory tab searches the team's notes by exact words only.memory.mdis now a short index the app keeps. Agents add notes tomemory/inbox.md, and a background tidy up (memoryTidy.ts, Haiku with no tools) sorts them in with itemised changes, so memory stays bounded.
| Risk | Mitigation |
|---|---|
index.lock corruption |
Single committer (main process), retry+backoff, stale-lock cleanup |
| Infinite Stop-hook loop | Guard on stop_hook_active; hops cap; CLAUDE_CODE_STOP_HOOK_BLOCK_CAP |
| Two agents ping-ponging | Only request/query/propose obligate replies; hop cap → god escalates |
| Reprocessing messages | Per-agent cursor.json; processed messages move to inbox/.done/ |
memory.md unbounded growth |
App kept index with a size budget, tidied in the background (memoryTidy.ts) |
| Modifying the user's repo with hooks | Hooks ride in a per-agent settings file passed with --settings, kept at <harnessHome>/agent-settings/<id>.json; nothing is written into the user's repo or ~/.claude |
| Office data leaving through the internet (every office) | Only the Research agent (research, Pete by default) has web tools or shell network; the hook refuses WebFetch and WebSearch to everyone else, Michael included, whose shells get an empty strict network list. Every message to Pete passes the trust layer (trustLayer.ts): passed, cleaned or refused against the owner's data classes, fail closed |
| Web text steering the team | Pete's answers pass the outbound trust layer: instructions aimed at agents are removed, the answer is labelled as information, and a likely injection is flagged to Michael only. Pete writes only to the teammate whose question he answers, or to Michael |
| An agent posing as a teammate on the hook socket | Each start gets a secret in memory, handed over in the agent's environment; the sender is taken from it and frames without a valid one reach nothing (issue #61). The environment is readable by other processes of the same user, so this stops stale processes and name claims, not a member that reads a sibling's environment (TODOS: hand the secret over where another process cannot read it) |
| An agent rewriting its own hooks or sandbox | The hook program (<harnessHome>/hive/bin) and agent-settings are locked for every agent, Michael included: shell deny-write, an Edit deny rule, and the hook's own folder check. Reading stays open (issue 100). Windows has no shell sandbox, so there only the file tool rules hold |
- Anthropic — Building a multi-agent research system (lead/subagent, plan-to-memory).
- LangGraph supervisor (structured routing + handoff registry + checkpoints).
- FIPA-ACL / KQML (speech acts).
- Stanford Generative Agents (memory stream, reflection, 2D world).
- Claude Code hooks reference (
Stop,PreToolUse,UserPromptSubmit;stop_hook_active).
{ "id": "2026-05-30T14-03-11-123Z-a1b2", // set by the app, unique, time-sortable; one an agent writes is replaced "conversation": "conv-7f3", // groups a thread "in_reply_to": "<prev msgid> | null", "from": "agent.researcher", "to": "agent.coder | god | broadcast", "act": "request | inform | propose | query | agree | refuse | done", "subject": "short human-readable summary", "body": "free text / markdown / structured payload", "hops": 3, // ++ per reply; capped to kill ping-pong loops "requires_reply": true, // only request/query/propose obligate a reply "needs_human": false, // router/god may flip this to escalate "created_at": "ISO-8601" }