A Door Coop Does Not Own
Every Claude Code session with cross-session messaging binds its own inbox: a Unix domain socket that other sessions on the same Mac can write to. Claude Code built it so one of your sessions could warn another one mid-task. Coop is not a Claude Code session and has no seat in that conversation, yet it still has to hand one specific chat an invitation and, later, its work. So it uses the same door the way a careful neighbor would: find exactly one live chat, check that the door really belongs to that chat, push one message through the slot, and walk away. It does not wait on the step for an answer, because the slot never gives one.
That last sentence is this whole track in miniature. A socket write has no synchronous delivery acknowledgment. Whatever Coop knows about what happened next, it learns afterward, by reading the receiver's own records. Native doors are observations.
Read the Receiver Twice, Then Once More
Claude Code keeps a small registry file for each running session: its session id, its process id, the process start time, a peer protocol number and the absolute path of its messaging socket. ClaudeDesktop.target() reads that registry and accepts exactly one live match. The pid must equal the file's name. The recorded start time must equal the live process's birth time, or the entry is an exited process's leftover. The peer protocol must be 1. The socket must pass private_socket. What comes back is four facts: pid, birth, socket path, socket inode.
send calls target() twice and refuses with Native receiver changed before send if the two readings differ. Then it hands those four expected facts to the host, which is office itself or another Mac reached through Coop's ssh helper, and the host reads the registry a third time, on the machine where the chat actually lives, and compares again. Only then does exactly one newline-terminated line go into the socket: connect, sendall, shut down the write side, close. Nothing is read back.
Why three readings? Because between "I found the chat" and "I wrote to it", a chat can exit, a new process can reuse the pid, and a socket path can come to name a different file. A reading is not free: it runs ps once for every registered chat to read that process's start time, so it costs milliseconds per chat, and the host's reading on another Mac also crosses ssh. That is still a bargain. A frame written into the wrong chat is an instruction handed to a stranger.
What Counts as Private
private_socket is four checks and one answer. The path must be a socket, not a regular file someone parked there. It must be owned by the user Coop runs as. Its mode must carry no group or other bits (mode & 0o077 is zero). And its parent directory must pass the same owner and mode test, because whoever controls a directory controls what its names point at. Pass, and you get the socket's inode, which is what the later comparison pins. Fail, and you get Native socket ownership or permissions changed, never a best guess.
Notice what is not on the list: the chat's working directory. A Claude Desktop 2.1.284 chat opened for a project starts in a scratch workspace and moves later, and a worker may cd around its checkout. On 2026-09-30 discovery filtered candidates by registry cwd and found a chat sitting at READY ten minutes late, after Dad had already told it to go ahead by hand (commit d056235, titled "cwd is data, not identity"). Identity is the session id, the live pid with its birth time, and the private socket. Everything else is description.
The Frame Itself
One JSON object. type is user; session_id names the receiver; uuid and msg_id both carry the outbox message id; from is cwkCoop; priority is next; message holds the prompt. Reusing the outbox id as the native uuid is the move that makes everything afterward possible: the id Coop stored before sending is the id it will search for in the transcript.
And notice what the frame does not claim. It asserts no permission mode for the sender. Hold that thought until the next lesson; it cost a day.
Receipts Come from the Transcript, in Two Shapes
Because the socket answers nothing, the adapter reads the chat's transcript forward from a byte offset it recorded before sending. An idle receiver starts a new turn with the frame and writes a user event whose uuid is the message id. That was the only shape Coop read until 2026-10-04. That day, todo #310's invitation reached a chat that was in the middle of a turn. Claude Code absorbed the frame into the running turn and wrote a queued_command attachment whose source_uuid was the message id, with the reason absorbed_mid_turn. The worker answered COOP_ACCEPT. The adapter, looking only for the first shape, never saw the envelope arrive, so it dropped the final that answered it, and the invitation sat there watching (commit ff2a225).
peer_input now accepts both shapes and checks them alike: isMeta must be true, and origin must be exactly {kind: peer, from: cwkCoop, msg_id: <the id>}. A match with the wrong provenance is a Conflict, not a receipt, and the same envelope seen twice is a Conflict too. An absorbed envelope's answer is the final of the turn it joined, however many tool calls later.
Five Facts, Never One
Section 9 of Coop's architecture refuses to let "sent" stand in for anything else. It records five separate facts:
| Fact | Where Coop learns it on the Claude door |
|---|---|
| 1. Coop durably stored the message | The outbox row, committed inside the engine's own transaction |
| 2. Host transport accepted it | The socket write returned without an error |
| 3. The harness received it | A peer entry, in either shape, with the exact id and provenance, in the transcript |
| 4. The receiver accepted offered work | A completed final carrying COOP_ACCEPT with this invitation's nonce, then the engine's one-transaction take |
| 5. The work acquired an outcome | A report from the bound worker, or a recorded decision by Dad or the chief |
An unobservable receipt stays unknown. A Coop that folded fact 2 into fact 3 would have called #310 delivered while nothing had been bound.