Set up an agent team chat¶
A team chat is a Hadron memory that several participants — AI agents
(Claude Code sessions or similar) and humans — read and write to
coordinate on one task. Every message is a node; the server assigns each
one an ordering seq, so any participant can read only what's new since
its last turn. Each agent is kicked off with a generated prompt that
teaches it the protocol; humans just post messages.
This is a validated workflow. The reference implementation is a runnable prompt template that lives in the agent's system memory and travels with the agent across installs.
By the end you'll have:
- a shared chat memory all participants can read and write,
- one or more agents started with a kickoff prompt,
- messages flowing with server-assigned ordering,
- optionally, push delivery into live Claude Code sessions.
Prerequisites¶
- A Hadron org and the
hadronCLI installed (Homebrew) and authenticated (hadron auth login). - An Agent and the App that installs it in your org — the app is
what hosts the chat participants. For example the agent
hrn:agent:hadronmemory.com::agent-teaminstalled ashrn:app:micromentor.org::agent-hadronmemory-com-agent-team. See Building an agent for the agent/app setup. - A shared memory for chats that every participant can read and
write — for example
micromentor.org::agent-team-chats. Grant access the same way you would for any shared memory: add each participant (the app's members, plus any human accounts) with write access, or share via the app key. If a participant can't post, that's almost always a missing write grant — see Debug PERMISSION_DENIED errors.
The data model¶
One chat is a slug under chats:. Messages are nodes at
chats:<chat_slug>:messages:<YYYY-MM-DDTHHMMSSZ>-<handle>, each of type
message. The message payload lives in the node's data block:
{
"author": "iris",
"identity": "Claude Fable 5",
"role": "Backend Engineer",
"timestamp": "2026-06-21T21:34:00Z",
"body": "@rufus I've drafted the API schema. Can you check it?"
}
authoris the handle;identityis the real model (or"human").- The server auto-assigns
seqfor each new node in an ordered sibling list — participants must never set it. Incremental reads then work byseq:
hadron node ls -m <chat_memory> \
--prefix chats:<chat_slug>:messages: \
--seq-gt <last-seq> --sort-seq asc
On the first turn there's no prior seq — use --seq-gt 0 to read the
whole history.
- Replies are a reply edge from the new message to the
message it answers, which renders as a visible thread.
For the addressing rules and message-node type, see Node types and the CLI reference.
Step 1: Understand the kickoff prompt template¶
The kickoff prompt is a runnable task node — a Mustache template stored
with isRunnable: true and its accepted arguments declared in
Node.data.args. The reference template lives in the agent's system
memory (it's generic, so it travels with the agent across installs):
hrn:node:hadronmemory.com::agent-team-system::prompts:agent-prompt
It takes six arguments, all required:
| Argument | Example | Meaning |
|---|---|---|
chat_memory |
micromentor.org::agent-team-chats |
Memory (org::memory) holding the chats |
chat_slug |
api-redesign |
The chat's slug under chats: |
handle |
iris |
The agent's chat handle |
role |
Backend Engineer |
The agent's role |
location |
hadron-server repo |
Where the agent works |
assignment |
the run-tool loop (spec 042) |
One-line description of the task |
Running the task compiles those variables and emits a ready-to-paste
prompt that teaches the agent the whole protocol: how to read new
messages with --seq-gt, how to post a message node, and how to add a
reply edge.
Don't copy the template from a compiling read¶
This bit is non-obvious and worth internalizing before you edit or copy the template. Mustache renders a missing variable as an empty string, with no error — so how you read the node changes what you get back:
| Read path | Template behavior |
|---|---|
hadron task run / hadron_run_task with args |
Compiles with your args — the intended path. |
Portal / CLI / GraphQL node(ref:) reads |
Compiles against node + memory data; undefined variables silently render empty — this blanks the template if you copy from it. |
MCP hadron_get_node / raw reads |
Returns the source verbatim — use this to view or edit the template. |
If you ever need to see or change the template itself, read it with
hadron_get_node (or another raw read), never through a compiling read.
See Mustache template syntax for the
full resolution rules.
Step 2: Kick off each agent¶
Run the task once per agent, filling in that agent's identity and assignment:
hadron task run prompts:agent-prompt -m hadronmemory.com::agent-team-system \
--arg chat_memory=micromentor.org::agent-team-chats \
--arg chat_slug=api-redesign \
--arg handle=iris \
--arg role="Backend Engineer" \
--arg location="hadron-server repo" \
--arg assignment="the run-tool loop (spec 042)"
Paste the output as that agent's kickoff prompt — one run per agent, with
a distinct handle. Humans need no prompt; they just post messages
(Step 3).
Because a missing arg renders empty rather than erroring, check the output before pasting: if a section looks blank, you dropped an arg.
The MCP equivalent is hadron_run_task with the same values passed as
args.
Default for
chat_memory. The template compiler falls back to the memory's defaultdatanode for any unset variable (see template syntax), so the agent's author can bake a default chat memory into the system memory'sdatanode — runs that omit--arg chat_memory=…then inherit it, and a run-time arg still overrides it. You can't set this yourself from an install: an agent's system memory is read-only once the agent is installed into an App. Treatchat_memoryas an arg you always pass, and pick the chat memory per run.CLI note. Use the bare-loc +
-m <memory>form shown above (hadron task run prompts:agent-prompt -m …) rather than a positional URN — the positional-URN form is affected by hadron-cli#171.
Step 3: Participate as a human¶
A human posts a message the same way an agent does — the shape is
identical, with identity: "human". Put the data in a JSON file (a
multi-line body won't survive inline shell quoting):
cat <<'EOF' > msg.json
{
"author": "biene",
"identity": "human",
"role": "Project Lead",
"timestamp": "2026-06-21T21:45:00Z",
"body": "@iris @rufus looks good. Rufus, mock it on the frontend?"
}
EOF
hadron node create -m micromentor.org::agent-team-chats \
--loc chats:api-redesign:messages:2026-06-21T214500Z-biene \
--name "Message from biene" --type message --data-file msg.json
You can also post from the portal's node UI or via hadron_create_node.
To reply, add the edge from your new message to the one you're answering:
hadron edge create \
--from micromentor.org::agent-team-chats::chats:api-redesign:messages:2026-06-21T214500Z-biene \
--to micromentor.org::agent-team-chats::chats:api-redesign:messages:2026-06-21T213400Z-iris \
--name reply
Pass full org::memory::loc URNs to edge create and omit -m — a
bare loc with -m joins memory and loc with a single : and won't
resolve.
Step 4 (optional): Push delivery into live sessions¶
Polling with --seq-gt works everywhere and is all an agent needs. If
you want new messages to arrive automatically in a running Claude
Code session instead of on the next poll, the
hadron-client team-chat channel
upgrades the chat to push:
- hadron-client registers the Claude Code
claude/channelcapability, watches the chat, and injects each new message into the running session as a<channel source="hadron-chat">event. - It exposes a
chat_postreply tool, so the agent answers without shelling out to the CLI.
Setup, in the project's .hadron/config.json:
{
"handle": "iris",
"chat": {
"memory": "micromentor.org::agent-team-chats",
"messagesLoc": "chats:api-redesign:messages:"
}
}
Then launch with the channel enabled:
Caveats:
- Requires Claude Code v2.1.80+.
- Anthropic auth only — not Bedrock or Vertex.
- Watcher state is persisted in
.hadron/chat-state-<subscription>.json, which makes Hadron the durable queue across restarts: a session that was offline catches up on everything it missed.
Gotchas¶
- No colons in the time portion of the loc. Use
2026-06-21T175936Z-iris, not…T17:59:36Z-iris— colons are loc separators and will split the address. - Never set
seq. The server assigns it for ordered lists; the first read uses--seq-gt 0. - Reply edge direction: source is your new message, target is the message you're answering.
- Don't copy the template from a compiling read (Step 1) — undefined variables render as empty strings and quietly gut it.
- A blank section in a kickoff prompt means a missing arg. Mustache doesn't error on it; you have to eyeball the output.
Related¶
- Building an agent — create the agent and app that host the chat participants.
- Mustache template syntax — the variable-resolution rules behind the compile-vs-raw-read table.
- Node types — the
messagenode type and therecord/messagedistinction for high-volume content. - hadron CLI reference —
node ls,node create,edge create, andtask run. - Debug PERMISSION_DENIED errors — when a participant can't read or post to the chat memory.