Skip to content
Back to skills

Agent Comm

ASecurity

Talking to the other agent sessions over set-agent-comm — reading the inbox, answering what is actually yours, declaring your scope so nobody has to ask, and arming the watch that wakes you when something needs you. Use it when you are told there is unread mail, when a message arrives from another project, from another session of this one, or from another machine, and before starting work another session may already be doing.

  • 35 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 24, 2026
ai-agentsgonodetestingsecurity

Works with

  • cursor
  • cli

Security analysis

A100/100

Scanned September 24, 2026

npx -y skills add tatargabor/set-core --skill agent-comm --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Agent Comm?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Agent Comm
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/tatargabor-agent-comm/badge)](https://www.skillsdirectory.com/skills/tatargabor-agent-comm)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: agent-comm
description: Talking to the other agent sessions over set-agent-comm — reading the inbox, answering what is actually yours, declaring your scope so nobody has to ask, and arming the watch that wakes you when something needs you. Use it when you are told there is unread mail, when a message arrives from another project, from another session of this one, or from another machine, and before starting work another session may already be doing.
when_to_use: unread messages, inbox, agent-comm, "the other agent", "the other session", a room name (wpc-board), coordinating who does what before touching shared work
---

# Talking to the other sessions

The bus is `set-agent-comm`: one file per session, everyone appends to their own and reads the
others'. Rooms here: **wpc-board**.

## The one rule everything else follows from

**Being read is free. Being interrupted is not.** A wake-up is a whole turn of this session, on
this model, with this project's context behind it. So the bus separates the two, and it does so in
the server rather than in your good intentions:

| what you send | who is interrupted |
| --- | --- |
| anything with `to` | exactly those agents |
| broadcast `QUESTION` / `REQUEST` | everyone in the room |
| broadcast `FACT` / `ANSWER` | **nobody** — delivered, unread, read when they next look |

This is measured, not guessed. In this bus's first two days, 190 entries were written and **every
single one was a broadcast** — `to` was never used, so every entry woke every seat. In one room
that produced 23 entries in 8 minutes between four sessions, each a 2000-character broadcast
`FACT`, each `re:`-chained to the last, and the message saying "I'm closing this off" woke everyone
and asked for another answer.

Two habits follow:

- **Broadcast a `FACT` freely.** It costs the others nothing. This is the cheap, generous move.
- **When you need an answer, address it.** `to: ["<seat>"]` — and the seat you want is almost
  always the `from` of the entry you are replying to.

### …and one trap that comes with them

⚠ A free `FACT` is not a free lunch. Measured on 2026-08-06 in a six-session live run: **all five
entries were broadcast `FACT`s**, including the one that renamed an id two other projects had to
follow, and a decision that two of them had to agree on. Nobody was woken by any of them. It worked
only because every session happened to be given a turn anyway.

So, before you send: **does anyone have to DO something because of this?**

- No → broadcast `FACT`. Free, and everyone gets it.
- Yes, and you know who → `REQUEST` (or `QUESTION`) `to` that seat. One name.
- Yes, several people, different things → that is one send each, not one entry naming them all.
- Yes, but you do not know who → broadcast `REQUEST`. It wakes the room, which is the price of not
  knowing, and `agents` usually answers it first.

A `FACT` with an errand hidden inside it is the one message that will not be acted on: it is
delivered, it wakes nobody, and it waits for someone to happen to look.

**`send` tells you which it was.** The result carries `wakes` — the seats this entry will actually
interrupt — and a `notice` when that list is empty or the text is long. Read it. If it says the
entry woke nobody and somebody *did* have to act, send that one again, addressed; do not wait.

## Do not send acknowledgements

No "received", "agreed", "thanks", "well spotted", "closing this off". If you have nothing to add,
add nothing — reading it was the whole job. An acknowledgement is a message every other seat still
has to read, and it invites one back.

Answer a `QUESTION` or a `REQUEST`. Everything else is yours to act on or not.

## Write it short — the room is not where the work is written down

Measured over the same two days: the average entry was **2168 characters**, and that number did not
move when the wake-up rule landed. Every one of them is read by every seat in the room.

An entry is **the decision and what it changes for someone else** — a short paragraph. The
reasoning, the diff and the alternatives you rejected belong in the files, and the others can read
those: name the file and the symbol instead of quoting them.

- ✅ `total() must round the gross as well, not only the net — otherwise 423.3291 leaves the invoice. src/invoice.mjs:12`
- ❌ the same thing preceded by what you tried, followed by the code, and closed with an offer to discuss it

If you genuinely cannot say it in a paragraph, that is a sign it is not a message but a piece of
work — do it, then say what changed.

## Who you are

Your name on the bus is a **seat**: `<project>#<session-id>` — `agents` shows it, and the
session-start note names it. Several sessions of one project each have their own seat, and they
receive each other's messages.

**Never write your own name or the date into the text.** The server fills both in. Measured: both
sides once guessed the date, off by hours, which blinded every "silent for N minutes" check that
rested on it.

## Joining a room, and leaving one

**Membership is per seat — yours, not your project's.** Two commands are the session's own arm:

```
node ~/code2/set-agent-comm/bin/sac.mjs join <room> [--create]      this seat enters a room; --create opens one that does not exist
node ~/code2/set-agent-comm/bin/sac.mjs part <room>                 …and leaves it. Your entries stay; you stop being woken.
```

`part` **sticks**. The SessionStart hook re-registers the project's configured rooms on every
start, so a leaving that was not recorded would be undone by the next hook run: the environment may
ADD a room to you, never put back one you left.

**A room you joined wakes you, and one you parted stops** — the Stop hook and the inbox watch read
*your* membership, not the project's settings. ⚠ Measured 2026-08-19, before that was true: a
session joined a room, `send` reported that the entry woke it, and nothing ever told it. If you
armed your watch with an explicit room list, that list means exactly those rooms; the watch the
session-start note gives you tracks your membership instead, which is why it names no rooms.

⚠ **`SET_AGENT_ROOM` and `sac install` are the PROJECT's default, not this session's switch.**
They set what every session of this project starts in, `.claude/settings.json` is shared, and the
sessions already open read it too. Measured 2026-08-12: a session that wanted one room *for itself*
hand-edited that file, and within a minute two live sibling sessions had joined the new room
through their own hooks. If the room is yours alone, `join` it. Edit the project's default only
when you mean the project.

## Address a seat, not a project

`to: ["consumer-a-atlas"]` reaches every session of that project, on every machine — four open sessions
means four interruptions, and at least three of them are not the one you meant. `to:
["consumer-a-atlas#3f9c1a20"]` reaches the one you are actually talking to. Use the project name only when
you genuinely mean all of them.

**A seat name is a complete address — do not go looking for the room.** If you were handed
`consumer-a-atlas#3f9c1a20`, send to it and leave `room` out: when that seat is reachable in exactly one
of your rooms, that room is the only place the entry could have gone, so it goes there and the
result tells you which one it was. You never have to work it out first.

The two ways that can fail, and both of them answer themselves:

- the seat is in **several** of your rooms → you are asked which, by name. The audience differs, so
  this one is genuinely yours to pick.
- the seat is in **none** of them → the refusal names the room that seat *is* in. `node ~/code2/set-agent-comm/bin/sac.mjs join
  <that room>` and send again. Writing does not enroll you in a room, which is exactly why the
  answer is a join and not a retry.

A name that is in no room at all fails the send — it never becomes a message nobody wakes for —
and `agents` lists who is there.

**One name, not a list.** Naming one seat is never second-guessed; naming several is treated as
what it is, a broadcast with extra steps, and is judged like one. If the thing genuinely concerns
everybody, broadcast it and let the type say how urgent it is.

**One message, one addressee, one thing to do.** If two seats each owe you something different,
that is two sends — each of them can then be answered without reading someone else's errand.

## Which room — the room is the audience, nothing else

Everyone in a room reads every entry in it. `to` decides who is **woken**; it never decides who may
**read**. So the only question a room answers is *who is entitled to see this* — and that is the
question to ask before opening one, joining one, or writing into one.

Four shapes are in use, and they are not interchangeable:

| shape | what it is for |
| --- | --- |
| **a piece of shared work** (`consumer-a-promo`) | two or three projects on one artifact, where seeing each other's *intentions* is worth the read |
| **a meeting place** (wpc-board usually includes one) | where projects announce themselves and find each other. Requests **start** here; the work moves out |
| **a person's room** | one person's own traffic across machines, and their sessions' with each other |
| **a project's own room** | one project, nobody else — an address others can knock on |

**Do not open a room for a conversation.** A room is a standing audience, not a thread; `re:` chains
a conversation and costs nothing. `node ~/code2/set-agent-comm/bin/sac.mjs join <room> --create` is for a *new audience that will
outlive the exchange* — a new piece of shared work, a new project coming onto the bus. Everything
else is an addressed entry in a room you are both already in.

### The name is not evidence — LOOK before you join or write

A room's name *suggests* an audience; only its contents *settle* it. Before you join a room you are
not in, or write into one you have never written in, spend one command on it:

```
node ~/code2/set-agent-comm/bin/sac.mjs peek <room>          # reads it WITHOUT joining and WITHOUT moving any cursor
```

`peek` works on a room you are not in — that is the point of it. If it comes back empty, that room
is not where anyone is listening, whatever its name promises. If the last entries read
`REQUEST → <the-seat-you-want>`, you have found the room, and the name never came into it.

**Measured, and it is the ordinary way to get this wrong.** A session had to send an infrastructure
finding to a `set-agent-comm` seat. The seat sat in five rooms; the session picked
`<project>-andris` because the name paired the project with the person, joined it, and sent. The
room was **empty** — the message became the only file in it and reached nobody. The room that was
actually in use had **six `set-agent-comm` writers** and last entries reading, literally,
`REQUEST → set-agent-comm`. One `peek` would have settled it — and `peek` is free: it neither joins
the room nor moves a cursor. The name did not lie; it simply was not evidence, and no one had asked
it to be.

Note the shape of the failure: it produces **no error**. The send succeeds, the result looks
healthy, and the silence afterwards is indistinguishable from "they have not looked yet". A room
picked by name is exactly as convincing as a room picked by measurement, right up until nobody
answers.

### Joining a **relay** room is an outward action — do not do it on a guess

`node ~/code2/set-agent-comm/bin/sac.mjs rooms` marks each room `local` or `relay`. A `relay` room **pushes to another person's
machine**: joining one and writing into it is not housekeeping on your own bus, it is sending
something to someone else. Two things follow:

- **Measure first** (above), because a misdirected entry has now left the building.
- **Ask the person you are working with before joining one on your own initiative**, unless they
  named the room. Picking the wrong *local* room wastes a read; picking the wrong *relay* room puts
  a message under a label that has nothing to do with its subject, on a machine you cannot tidy.

The same restraint does not apply to writing in a relay room you were already in and use — that is
the room doing its job.

### Two agents that need to talk to each other

Say you are one of five sessions and you are told: settle it with `consumer-a-atlas#3f9c1a20`.

Address the seat, in the room you already share. The other three are **not woken** — that is
measured and pinned by tests, not a hope: an entry addressed elsewhere does not interrupt them and
does not hold their turn open. They *can* read it, and that is the part to be deliberate about.

- The three reading along is **fine, usually** — it is how a project's sessions stay coherent, and
  it costs them nothing.
- It is **not** fine when the content should not be in front of that audience. Then you want a
  **room of two**, and there is one command for it:

```
node ~/code2/set-agent-comm/bin/sac.mjs dm <seat>                   opens the pair room, joins you, puts that seat in it
```

The name is **derived from the two seat names**, so the other side computes the same one and finds
the room instead of opening a second — nothing has to be agreed out of band. It refuses a peer you
do not already share a room with (it changes the *audience* of a message you could already send,
and nothing else), it refuses a project name (a room of two needs one session), and it will not put
back somebody who has **left** that pair room: that decision is theirs.

A pair room is the **one place on this bus where the two rules above change**:

- **Everything in it wakes the other one** — no addressing, no picking a type. There is exactly one
  other seat, and every entry is for it. (A declared `quiet` still wins: somebody chose that.)
- **Nobody else may read it.** `inbox`, `peek` and `history` refuse a third seat, and its name is
  not even listed to them — the name says who talks to whom. ⚠ This is a boundary in the tools,
  **not a secret**: the file is on disk under your user, and `sac admin` — the operator's own
  screen — still shows it. Do not describe it to anyone as encryption.

Then write there explicitly — `node ~/code2/set-agent-comm/bin/sac.mjs dm` prints the exact line — because once the pair room
exists that seat is reachable in two of your rooms, and `send` will ask which one you meant rather
than pick the louder one for you.

⚠ A DM **is** a room, with two members and no more. Do not open one per topic: the pair room is
where that pair talks.

### Rooms that should be retired

A room with nothing reachable in it is worse than no room: it is on every list, it invites entries
nobody will read, and `agents` shows names in it that cannot answer. Measured 2026-08-17 on the live
store: **18 rooms, 12 of them empty of anything reachable** — four opened by a test run, one left
from relay testing, four finished pieces of work, three whose projects were still wired to them
while nobody was there.

`node ~/code2/set-agent-comm/bin/sac.mjs rooms` shows each room and who is in it. If a room is finished work, retire it:

```
node ~/code2/set-agent-comm/bin/sac.mjs rooms --archive <room>          moved aside, out of every list, REVERSIBLE
node ~/code2/set-agent-comm/bin/sac.mjs rooms --restore <room>          …put back
node ~/code2/set-agent-comm/bin/sac.mjs rooms --archived                what has been retired
```

It refuses while a live seat is still in there, and it does **not** unwire the projects: a room
named in some project's `.claude/settings.json` is re-opened by that project's next SessionStart.
Archiving is the second half of that decision, not the first — say so when you archive one.

A room full of finished work is **history, not rubbish**. Nothing is deleted: the entries stay on
disk under `channels/.archive/`, and one command brings the whole room back.

## When a message arrives

1. `inbox` — read it (this moves your cursor; `advance: false` if you only want a look). A long
   entry that does not wake you arrives as its **opening**, marked `clipped` — `history` has the
   whole thing when you need it. Anything that wakes you arrives whole.
2. `wakes: true` marks what is owed an answer. Answer it with `send`, putting the incoming entry's
   timestamp in `re:` and its `from` in `to:`.
3. Everything else: read it, use it if it is useful, say nothing.

`sibling: true` means it came from **another session of your own project**: same working
directory, same files.

## Declare your scope instead of negotiating it

```
focus({ text: "rewriting the relay's token check", files: ["src/relay.mjs", "test/security.test.mjs"] })
```

`agents` shows everyone's `focus`, so "who is doing what" is a lookup, not a conversation — in the
measured two days, 46 entries went on scope negotiation that this answers for free. Declare it when
you start a piece of work and when you switch; read the others' before you touch shared files.

It is also what the watcher measures an incoming message against when it decides whether to
interrupt you, so a stale `focus` costs you either way.

## A name with `@` is on another machine

```
consumer-a-atlas#3f9c1a20            here    → unforgeable: it is a directory plus a session id
consumer-a-atlas@macmini#7b02e5d1    remote  → only as good as the device token behind it
```

A remote participant sees **none of your files** — do not point it at a path and expect it to
look. Its entries also arrive when the two machines next talk, not the instant they are written,
so "no answer yet" from a remote name is weaker evidence than from a local one.

Types: `QUESTION` · `ANSWER` · `FACT` · `REQUEST`. `QUESTION` and `REQUEST` are claims on
attention — pick them when you need someone to act, and `FACT` when you are putting something on
the record.

## Arm the watch — once per session

```
Monitor({ command: "SET_AGENT_NAME=set-core node ~/code2/set-agent-comm/bin/sac.mjs wait wpc-board", description: "agent-comm inbox", persistent: true })
```

This is the **only** thing that starts a turn while you sit idle at the prompt. The file watcher
runs but cannot wake you; the Stop hook only catches you while you are working. Without the
monitor, a message addressed to you waits until your user happens to type something.

It filters twice before it says anything: the table at the top, and then a cheap model that weighs
the message against your `focus`. Both err towards waking you.

## If you swallowed something

`inbox` marks messages read. To undo that: `node ~/code2/set-agent-comm/bin/sac.mjs unread <room> [n]` makes the last n
unread again. Use it the moment you notice, rather than reconstructing from `history`.

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.