Patterns for the Agentic Age

Your agents already know each other's addresses. What they should never do is deliver the mail themselves.

When many agents need to talk to each other, wiring them directly is a trap. Every agent would have to know exactly where every other agent's inbox physically lives, and one rename breaks the whole mesh. A post office fixes it: agents already know who they're writing to, they just hand the letter to one place, and a registry, not the sender, knows exactly where each inbox lives.

Picture a city where every resident must memorize every other resident's home address to send a letter. Add a resident, everyone updates their address book. One person moves, every letter to them bounces. That is a multi-agent system with direct wiring, and it collapses under its own connections as it grows.

The post office is the fix humans already invented. You know exactly who you're writing to; you address the envelope yourself. What you don't do is drive it to their house. You hand it to the post office, and the post office, which knows the physical location behind every address in its territory, delivers it. When someone moves, the post office updates its own records. The address you wrote never has to change, and not one of your letters breaks.

This page is that pattern applied to agents. One shared outbox, one registry, one routing script. The design turns out to be small, deterministic, and boring in exactly the way infrastructure should be.

01

The one decision that makes it work: the outbox

Agents never write to another agent's inbox. They write to one shared outbox and walk away. A routing script owns every delivery.

This is the whole design in one rule, and everything good follows from it. The sender already knows the address; it writes the recipient straight into the message. What the sender never does is touch another agent's inbox, so it never needs to know exactly where that inbox physically lives. Its job ends the moment the addressed message hits the outbox. Delivery, the permission check, logging, and failure handling all become the routing script's problem, in one place, instead of being reimplemented in every agent.

The message-routing workflow
Every message dropped in the outbox follows this same route, in this order, every time. Watch the envelope stop at each step.
1

Write

Sender drops a message in the shared outbox.

2

Look up

Postmaster reads the registry to find the recipient's inbox.

3

Check

Is the sender allowed to write to this recipient?

4

Deliver

Message moves to the recipient's unread inbox.

5

Log

Every action appends one line to the delivery log.

02

The postmaster is a shell script, not an agent

This is the load-bearing principle of the whole system. Routing a message from an outbox to an inbox based on a lookup table is pure deterministic mechanics. It has no judgment in it. So it must not be an LLM.

Using an agent to move a file from one folder to another based on a YAML lookup is slower, more expensive, and less reliable than the ten lines of shell that do it perfectly every time. The rule generalizes far past this system: if a step has a single correct output for a given input, it is mechanics, and mechanics belong in code. Save the agents for the work that actually needs judgment.

The postmaster runs on a schedule, every ten minutes by cron. It is stateless: it reads the outbox, routes what it finds, and exits. Nothing is stored between runs, so it is safe to call at any time, as many times as you like. For an urgent message, any agent just calls the postmaster directly instead of waiting for the next cycle.

Why this matters

Every deterministic task you hand to an agent is a slower, pricier, less predictable version of a script. The skill is knowing which is which. Mechanics to code, judgment to agents.

03

The registry is the wiring diagram

One YAML file is the address book. Each agent has an entry: its address, where its inbox lives, what message types it accepts, and which senders it accepts them from. That last field, accepts_from, is a security filter. The postmaster rejects mail from any sender not on the list, before it ever reaches an inbox.

The move that makes the registry powerful is one extra field: emits. An agent declares not just what it receives, but what it sends and to whom. Now the registry is not just an address book; it is a complete map of every message flow in the system, readable in one file. That changes what integration means. Connecting two agents stops being a code change and becomes a config change; you wire the system by editing a declaration, not by editing agents.

# one agent's entry in the registry
- id: council.product-agent
  address: product-agent@council.internal
  inbox: ~/.dna/post-office/inboxes/council.product-agent
  accepts:
    - type: council.evaluate      # what it receives
  accepts_from:
    - orchestrator@council.internal  # who may send
  emits:
    - type: council.vote          # what it sends back
      to: orchestrator@council.internal
this line is the payoff

Each plugin ships its own declaration file, the way a package ships a manifest. A setup script merges those into the global registry and fails loudly on any address collision. The plugin owns its declaration; the system owns the global contract. Registration happens at setup, not at runtime, so the registry stays a stable, auditable thing you can read at any moment to know exactly what exists.

04

Every message carries its own audit trail

A message is a markdown file with a small frontmatter header. The sender fills in the obvious fields: who it is to, who it is from, the type, a subject, and a correlation ID. Two fields the sender never sets, because the postmaster injects them at delivery.

The message ID is an MD5 hash of the message body. The sender cannot set it, for the same reason a Git commit cannot contain its own commit hash: putting the hash inside the thing you are hashing is a circular dependency. Computing it at delivery gives you three things for free. The message is self-validating, since re-hashing the body proves it was not corrupted. Duplicates are caught automatically, since identical messages produce identical IDs. And no coordination is needed: no shared counter, no UUID service, no race.

The sequence number orders messages within a flow. Every message in one pipeline run shares a correlation ID. The postmaster groups by that ID, sorts by send time, and numbers them. Now you can reconstruct any run exactly: filter the delivery log by correlation ID, sort by sequence, and you have the complete ordered history of that flow. It is git log for a single pipeline.

The pattern underneath

Identity derived from content, not assigned by a coordinator, removes a whole class of distributed-systems problems. No central counter to contend on. The content is the ID.

05

Nothing is silently lost

Silent message loss is the hardest class of bug there is. A phone rings, nobody picks up, and neither side knows it happened. A pipeline stalls and you have no thread to pull.

So the system refuses to lose anything quietly. A message to an unknown address does not vanish; it goes to the dead-letter queue with the reason recorded. A message from an unauthorized sender goes there too, marked rejected. Nothing is ever deleted. When a pipeline stops unexpectedly, you inspect the dead-letter queue and the delivery log, and the reason is written down.

On the receiving side, agents acknowledge by moving, not deleting. A processed message moves from the unread folder to the read folder. The read folder is the acknowledgment and the audit trail at once. You can always see what an agent has and has not handled.

DELIVERED → moved to read/
UNDELIVERABLE → dead-letter/, reason recorded
~/.dna/post-office/
  agent-registry.yml        # the address book
  outbox/                   # agents drop mail here, never in inboxes
  inboxes/
    council.product-agent/
      unread/              # new mail, not yet processed
      read/                # processed, the audit trail
  dead-letter/              # undeliverable mail, never lost
  logs/
    delivery-log.jsonl      # one line per routing action
  scripts/
    postmaster.sh           # the router, run by cron

The principles that travel

Most of what makes this work is not specific to agent mail. These ideas apply anywhere you are wiring independent parts together.

One drop point, not N

Route through a hub and each part hands off to one place. Wire directly and every part must know exactly where every other part physically lives, and the mesh breaks on the first move.

Mechanics to code, judgment to agents

If a step has one correct output for a given input, it is mechanics. An LLM there is a slower, pricier, less reliable script.

Declaration over modification

Each part declares what it accepts and emits. The system is assembled from declarations, so integration is a config change, not a code change.

Identity from content

Hash the content for its ID instead of assigning one from a coordinator. Self-validating, deduplicating, and free of shared-counter contention.

Fail loud, never silent

Undeliverable goes to dead-letter with a reason. Silent loss is the worst bug class; make every failure leave a trace.

Acknowledge by moving

Processed work moves from unread to read. The move is the acknowledgment and the audit trail, with nothing deleted.

Part of a larger body of work on agent design.

This is one pattern in an ongoing series on how to build with AI agents: how they fail, why they fail, and how to structure the systems they run inside.