Agent teams: document a contract for external processes writing to teammate inbox files
The agent-teams docs describe ~/.claude/teams/{team}/inboxes/{agent}.json and state that Claude Code validates entries on read and removes malformed ones — but there is no documented entry schema, no locking contract, and no statement about whether external writers are supported at all.
Observer tooling (herdmates: a mission-control board over team files) has exactly one write: a human-confirmed "nudge" message appended to a stuck teammate's inbox, done under an advisory lock with read-modify-atomic-rename. Today that operates in an undocumented seam: a validation change could silently discard entries, and concurrent writes between Claude Code and the external writer can lose updates because no shared locking protocol exists.
Ask, in order of preference: (1) document the inbox entry schema + a locking/append protocol external writers may rely on; (2) or a small CLI/IPC affordance to enqueue a message into a teammate's inbox; (3) or an explicit statement that external writes are unsupported, so tooling can stop offering them.
3 Comments
Related prior asks for the option-(2) shape (a small affordance for external processes to enqueue a message): #27441 and #53049 request an external-process message-injection API. Cross-session messaging (v2.1.224) doesn't cover this case — its socket is reachable only by a session's own child processes, and cross-session messages bypass the team inbox files entirely, so observer tooling still has no supported write path to a teammate.
Running into the same undocumented-seam problem on an orchestration layer that coordinates Claude Code agent teams via their inbox files.
The specific gap we hit: there is no defined locking protocol between Claude Code's own inbox writer and an external process. The atomic-rename approach (write to a temp file, rename over the target) is safe when Claude Code is NOT concurrently writing, but if Claude Code has the file open during a team step, the rename can win the race and Claude Code's write goes to the old fd, producing a silent drop.
A minimal documented contract would need to cover at least three things:
Without (3), it is very hard to debug whether your write was accepted without adding a polling loop that reads back the inbox after each write.
One thing that has helped us in the interim: write a separate sidecar file (e.g.
inboxes/{agent}.pending.json) that the external process owns exclusively, and have a thin wrapper process move entries into the real inbox when it holds the lock. Adds a hop, but contains the race.Happy to share the pattern in more detail if it would help the docs effort.
We hit the same undocumented-seam problem coordinating Claude Code agent teams (herdmates). One finding that may help this thread: for processes that are children of a session, there's a push path that avoids the inbox-file race entirely, because Claude Code owns the write end to end.
A session exports
CLAUDE_CODE_MESSAGING_SOCKET(per-session UDS) andCLAUDE_CODE_MESSAGING_TOKENto its child processes. The wire protocol is NDJSON, at most two lines — and while it's not in the public docs, the v2.1.237 binary logs the literal recipe at inbox startup:Verified live on v2.1.237/Linux: a child process posted, and the message reached the receiving session's model as a real turn — enqueued mid-turn, delivered after, re-framed as "Another Claude session sent a message" (not as user input), with
origin: {kind:"peer", from:"unknown", verifiedPeerPid:<pid>}— the kernel-verified poster pid, recorded even though the poster had exited before delivery.Two details relevant to the debugging-observability ask in (3): the auth line is conditional (the inbox logs
REQUIREDvsoptionalfrom its own state — when key-file publishing fails it accepts unauthenticated peers), and a post can be answered with apeer_message_statusreceipt (held/denied/expired), so unlike the inbox-file seam you can observe whether your message was accepted or held for user approval (permission-mode parity).Honest limits of what we've verified: the probe was a direct child of the session (same relationship a hook has, but not a literal hook invocation); the hold path under
bypassPermissionsis untested; there's no visible way to name the sender (it showsfrom: "unknown"); and this doesn't cover the fully-external-process case this issue asks for — the socket/token only reach the session's own children. For the remaining inbox-file seam, +1 on documenting the entry schema and validation-failure behavior.