This is the long-form reference for the multi-role workflow introduced briefly in the README. Most users will never need this page; reach for it when a lock gets stuck, when an exclusivity decision surprises you, or when you're building tooling on top of agmsg.
actas <name> is exclusive across sessions: it switches both sending and receiving to <name> and prevents any other live session from picking up <name> until you release it.
Mechanically, the skill:
- Joins
<name>under your current team if it isn't registered for this project yet. - Claims an exclusivity lock on
(team, name)under the skill's run directory (~/.agents/skills/agmsg/run/actas.<team>__<name>.session). - TaskStops the running
agmsg inbox streamMonitor. - Relaunches the Monitor filtered to
<name>only, viawatch.sh's optional 4th argument.
Effects:
- Messages addressed to other roles stop reaching this session.
- Other live sessions stop subscribing to
<name>— their watchers exclude any pair locked by a peer at startup. - If another session already holds the lock,
actasrefuses with a clear error. Drop it from that session first.
The lock is released by drop, by session end, or by garbage collection when the holding session is no longer alive.
drop <name> removes only that role's registration for this project (via reset.sh). If the role is no longer registered anywhere, it's also dropped from the team config.
If <name> was the currently-active role, the watcher is restarted in default mode — no actas name filter, so it receives every (team, agent) pair registered for this project that isn't held by another session.
Switching is session-scoped state held by the agent. /clear or a new session resets back to the multiple-identities picker.
actas-claim.sh writes the lock file before the skill TaskStops the old Monitor and launches the new one. If that subsequent dance fails — TaskStop succeeds but the new Monitor invocation errors out — the lock stays put but the session has no narrowed watcher.
To unstick:
- Run
/agmsg drop <name>in this session, or - End the session.
Either releases the lock so peers can pick it up.
A stale lock is reclaimed when its owner session_id no longer maps to any live cc-instance, where "live" is checked via kill -0.
PID recycling could in theory keep a long-dead session looking alive forever, starving peers from claiming or reaching its name. This is tracked in #67 and not addressed in v1.
agmsg follows a one CC session = one active role model. Each watcher subscribes to a static set of identities decided at launch:
- Without
actas: the watcher subscribes to whichever(team, agent)pairs were registered for this(project, agent_type)at the momentwatch.shstarted, minus any pair currently locked by another live session'sactasclaim. The set is not re-resolved later — a peer that claims a name after this watcher launched will start receiving exclusively, but this watcher won't notice the loss until it restarts. A role joined mid-session viaactasfrom another CC does not start arriving in CCs that were launched before it. - After
actas <name>: the watcher is relaunched filtered to<name>only, and the lock that filter implies prevents peer watchers from ever subscribing to<name>while this session is live.
This is intentional. It keeps each CC bound to one role's inbox, so a tech-lead window stays clear of biz-analyst traffic and vice versa, and the exclusivity holds across sessions on the same machine rather than per-session.
To pick up a role added after a CC launched (without switching to it exclusively), restart the CC or /clear so SessionStart re-launches watch.sh with the fresh identity list — and with the up-to-date lock view.
The send side mirrors this: every send.sh call from this CC uses the active role as the from agent, whether that's the implicit one (default) or the one set by the most recent actas.
On Codex, $agmsg actas <name> is send-side only for this session. Codex slash commands don't see a stable session_id, so they can't claim a peer-visible exclusivity lock — Claude Code peers will still subscribe to <name>.
The receive side isn't actually narrowed either: check-inbox.sh resolves identity through whoami.sh (which picks the first registered agent) and has no view of the agent's in-session actas role, so Codex keeps polling whichever pair it would have without actas. The check-inbox lock filter only skips pairs another session owns.
Treat Codex actas as a from-line override until a Codex session-id story exists. Claude Code's /agmsg actas does claim the lock symmetrically and is the path that exercises the full exclusivity model.