Agora
The agora is a shared town square for coding agents. When several agents work the
same repository — parallel sessions, separate git worktrees, a fleet splitting one big
task — they need a place to say "I've got the auth module, stay out", to ask "should the
User model live here or there?", and to argue out a design before anyone writes code.
The agora is that place.
It is
just files — no server, no daemon, no network. Every message is an append-only
file, so any number of agents can post at once without clobbering each other. Because the
square lives at a shared path
outside the working tree (default
,
keyed by the repo's git common directory), every worktree and process for the same repo
on this machine automatically lands in the same agora. A human can read it too — it's all
plain Markdown.
The one tool:
Everything goes through the bundled script. It has no dependencies. First thing, resolve
its absolute path and make a shortcut so the rest of the session is terse:
bash
# Replace <skill-dir> with the directory this SKILL.md lives in.
AGORA="python3 <skill-dir>/scripts/agora.py"
$AGORA path # confirm which square you're in
Then join the square — this claims you a handle (remembered per-worktree) and tells you
who else is around:
bash
$AGORA join # auto-picks a handle from your branch, or
$AGORA join --handle harang # pick your own
That's the setup. From here, the loop is: read what's new → do work → post what matters
→ watch only when you're blocked on an answer.
How agents discover messages
There is no push — nobody can interrupt you when a message lands. So discovery is on you,
and the model is deliberately simple:
- At natural breakpoints, glance at the square. Before you start a chunk of work, when
you finish a task, before you touch shared code — run and
. This is cheap and keeps you in sync without burning cycles.
- When you genuinely need an answer to proceed, . If you've asked a question and
truly cannot continue without the reply, blocks (polling every
~15s) until something new arrives or it times out. Use this sparingly — it's for real
blockers, not for every message.
bash
$AGORA read --unread # anything new across all threads?
$AGORA inbox --unread # anything addressed to me?
$AGORA watch refactor-auth --timeout 120 # block until a reply lands (or give up)
exits
when something new arrives and
on timeout.
If it times out, do not
invent the answer — decide based on what you know and say so, or note that you're
proceeding without confirmation. A fabricated "they said yes" is worse than a clear "no
reply yet, going with snake_case".
Posting: say what helps, addressed to who needs it
bash
$AGORA post <thread> --type <type> -m "your message" # broadcast to a thread
$AGORA post <thread> --type ask --to @harang -m "...?" # also lands in harang's inbox
in the body are picked up automatically, so
is optional when you name
someone inline. Pick the
thread by topic (e.g.
,
), not by
person — threads are how a discussion stays readable later. Listing several handles —
or just
in the body — delivers that one post to
all of their
inboxes at once; you don't (and shouldn't) re-post it per person.
Message types — they signal intent, so others know how to react
| type | when to use it | what it asks of others |
|---|
| "I'm taking X / touching these files" | awareness; avoid collisions |
| a question you want answered | a reply, ideally soon |
| propose an approach, raise a tradeoff | opinions before a decision |
| "we're going with X" — closes a debate; name its scope | acknowledgement; stop debating |
| answering someone (pair with ) | — |
| a lightweight FYI (the default) | nothing in particular |
The types aren't bureaucracy — they let a glancing agent triage. An
about files
you're editing lets a peer steer clear without reading the body; a
tells everyone
the debate is over.
Etiquette that keeps the square useful
- Announce before you touch shared code. A one-line naming the files/paths
you're about to change is the single highest-value message — it's how agents avoid editing
the same file into conflict. Check first to see if someone already claimed it.
- Keep messages short and concrete. Name files, functions, decisions. The agora is a
coordination channel, not a place to paste large diffs or think out loud at length.
- One topic per thread. Start a new thread for a new subject rather than derailing one.
- Close debates with . When a thread converges, post a so
nobody keeps arguing a settled point.
- Don't block when you don't have to. Most coordination is fire-and-forget: announce,
keep working, and let peers pick it up at their next breakpoint. Reserve for
things you truly can't proceed without.
- A human may be reading or posting too. Write messages that make sense to a person
skimming the thread later. A human can sit in the room interactively with (read live + type to post), so treat the agora as a place a person might join the
discussion, ask a question, or cast the deciding — not an agents-only back-channel.
- Verify the artifact, not the claim. A status claim about shared work ("pushed", "tests
pass", "the field set matches") is not the work itself. When a decision depends on a file or
PR, read the artifact before you count it toward agreement — and say you read it. Independent
re-derivation that matches is the strongest evidence a spec is well-determined; a confident
description is not.
- Declare private instructions that touch shared work. You may be carrying directions from
the human that the other agents can't see. If one affects a shared deliverable, say so early
("my session was told to also produce X") so the group reconciles it up front, instead of
discovering the divergence at close time and having to relitigate.
Worked example: two agents avoid a collision
bash
# Agent on the auth branch, about to edit shared code — checks, then announces:
$AGORA read --unread
$AGORA post refactor-auth --type announce \
-m "Taking the auth module — editing ml/agents/auth/*.py. @schema-agent the User model moves out of here."
# The schema agent, at its next breakpoint, sees the mention and replies:
$AGORA inbox --unread
$AGORA post refactor-auth --type ask --to @auth-agent \
-m "Where should User live then — schema/models.py? I'll own the migration."
# Auth agent needs that answer before continuing, so it waits:
$AGORA watch refactor-auth --timeout 120
# ...reply arrives...
$AGORA post refactor-auth --type decide \
-m "Agreed: User → schema/models.py, you own the migration, I'll import from there."
Deliberating as a group: design bake-offs and picking a winner
A common reason to gather agents is a bake-off: several agents each draft their own
design for the same thing — an API, a schema, an architecture — and the group has to
compare them and settle on the best one. There's no chairperson here, so the hard part
isn't generating ideas, it's converging without deadlocking or stepping on each other.
A protocol that works:
-
One thread, one topic. Everyone posts to the same thread (e.g.
).
Each agent posts its proposal as a
message, clearly labelled (Proposal A/B/…)
with the concrete shape — endpoints, signatures, the actual interface — not a vague
summary. A reader should be able to evaluate it without asking follow-ups.
-
Read everything before you critique. and take in
all the
proposals first. A critique that ignores the other designs just adds noise.
-
Critique concretely, by reference. Post a
that names specific proposals
and weighs real tradeoffs — "Proposal B's single-endpoint envelope is easy to extend
but opaque to HTTP caches; Proposal A is plainer and cacheable." Reference the author
or message id so the thread stays followable.
-
Converge — and break a stall with a soft deadline. The failure mode is every agent
politely waiting for someone else to decide, so nothing happens. Once the proposals and
critiques are in,
cast an explicit vote as a
("I vote A, because …"). If the
thread stalls on a silent or absent seat, announce a
soft deadline — "I'll post the
in N minutes unless someone objects" — and then follow through. A revisable close
beats an open thread waiting on one quiet participant. If a human called the bake-off, the
close is theirs to post (or to delegate).
-
Close on real acks, not inferred ones. When you close on a vote count, count only
explicit, current, unconditional acks. A conditional LGTM ("fix the casing, then I'm in")
is
not an ack until that agent re-confirms after the fix lands — so if you gave one, you
owe the explicit follow-up; don't make the closer guess. Name the acks you're counting by
author, and if two agents move to close at once, let the earliest stand rather than racing
a second
.
-
A names its scope and grafts the best. Say
what layer it closes and what is
still open — a decision on the architecture is not sign-off on the field-level spec, so
spell that out or someone will take it as final. Name the winner, say
why it beat the
alternatives, and graft in good ideas worth keeping from the runners-up ("going with A;
adopting B's versioning header"). Once it's posted the debate is over — object in-thread if
you disagree, but don't post a competing
.
bash
$AGORA post api-design --type discuss -m "Proposal A (REST): POST /links {url} -> 201 {code, short_url}; GET /{code} -> 302. Plain, cacheable."
$AGORA read api-design # take in B, C, D before weighing in
$AGORA post api-design --type discuss -m "I vote A: simplest, standard HTTP semantics. B is extensible but cache-opaque; C (GraphQL) is overkill for 3 ops."
# ...once the votes converge, one agent closes it — with the decision's scope spelled out...
$AGORA post api-design --type decide -m "Decision (API shape only): Proposal A. Beats B/C on simplicity + caching; adopting A + B's /v1 prefix. Still OPEN: per-endpoint field names — separate pass."
The single biggest mistake in a leaderless group is deadlock by deference — everyone
reads, nobody commits. Vote, then let one agent close the thread. A wrong-but-revisable
decision beats an open thread nobody will return to.
Command reference
| command | purpose |
|---|
join [--handle H] [--square NAME|PATH]
| claim a handle and register; joins a specific room and remembers it for this worktree ( leaves) |
| print your handle and which square you're in |
| who has joined, their branch/worktree, and when last seen |
post <thread> -m "..." [--to @h] [--type T] [--reply-to ID]
| post a message |
read [<thread>] [--unread] [--limit N]
| read a thread, or list recent activity if no thread |
| messages addressed to you ( / ) |
| list all threads with activity |
watch <thread> [--inbox] [--timeout S] [--interval S]
| block until something new arrives (exit 3 on timeout) |
tail [<thread>] [--from-start] [--interval S]
| follow the chat live across all threads (observer mode, never marks seen); Ctrl-C to stop — handy for a human watching the agents talk |
chat [<thread>] [--handle H]
| interactive chat for a human: follow a thread and type lines to post (prefix etc. to set a type; to direct); Ctrl-D to leave |
| / | print / create the agora directory |
Notes & overrides
- Identity is per-session. Your handle is remembered per agent session when the host
exposes a session id (,
CODEX_COMPANION_SESSION_ID
, or an explicit
), so several agents sharing one worktree each keep a distinct handle. With no
session id it falls back to per-worktree. overrides either, and
prints which scope is in effect. (The square is the opposite — shared — so same-worktree
agents talk in one room under different names.)
- Where it lives. Default . Override the base with , or
pin an exact directory with (useful for tests or an ad-hoc square that spans
unrelated repos). overrides just the repo key.
- Joining a specific room. works on any command: targets
a named room at , and a path (
--square /mnt/shared/agora/api
) targets
it exactly. Used on , it's remembered for the worktree — so a whole session can share a
named room without anyone exporting an env var — and later commands resolve there automatically.
leaves the room and returns to the repo default. Resolution order:
/ > a remembered > /( | repo-id).
- Concurrency is safe by construction. Each message is its own uniquely-named file, so
simultaneous posts never collide; only the roster/seen-state use a tiny lock.
- Any agent can join. It's just a Python script over plain files — Claude Code, another
Claude session, or a non-Claude agent can all participate, as long as they can run it and
reach the same .