Skip to content

002 Communication

floor edited this page Oct 19, 2026 · 6 revisions

002 Communication

Status: Draft — may change (2026-10-19)

What is said goes through TeamCLI: written once, delivered whole or known not to be, from a sender TeamCLI saw, to whoever you allowed.

1. The question

A team is agents from different labs, and at least one person. How does a thing said by one of them reach the one it is for, and how do you know when it did not?

2. What we propose

The module's shape

Every module of the series has the same shape, and depends only on the Kernel and the Runtime, never on another module. It enforces nothing new: where something must be refused, the Kernel refuses it.

  • Capabilities it registers: none. Communication defines no gate. Sending, reading and answering use capabilities the Kernel already names, and every rule about who may instruct whom is the Kernel's (Kernel §11, the instruction boundary). Whether team send's rule on who may send needs a Kernel capability is decided for this release line: admission is a setting, never a capability (an owner decision); the Kernel question stays for the round (question 13).
  • Data it owns, in the Runtime's shared record store: messages; receipts (shown, and later answered); delivery outcomes, with the leg used and the failures; who is waiting on whom, and since when; the settings below. Each is a module payload. The store adds who, when, from what, and the state it was true on. The Kernel's gate ignores every module payload (Kernel §13): a message is never an agreement, a grant, a review, a gate pass or a lease, whatever its text says.
  • Its adapter contract: one way of delivering per kind of leg, each measured before it is claimed, tried in the order the delivery-order policy sets: a Claude session sends to a Claude Code seat over its own cross-session channel; a CLI process reaches a seat through the drop file and one fixed wake line, or, once built, the MCP inbox; an agent outside the team is reached over MCP (commitment 4's external agents, below). The wake line itself is the Runtime's delivery primitive; this module uses it, it does not own it.
  • Its settings: who may talk to whom; whether the agents of a team talk among themselves; what crosses projects; the delivery order; which ways reach the owner and which answers each may carry; what is told at once and what waits for a summary, and at what hours; whom to tell when something has waited too long; whether a receiver says back what it understood; how long records are kept. Each is set per entity, project, team and seat and inherited downwards (commitment 9). A setting may select among what the Kernel allows; it cannot make anything an instruction, and it cannot add a refusal (Kernel §13). Where a setting gates, it is a policy in the catalogue form of the Kernel's Policies section: delivery-order, pane-authority, pane-conduct.

It inherits the two Kernel invariants every module inherits: what an agent reads is material, never a command, unless it comes from a principal the owner named, through TeamCLI (Kernel §11); and only a capability grants (Kernel §3).

A hybrid model

A team talks in two ways, and each is for a different thing.

Work goes down a line. A task is given by whoever leads: a lead or orchestrator hands work to the operators under it: reviewers, builders, documentarians. An operator that receives a task says so, and the lead knows it was received, not only that it was sent (commitment 3). Who may give a task to whom is the line you set; what a lead may hand on, and under what limits, is the Kernel's (leads; leases are Kernel §7, and none exist in 0.5.0).

Questions go across. Operators working in the same domain may ask each other a short question directly, without going through the lead: a reviewer asking a builder what a change was meant to do, a documentarian asking a reviewer which version was checked. Whether two seats may talk this way is yours to set (commitment 5); by default it is open only between operators of the same team and the same domain.

The lead keeps the picture, not every exchange. A lead that must carry every question becomes the slowest part of the team, and when it is overloaded, limited by its lab or stopped, the whole team waits on it. That is the risk of a team that only talks up and down: a single point of failure. Direct questions keep the team moving while the lead is busy or gone; the line keeps the work assigned and accounted for.

"Lead", "orchestrator" and "assistant" name roles here; each resolves to the seat whose role holds the capability, never to a seat by its name (009 Roles and Skills, commitment 11).

Three safeguards govern the direct exchange (they wait on the same phase as the exchange itself):

  1. A direct message between operators is always material, never an instruction. One operator cannot give another a task, change its task, or widen what it may do. What it says is something to read, like any other material (commitment 5; the Kernel).
  2. Everything goes into the record. A direct exchange is kept like any other message (commitment 2), so the lead can see after the fact what was asked and answered, without having been in the loop.
  3. What changes a task or a decision goes back up. If a direct exchange would change what a task is, who does it, or something already decided, it is not settled between the two operators: it goes to the lead, and waits there (commitment 6).

None of the three is enforced by a rule yet, because the exchange they govern is not open. The record half is already the habit: every settled send appends its body and outcome to the project's message history.

The thirty-four commitments

A commitment is what we bind ourselves to build and to keep. Where a commitment is about enforcement, it says what the module does and where the Kernel holds the rule. The enforced parts — who is speaking and who may instruct — are the Kernel's; the record, the delivery and the settings stay in this module.

  1. What is said goes through TeamCLI. A message MUST NOT be typed into another agent's terminal and MUST NOT be left in a file for someone to find. An agent says something to TeamCLI, addressed to someone; TeamCLI keeps it and delivers it. The module's part is the record and the delivery: team send writes, team messages reads. The only thing still typed is a short, fixed line that wakes an agent which is doing nothing, "you have something waiting"; that line is the Runtime's. Spec: Specs-messaging §3 (the drop file, the history line), §4 (team send). Proof: test/send.test.ts at v0.5.0. Today (v0.5.0). The write side is built. team send writes the message whole and delivers it, with an explicit outcome; what it writes is a drop file and a line in the project's message history (src/commands/send.ts, src/send/history.ts). The fixed wake line is still typed by the same command; no agent types into another's pane by hand. One exception at the tag: a seat whose profile selects a compose module (Jules) has the body passed to that module and is confirmed by a created-session proof line, not a wake line (deliverComposeSeat, src/commands/send.ts:1090–1157; src/profiles/jules.yaml:47; test/jules-compose.test.ts), the exception messaging.md's "What delivered means" describes; trigger-only delivery stays the target promise, and this leg is the gap disclosed. The watch still rings Team: run team messages when a mailbox record waits, and nudges Team watch: reports are waiting in .agents/team.log; nothing writes a mailbox record yet.

  2. A message is a record. A message MUST be a record: who it is from, who it is for, what kind of thing it is (a task, a result, a question, an answer, a thing ready to be accepted, a thing blocked), what it answers, and which piece of work it belongs to. Long material stays in files; the message points at them, and only at files inside what the team was agreed to work in. Pointing at a file serves nothing: the receiver opens it within its own read scope (Kernel §3). The module owns the record's shape; the Runtime keeps it. Spec: Specs-messaging §3 (the message record, the receipt). Proof: test/messages.test.ts at v0.5.0. Today (v0.5.0). In part, on both sides, not yet one store. The receive side's signed record is unchanged: its file layout and the id it is named by are Specs-messaging §3's (the message record); a copy under another id, in another place, or bound elsewhere does not verify; team init keeps the mailbox folder out of git (src/commands/messages.ts). The send side records who it is from, who it is for, the body and the outcome, in a drop file and a history line, not in that signed record. Kind, what-it-answers and the piece of work are on neither yet.

  3. It arrives whole, or you know it did not. A message MUST be delivered entire or not at all, and TeamCLI MUST know which: sent, delivered, read, answered. A message nobody read is something TeamCLI can show, not something found out later. The module's part is the record of each step and an explicit outcome for every send. Spec: Specs-messaging §4 (the outcomes and their exit codes), §5 (the delivery rules). Proof: test/send-deliver.test.ts at v0.5.0. Today (v0.5.0). Yes for sends, in part for reads. Every send ends in one of seven outcomes with its exit code, and delivered says exactly what was seen — for a compose seat (Jules), a created-session proof line after the body went through its module (deliverComposeSeat, src/commands/send.ts:1090–1157; src/profiles/jules.yaml:47; test/jules-compose.test.ts); held and not-submitted separate "on disk, nothing typed" from "typed and not taken"; a hold on the receiver's side is held, never delivered — the lesson of the socket leg that reported delivered for messages sitting in a held-message dialog, after which the tool took no leg that can end there (src/commands/send.ts, src/send/deliver.ts; contract/exit-codes.json's send.* rows). What is not recorded: read and answered as receipts. --wait is refused for every seat in this build: the socket frame it rides is not one this build makes, and a Claude Code seat's refusal names the native leg that will carry a waited send.

  4. TeamCLI knows who is speaking. Each agent MUST reach TeamCLI in a way given to it alone when it is started. A message MUST carry the sender TeamCLI saw, not the name the sender gives itself, and that sender is the agent the Runtime's registry knows, not a second way of knowing who someone is. The module's part: the record keeps the sender TeamCLI saw apart from any name written in the message, and shows the receiver the one it saw. The enforced part, that a sender cannot present itself as another, is identity in the Kernel (§11), and a name a caller supplies is recorded as an unauthenticated claim, never as the actor (Kernel §15). Spec: Specs-messaging §3 (the record's from), §5 (the admission rules). Proof: test/send-admit.test.ts at v0.5.0. Today (v0.5.0). In part. The registry holds an agent record per seat, its id minted once and never reused (Specs-registry §3.1, §3.4); the send path names the sender by that id and prints the rule that admitted it on the drop's first line (src/send/admit.ts, senderHeader). A sender the registry holds no record for is judged from the team file's declaration, and the header says the record is missing; it is never granted. The limit is unchanged: every seat runs as the owner-user, so nothing yet stops a process from writing another's id (§7); keys held apart are the Kernel's.

  5. Who may talk to whom is yours to set. Who may talk to whom MUST be yours to set: an agent and whoever leads it; a lead and those above it; agents of a team among themselves, or not; nothing from one project to another unless you say so. Being allowed to talk to an agent MUST NOT be being allowed to instruct it. A message is material, like anything else an agent reads, unless its sender holds your grant to instruct the one who receives it; a name alone is enough for material status and never for instruction (an owner decision; the Kernel's Grants section). A message from another project, or from a person who is not you, is material; naming the sender is not instructing authority, and the message becomes an instruction only under the applicable grant above. The module's part: it holds who may talk to whom as a setting, routes by it, and tells the receiver which of the two a message is, from the sender it saw. The enforced part, who may instruct, is the Kernel's instruction boundary (§11): a seat takes instructions only from the principals the owner named, through TeamCLI. Whether declining to deliver a message outside the setting is itself a refusal, and so the Kernel's, is open (question 12); decided for this release line: admission is a setting (an owner decision). Spec: Specs-messaging §5 (the admission rules). Proof: test/send-admit.test.ts at v0.5.0. Today (v0.5.0). The rules exist, as a fixed list in the code, default deny, with an owner-admitted pair (a setting, written through team grant, recorded as a setting, never a capability named send: an owner decision) as the way to add one without a code change; a refused pair exits 1 and names its reason; a caller with no agent record is refused unless the team file's own declaration places it. The project level is built: the project lead and the leads of its teams are admitted to each other, the team leads of one project to each other, and seats crossing teams within one project where the project allows it — a declaration, default off, never across projects (src/send/admit.ts, the project rules). A grant names a team by its registry team id or a seat by its agent id, never a runner or process name. What is not there: it is not a per-team setting, and nothing yet marks a delivered message as material rather than an instruction. The mailbox is still not a wall between seats. Direct questions between operators of one team and domain are not admitted yet.

  6. What waits on someone is visible. What waits on someone MUST be visible: to the one it waits on, at once; to the one who is waiting; and to you — at any moment you can see what each agent and each person is being waited on for, and since when. This is the module's own work, read from its records; the clock that says something has waited too long is the Runtime's, and a round's liveness (010 Workflow, commitment 14) reads the same records. Spec: Specs-messaging §3 (the history line), §4 (team feed). Proof: test/feed.test.ts at v0.5.0. Today (v0.5.0). In part. Every send that got somewhere appends a line — sender, recipient, body, outcome — to the project's message history, and team feed shows them (by seat, following, across projects, or as one machine-readable document). Inside one team, a watch tells the team's agents that results wait. Missing: nothing shows you who waits on whom since when; that view (team waits) is not built.

  7. You are told where you are. You MUST be told where you are: on the machine, on a phone, on a watch, in the tools you already use. What reaches you MUST say what happened and where to look; it MUST NOT carry a secret or the contents of a private file. The module shapes what you are told, including the notices the Kernel requires when what a team may do changes; the Runtime delivers them (Kernel §11). Spec: none of its own; the notices ride the Runtime's delivery. Proof: none yet. Today (v0.5.0). You are told on the machine only, by a desktop notification for what a team's watch reports; the owner's channels (Telegram, a webhook, the connectors of the 2026-10-12 decision) are not built.

  8. You can answer from there. You SHOULD be able to answer from there: agree to a proposal, answer a question, pause a team, send a line to an agent. A watch carries only what is safe from it — yes, no, pause, stop and later — and a grant, a revoke, a publish, a payment and a removal MUST be read in full and read back before they are answered (an owner decision). The module's part is to carry the question to the device and the answer back, and to record which device it came from. How strong that answer is, is the Kernel's: an answer from a device counts only when it is as strong as one given at the machine, which means a key on a device you enrolled, an answer that names exactly what it agrees to, and nothing in between able to forge it (Kernel §10, §11). The Kernel also records whether a yes was witnessed or only reported, and a reported yes is never recorded as yours (Kernel §11). A device gives nobody a right they do not hold: an answer has effect only if whoever gives it may do that thing (Kernel §9). Spec: none yet. Proof: none yet. Today (v0.5.0). You cannot answer a notification. Pending Charter round 2.

  9. All of it is yours to set, by entity, by project, by team and by agent. Every setting this module holds MUST be yours to set at each level: who may talk to whom, what crosses between projects, the delivery order, which ways reach you and which answers each may carry, what you are told at once and what waits for a summary, and at what hours, who is above an agent when something has waited too long, whether an agent says back what it understood, and how long messages are kept. A setting made for an entity holds for all its projects, one made for a project for all its teams, and one made for a team for all its agents, until something below says otherwise. A lower level may always be stricter; it may be looser only where the level above allows it (the Kernel's Policies section, §2.3). Every setting MUST have a default that works if you never open it: by default you are told at once what changes what a team may do, what is waiting on you, and a budget floor or a security refusal (an owner decision), and the rest as a summary. A team that tells you everything is a team you stop listening to. No setting makes a message an instruction from someone you did not name; that is the Kernel's and not a setting. Spec: Specs-messaging §5 (the delivery-order policy's rules). Proof: test/settings-inherit.test.ts at v0.5.0. Today (v0.5.0). In part. The delivery order is a policy in the catalogue form, per entity, project, team and seat, with the fallback native, mcp-inbox, typed and the rule that a level drops or reorders, never adds (src/settings/inherit.ts, DELIVERY_ORDER); mcp-inbox is not in this build, so a walk skips it and records it unavailable. You can switch off some of the watch's reports and its notifications, and set how often it looks and how long it waits; seats crossing teams within a project is a declaration, default off. Who may talk to whom is a fixed list, not a setting; quiet hours, summaries, routing and retention cannot be set.

  10. One module, every channel. The communication module MUST be one set of adapters, one envelope, one inbox and one router. Every way in and out of a team — the MCP bridge, a chat app, a voice line, an internal send — MUST be an adapter on that one shape, and adding a channel MUST mean writing one adapter, with no change to the core. Every message MUST become one envelope — an id, a thread, a channel, the sender's identity mapped from the channel's own authentication, timestamps per stage, and a priority — and a thread MUST be able to cross channels (an owner decision). The router MUST answer a state question from TeamCLI's data with no model in the loop; send a command to the MCP tool, with confirmation where a policy asks for it; carry an ask to the right seat through the registry, by id and never by pane; acknowledge long work at once, and bring the reply back on the same channel or the one you prefer; shape each reply for its channel — short and spoken for voice, rich with links for text; measure its efficiency — per-stage latency and failure rates — in one live view; and keep security on every route: per-channel authentication, rate limits, signed replies, redaction, and a record of every message (an owner decision). Spec: Specs-messaging §3 (the envelope's fields); the router's own rules have no spec yet. Proof: none yet. Today (v0.5.0). Not built at v0.5.0: there are no adapters, no envelope, no inbox and no router; one front serves a whole team, and each way in is a path of its own.

  11. Module, seat and nickname are three things. The communication module, the communication seat and a nickname MUST stay apart (an owner decision). The module is the plumbing — the adapters, the envelope, the inbox and the router. The communication seat is a seat like any other: a registry id, a team, its own agent on watch; it converses with you on every channel through the module. A nickname is what a team calls that seat, from its universe and in keeping with its casting; it is never the module and never a second identity. The communication role MUST sit in 009's catalogue, where every other role sits: its grants, capabilities and skills — read a team's state, route an ask, confirm a command on your word, speak on every channel — are the catalogue's to define (009 Roles and Skills; an owner decision). Spec: none yet: the role is 009's. Proof: none yet. Today (v0.5.0). Not built at v0.5.0: there is no communication role, no adapter, and no router; a team's channels are worked by hand.

  12. The address is the seat; a name travels as name@team. A message MUST be addressed to a seat — the normal team address: seats are attributed to agents, and a message goes to the agent on watch when it is delivered, so a restart or a model swap never breaks an address (an owner decision). An agent MAY also be addressed directly — for example, to ask it about its own watch after it was relieved (an owner decision) — under the same admission and authority rules as any other address. Delivery MUST resolve the address through the registry at the time it delivers; a reattribution changes who is on watch, never the address. A name MUST be unique within its team; across the entity a sender MUST be able to address any seat as name@team — a position's name or a nickname — and the form MUST resolve through the registry, never from a name typed into a message (an owner decision). The registry's agent id stays the identity underneath. Spec: Specs-messaging §5 (the name@team forms and their resolution). Proof: none yet. Today (v0.5.0). Not built at v0.5.0 as this text requires: the forms resolve at the tag (src/send/resolve.ts, whose header is the grammar) — a bare name is the caller's team, <seat>@<team> and <nickname>@<team> work from any folder, @<nickname> looks up the caller's team and then the entity, and the agent id is canonical — but the address is resolved when it is sent, not when it is delivered, and nothing delivers to the agent on watch.

  13. TeamCLI runs each channel's whole lifecycle. For every channel — the bridge, a chat app, a voice line — TeamCLI MUST hold the whole lifecycle, not just the messages. Setup MUST be per client: each client's authentication (OAuth or bearer) and its Origin declared in one place, with the front's allow-list and CORS written from that declaration, never edited by hand. Checks MUST run per client, as that client makes them — reachability with its Origin (the preflight, the POST, the .well-known documents), its OAuth metadata, and a signed ask round trip timed per stage — on demand and on the watch, and a failure MUST name the stage and the client. The signer, the shuttles and the bridge MUST be supervised, restarted on crash and logged. A build MUST be deployed only from a reviewed sha, by the one documented path, with the live sha recorded; the per-client checks MUST run after the deployment, and a build MUST be rolled back automatically if any check fails, with a report of what failed; your go is still taken where the policy asks for it. A failure MUST be diagnosable from the evidence the module keeps — the stage, the client, status codes and timings — and end as an issue with what was seen. The server's logs MUST be readable in a scope you grant, read-only, and no wider (an owner decision). Spec: none yet: the formats wait for a channel-lifecycle spec in a later revision. Proof: none yet. Today (v0.5.0). Not built at v0.5.0: the front's settings are edited by hand; there is no per-client declaration, no check matrix, no supervision, no deploy record and no rollback; a fault is found by asking.

  14. A message is kept; one that never arrived is not forgotten. Messages and the trace of who read what MUST be kept for 90 days by default, as a setting per entity. An undelivered message MUST be kept until it is delivered or the retention ends, and MUST be named in the daily report after 24 hours. (an owner decision) Spec: Specs-messaging §3 (the history line, the receipts). Proof: none for retention; the history's file and its rotation are test/send.test.ts's at v0.5.0. Today (v0.5.0). In part, by size only. The history rotates at 1 MB keeping three files (src/send/history.ts); a shown message leaves a signed receipt that is not aged; nothing keeps 90 days, no setting names retention, and no daily report says an undelivered message waits.

  15. An outside agent never instructs a leading seat by being named. An outside agent MUST reach a leading seat only through that agent's own team's lead, or through an owner's grant; a name in a team file MUST NOT be enough. (an owner decision; the Kernel's Grants section) Spec: Specs-messaging §5 (the admission rules). Proof: none yet. Today (v0.5.0). In part, by default only. The rule that reaches across teams admits a lead pair — the lead of one team writing to the lead of another — within one entity, subject to the chain's setting (src/send/admit.ts:172–181, :329–350); different entities are refused on that rule. The project-level pairs are separate rules (the project lead and its teams' leads; the team leads of one project; seats crossing teams within one project where the project allows it), and an owner-signed pair has its own rule (:205). No rule treats a leading seat specially, and the case cannot arise before such a pair is admitted.

  16. An undelivered push is not silent. A push to an outside agent that has not arrived MUST be reported to you after one hour by day and at the next morning report in quiet hours, and the threshold MUST be a setting (an owner decision). Spec: none yet: the formats belong to the channel-lifecycle spec, planned for a later revision. Proof: none yet. Today (v0.5.0). Not built at v0.5.0: no push to an outside agent is made — mcp-inbox is not in this build (src/commands/send.ts) — and no clock names one.

Accepted 2026-10-18 as amendment 01; frozen, editorial changes only.

The eighteen commitments below are 002 amendment 01's, accepted on 2026-10-18 under the owner's delegation (an owner decision) and folded as the text stood at batch 11 (9cc3b10). They run on from the sixteen above and are frozen: editorial changes only, until a later amendment. Their Today lines read at int/0.5.1 (8acb470b, unreleased); the tag v0.5.0 does not build them.

  1. One signed record per message, both sides. The record MUST be one signed record: written by the sender's team send into the recipient's folder, signed with the checkout's message key, the two registry ids, the admitting rule on it, written whole or not at all. The drop file goes; the typed leg types the wake line that names the record; the history line stays, one per settled send. Spec: Specs-events §3, and Specs-messaging §3. Proof: the messages and send tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: team send writes the signed record into the recipient's folder with the sender's and recipient's agent ids, the rule and the thread fields (src/commands/messages.ts, the record payload's from_id, to_id, rule, thread, in_reply_to).

  2. The id is the record's name, minted by the sender's tool, never reused; a second record of one id MUST be dropped by the receiver. Spec: Specs-events §3. Proof: the messages tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built as stated: the filename is the id.

  3. Status is derived, never edited, from the receipts in the order the event log gives them, and it MUST move forward only: a late receipt changes nothing. sent, delivered, read, replied, and held (the record waits: a dialog, unsent text in the box, or an unreachable seat; the side and reason named). held is one word on two records: the attempt's outcome, and the message's status while no later attempt has. Each status is a receipt record beside the message (Kernel §15: monotonic, derived state). The attempt outcomes not-submitted, timeout, unreachable and refused stay outcomes of an attempt, not statuses of the message. Spec: Specs-events §3 (the status words). Proof: the messages tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: the receipt makes the record's status move (src/commands/messages.ts).

  4. A thread is the first message's id. Every record MUST carry thread and, for a reply, in-reply-to; a reply is written by team reply <id> <text> (or team send --reply <id>), goes to the sender of the record it answers, and is admitted when it is written under one rule the thread adds (commitment 34): a thread admits nothing by itself. --wait becomes "wait for the first reply on this thread within the window", carried by whichever leg the receiving seat's policy takes, and a reply after the window still arrives, as an event, with the thread open. Spec: Specs-events §3, §4 (team reply). Proof: the reply tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: team reply <id> [<text>] [--close] writes a signed record in the sender's folder on the same thread, admitted while the thread is open (src/commands/reply.ts).

  5. A question is a kind, an answer another; a thread MUST end on team reply --close or when thread-ttl passes with no record; a reply on a closed thread opens a new one that cites it. Spec: Specs-events §3. Proof: the reply tests at the tip. Today (int/0.5.1 8acb470b, unreleased). The close is built: --close ends the thread, and a later reply opens a new one that cites the closed thread's last record.

  6. The owner's messages are records too: written like any other with owner as the recipient, delivered by the Runtime to his own channels, the liaison's connector beside them where he chose one; his answer returns on the thread by the channel he used. Spec: Specs-events §5 (the owner delivery). Proof: the bus's owner tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built in the bus's owner delivery (src/bus/owner.ts).

  7. An event is a signed record in the project's event log, appended by the bus alone when a record the Runtime keeps changes: a message's status, a seat's state, a piece of work, the roster, a grant, a policy. It MUST say what happened, when, to which record, and nothing else; it MUST carry no body and no instruction. The log mints each event a sequence number in order; a subscriber applies them in sequence and drops one it has seen, so a duplicate or a reordered delivery changes no state. The record is the truth and the event is derived from it: whoever doubts an event reads the record it names. The signature is the checkout's message key, which every seat holds: it says a tool of this checkout wrote the event, not which seat. Spec: Specs-events §3 (the event record). Proof: the bus log tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: the bus appends signed event records to the project's event log (src/bus/log.ts, src/commands/bus.ts).

  8. The bus is a service of its own, beside the broker. The broker keeps its one operation and its socket; the bus MUST be a second process with a second socket, both under the one supervisor, so a slow tracker read never stalls an event and either restarts without the other. Two operations: subscribe (a seat by registry id, its pane, a cursor — a sequence number) and publish (a Runtime tool handing it a record that changed). Each subscriber gets a bounded buffer; one that was away reads from its cursor, the log durable and the subscription a position in it. When the cursor is older than the oldest event the rotated log keeps, the bus MUST answer gap with that oldest sequence, and the subscriber resyncs from the records before following on. A reconcile pass — at the bus's start and every reconcile-interval — MUST list the records with no event and raise them, so a tool that crashed between its record and its publish loses time, never the event. The watch becomes a subscriber first and a poller second, keeping the reads no event can give; its poll raises seat.unreachable for a seat that died without a word. Spec: Specs-events §4 (team bus), §5 (gap and reconcile). Proof: the bus and reconcile tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: team bus runs the bus over .agents/bus.sock, appending signed event records and reconciling durable message and seat records (src/commands/bus.ts, src/bus/reconcile.ts); only the owner runs it, from a terminal outside the pane manager.

  9. The list, with who receives each by default. The event kinds and their default recipients are the spec's table; "lead" is the seat whose role holds leads; "operator" is the file's operator seat; "sender" and "subject" are the records' own fields. Spec: Specs-events §3 (the kinds table). Proof: the kinds tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built as a closed table (src/bus/kinds.ts), with 002 amendment 01's kinds plus release.ready, decision.needed, record.invalid, security.refused and team.event-log-dropped, and the prompt kinds permission, trust, login, vendor notice and rate limit.

  10. Who receives which event is a policy, event-routing, in the Kernel's Policies section's catalogue form. Scope: entity, project, team, seat. Values: a map from event kind to recipients, each a position (lead, operator, sender, subject, owner) or a seat by registry id. Default: the table. Set by: the owner at any scope; a holder named by grant at project or team for non-privileged kinds; a seat for itself only by adding itself, never to a privileged kind.

    A privileged kind is team.grant-*, team.policy-*, seat.at-prompt, and any kind the owner marks privileged — the privileged-kinds setting, owner-sovereign, growing only by his act and never removed by a level. A seat MUST NOT add itself as a recipient of a privileged kind. The default recipients of one stand, and no level may remove any of them; a seat that is a default recipient is not self-subscribing, because the ban is on the seat's own act, not on the table. Only the owner routes a privileged kind — at any scope — or a holder whose grant names that kind; an ordinary routing grant never implies one. A refusal is visible: the bus refuses the write, names the rule privileged-self-subscription with the requester and the kind, appends it as a refusal record to the operational log (Kernel §15's refusal shape; the operational log holds it, not the ledger), and raises team.policy-refused to the lead and the owner. Nothing about a refused subscription is silent. Tighter is additive: a level may add recipients among its own seats and may remove none that any level above it set. The thresholds that delay a notice (held-threshold, unreachable-threshold, idle-threshold) tighten the other way, shorter only. Kind: enforced by the bus; every delivery and every refusal is logged. Spec: Specs-events §5 (the routing policy's form). Proof: the routing and refusals tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: the routing policy, the privileged kinds and the named refusal (src/bus/routing.ts, src/bus/refusals.ts, src/policy/events.ts).

  11. The owner is reached by the Runtime, never through a seat. What the policy routes to owner the Runtime MUST deliver itself: on the machine, as the notification the watch raises today, and in the owner's own team tool's inbox, with no seat in the path. The liaison's connector (commitment 7) is one more delivery beside those, never the only one and never a gate in front of them: a liaison seat held, unreachable or wrong silences nothing the machine shows. His answer comes back on the thread by the channel he used, and the watch's owner-addressed reports become events routed to owner, so his list is one list. Spec: Specs-events §5. Proof: the owner tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built in the bus (src/bus/owner.ts).

  12. The MCP inbox is the transport for Claude seats. The launch installs an MCP server in the seat's session that holds a subscription on the bus; a message or event for that seat arrives as a notice on the next tool round, and the seat's own fetch is the delivered receipt. The notice MUST be a count and a cursor, one line per tool round however many arrived, never one line per event: a stream into a context window is a seat that reads and never works. Nothing is typed into the pane. The seat reads a body with the inbox tool — the read receipt — shown as a message from the seat named on the record, after the signature check, and replies with the reply tool, which writes the record. A session at a permission or trust prompt runs no tool round, so a notice waits there, sent, until the prompt clears; the inbox still wins the order because it never types into the prompt. This is the expectation, measured in the build before the text claims it. Spec: Specs-events §4. Proof: the inbox tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built (src/mcp/inbox.ts); the mcp-inbox leg takes a send only while the seat's own inbox server is listening, and is skipped as unavailable otherwise (src/settings/inherit.ts, the delivery order's note).

  13. The drop leg is the owner's first step (2026-10-18). The record MUST be written whole into the recipient's messages folder and nothing typed. The seat is told by watching its drop folder: the launch installs the watcher where the seat's CLI takes one, and the watcher's or the seat's fetch writes the delivered receipt. The sender's confirmation is that receipt, not a pane status: a record with no receipt is sent and retried as commitment 33 says, and a seat that watches nothing is unreachable after unreachable-threshold, said, never typed at. The leg needs no bus — the record on disk is what the seat reads — which is why it can land first. Which CLIs take a watcher is measured in the build before the leg's default order is claimed for them. Spec: Specs-events §4; the leg's name to the delivery-legs registry. Proof: the delivery tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: drop is a fourth leg beside native, mcp-inbox and typed (src/settings/inherit.ts, DELIVERY_LEGS); it writes the same 0600 file and types nothing, and a seat that watches nothing never walks it.

  14. No typing ever goes into a pane a human types in. A typed line MUST be refused for the owner's own seat and for any pane the owner launched by hand; those seats take the inbox and the drop legs only. The refusal is visible: the leg is recorded unavailable with the reason, the next leg runs, and the log says why. For every other seat typed remains the last fallback, under the pane rules of the Kernel's Policies section (an empty idle prompt only; unsent text or a dialog is held). Spec: Specs-events §5. Proof: the delivery tests at the tip. Today (int/0.5.1 8acb470b, unreleased). The fallback order is built: mcp-inbox, then drop, then typed; the human-pane refusal is stated in the order's own note (src/settings/inherit.ts).

  15. Typed is the fallback for every other CLI: the record is written, the wake line names its id, and delivered is the typed outcome of today. Native stays the sender's own tool, recorded through team route and --record, because the CLI attests nothing on a seat's behalf (an owner decision). The native leg is not in the default order: a level that names it is the sender choosing its own cross-session channel and recording the delivery itself, so native is the one leg a declared word may add. Spec: Specs-events §4; Specs-messaging §4 (team route, --record). Proof: the route tests at the tip. Today (int/0.5.1 8acb470b, unreleased). As stated (src/settings/inherit.ts).

  16. The one-step send (an owner decision, whose universal-channel part stands and whose address grammar is the owner decision's): team send MUST write the record whole, publish it to the bus, which raises message.arrived, and the transport the order picks carries it, in one command and one history line. A crash between the record and the publish leaves a record the reconcile pass finds and an event it raises: nothing is orphaned on disk, and a sender whose command returned no outcome reads the message's status from the record. The separate --record step remains only for the native leg. Spec: Specs-events §4, §5. Proof: the send and reconcile tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: the record is written whole by the send, and the reconcile pass raises events for records that have none (src/bus/reconcile.ts).

  17. Failure and retry.

    1. held (receiver side): the record is written and the event raised; the transport MUST NOT type into a held box and MUST NOT retry blindly. The bus retries an inbox delivery when the seat next subscribes or its state turns to working or idle; a drop delivery at the watcher's next pass; a typed delivery only on seat.idle. Several held records for one seat go as one wake line naming them all, or one per idle turn in order. The sender sees message.held, then message.delivered; a hold longer than held-threshold raises message.held to the lead.
    2. unreachable: no session, no inbox, no watcher, no pane. The record stays sent with the attempt's outcome; the sender is told at once; retry as for held, never by typing at a human pane, and after unreachable-threshold the lead is told. A seat that died without a word is found by the watch's poll, which raises the event. Nothing is dropped.
    3. At a prompt (permission, trust, login, vendor notice, rate limit): the message waits; the seat event goes to the lead, the operator and, for the owner's kinds, the owner; no key is pressed but the one sanctioned path (team answer <seat> trust, where the policy allows it).
    4. timeout of a --wait: the window closed with no reply; the thread stays open and the reply arrives later as an event. Late and doubled receipts: a status moves forward only, in the log's order; a receipt delivered twice MUST be dropped by its sequence.
    5. The rules of the module stand: a fall-through only on a definite failure before the send went through, never on a missing acknowledgement; every attempt recorded with its leg and reason; a duplicate id dropped by the receiver; a transport that cannot start is unreachable, said, not silent. Spec: Specs-events §5. Proof: the bus, reconcile and delivery tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built in the bus's seats and reconcile passes (src/bus/seats.ts, src/bus/reconcile.ts); the retry pacing is the build's tuning, per the acceptance's build notes.
  18. Authority, admission and what an event is not.

    1. Admission is unchanged, and it is checked when each record is written, replies included: no reply is admitted because its thread was. The thread adds one rule to the fixed list, reply-on-open-thread: a reply to the sender of a record on an open thread is admitted while the thread is open, neither seat has been removed, and no revocation or deny applies to the pair. The rule MUST be named on the record, the owner may switch it off per team, and it is how any seat he wrote to may answer him on that thread and no further. A reply to a reply follows the thread; thread-cap (§10, question 8) ends an exchange nobody closes.
    2. An event is not a message: it carries no body and instructs nobody; it is material at most. An admitted message is material too, unless its sender is an owner-named principal allowed to instruct that recipient; the refusal of every other channel as a source of instructions stands (§2, §7; the instruction boundary is the Kernel's, §11). A Claude seat's inbox is a channel for records the tool wrote, not for text anyone injects: it MUST verify the signature before it shows a body, and shows it as talk from a named seat under a named rule; nothing in a body runs, and an event that wakes a seat says something waits, not what to do.
    3. The bus confers nothing: a subscription is not a capability; what a seat may see follows the routing policy and, for bodies, the mailbox's rules (§10, question 18: not a wall yet).
    4. The owner is told of every roster, grant and policy change through the same bus (Kernel §11), the first delivery of Runtime's §10 question 8. Spec: Specs-events §3, §5. Proof: the reply and inbox tests at the tip. Today (int/0.5.1 8acb470b, unreleased). Built: a reply is admitted while the thread is open (src/commands/reply.ts), and the inbox verifies and shows a record, never runs it (src/mcp/inbox.ts). The material-versus-instruction distinction is not built on this side: it is the Kernel's instruction boundary (§11), and nothing yet marks an admitted message as material rather than an instruction (commitment 5's Today).

Held whatever the settings say

The accepted text named four things to hold whatever the settings say. In the re-lay they are held in two places:

  • TeamCLI is to know who is speaking; a sender cannot present itself as another. Enforced by the Kernel (identity, §11; actor and signer kept apart, §15). The module records the sender it saw.
  • Talking is not instructing: no setting makes a message an instruction from someone you did not name. Enforced by the Kernel (the instruction boundary, §11). The module labels each message.
  • What goes out is not a way in: something that reaches your phone does not, by itself, let anything come back as an instruction. Enforced by the Kernel (§10, §11). The module carries answers; it never turns an outgoing notice into a channel for instructions.
  • Nothing private travels in what you are told: it says what happened and where to look. This is the module's own rule, about what it puts in a notice. It refuses nothing; the Kernel's key-shape check (§11) is a separate, narrower thing.

None of the four is fully built. The first half of the first is nearest: the sender on a record is a registry id, not a typed name, but the id is only as trustworthy as the machine's honesty until agents are contained.

External agents

A team will be spoken to by agents that hold no seat in it: an assistant the owner uses on a phone or in a browser, run by a lab on its own servers, that TeamCLI does not start and cannot contain. This is how such an agent is known, when it may instruct, and how an answer reaches it. We are building towards all three. Today one part exists: the MCP bridge, which lets an outside agent ask a seat and be answered.

  1. An outside agent is known by what you enrolled, not by what it says. It holds no seat, so the Runtime's registry does not know it. You enroll it yourself: you give it a credential that is its own, for that agent alone, and TeamCLI records whose it is. A message carries the sender TeamCLI saw from that credential, never the name in the text (commitment 4; the Kernel, §11). A credential can be taken back, and from then on nothing it sends is heard. Today: the bridge is one front for the whole team, on the owner's bearer token; its per-agent setup and key minting are not in this build, and nothing tells one outside agent from another.
  2. It may instruct only under an applicable grant. By default, what an outside agent says is material, like a message from another project (commitment 5). It becomes an instruction only under a grant that applies to the receiver and covers instruction, and only within what you could have said yourself; being enrolled, or named, is not enough (an owner decision). A leading seat keeps commitment 15's narrower route: through the leading seat's own team's lead or an owner's grant, never by being named. It gives nobody a right they do not hold, and it never counts as you: an agree, a grant, a create or a lead it relays is not yours, and is refused as one (Kernel §3, §10, §11). TeamCLI tells the receiving seat which of the two a message is. Today: nothing records such a grant; a seat is told in words.
  3. An answer is pushed to it, not waited for. An outside agent is not in a terminal, so no line can wake it. When a seat answers, or needs to start a conversation, TeamCLI sends the message to an address you registered for that agent, and records that it was sent and whether it was received (commitment 3). Asking again until an answer appears is the fallback, never the design: every interval spent waiting is added to every answer. The standard way in and out is MCP: the outside agent asks through MCP tools, and is reached at an endpoint it published when you enrolled it. The MCP bridge is the reference for the way in; the way out is not built. Today: an outside agent can only ask and wait, and a seat cannot reach it first.

Multi-channel resilience

An owner decision, paraphrased: agents have several ways to reach each other and the owner (the bridge's inbox webhook, a seat on the bridge, a liaison seat, brief files in the team's briefs folder, email, and others). Inbox-based briefs and messages are one channel among others. If one server or channel goes down, agents find another way: redundancy, no single point of failure. Each failed attempt is logged. The ordered ranking of channels must be configurable per team, not hardcoded.

Decided 2026-10-17: that ordered ranking is the delivery-order policy of the Kernel's Policies section, tried in order until one leg confirms, every attempt recorded; the legs and the fallback rules are in Specs-messaging §5.

3. Why

Most of what a team does is wait: for a decision, for another agent, for a person. A team is slow or fast mostly by how well the waiting is handled, not by how fast anyone works. A team that cannot hand things to each other reliably is not a team, only agents in the same room.

Agents used to talk by typing into each other and by leaving files. Text was pasted into another agent's terminal and a key pressed; or a file was written, and someone was expected to look. Both fail quietly. A pasted message was cut at about nine hundred characters and acted on. Messages were typed and never sent. A file wakes nobody: finished work waited three hours one night, and fifty minutes, then two hours, the next day, each time for someone who had not been told.

It is uneven by lab. Agents run in Claude Code can message each other directly and at once, over its cross-session channel; agents run in other labs' own programs have nothing of the kind. A team should not communicate better or worse depending on who made its agents.

Typed text has no edges. It can be cut by the program it lands in, submitted without its Enter, or mangled when it is long; the only way to know what happened is to read the pane back and guess. It carries no sender TeamCLI saw, no kind, nothing it answers and no piece of work it belongs to, so it is an instruction to whoever reads it, from whoever typed it. And every word of it costs the receiver's attention, whether or not it was meant for it.

A record is the other way round. It is written once, kept, and delivered whole or not at all; it says who it is from as TeamCLI saw it, a registry id, with the rule that admitted it, and every send ends in an outcome someone can read.

4. What it costs, and what it does not cover

  • Files on disk. Every settled send leaves a drop file and a line in the project's message history. A busy team writes many; how long they are kept is a setting this module owes and does not have yet.
  • A message key to keep. There is one message key for the checkout, in TeamCLI's configuration folder, apart from the approval key. A key per seat, so a signature could say which seat wrote it, means one key per seat to issue, keep apart and replace; it is worth that only once agents are contained (the Kernel). Pending Charter round 2 where it touches who may speak for whom.
  • The send daemon. It is written: a process per project that would carry sends to a seat's program, one drop queued per seat at a time. No shipped command starts it, so sends take the direct path; run, it is one more process to watch on every machine with such a seat. When it is down, the plan says sends to those seats end as unreachable, said, not silent.
  • The MCP bridge. An outside agent reaches a team through a front on a server and a connector on your machine: one more service to run, one more credential per outside agent, and one more place a message leaves your machine for. What it does not cover: what the outside agent does with what it is told, on servers we do not run.
  • Legs to keep honest. Each leg is measured before it is claimed, and every attempt is recorded; the order is one more setting per level to keep true, and a seat the owner set to socket-accepted — to accept unattested senders on its own socket — is a seat that no longer filters who talks to it.
  • Your attention. The record now writes itself, every settled send leaves a line, the body included, and a record anyone can read is a record someone must read. The log is only worth what it costs a person to look through it, so team feed has to be enough to see what matters, and a summary is owed before direct questions between operators open. Pending Charter round 2 for whether those direct questions stay.

What this release must not pretend

  • A delivery outcome is not a receipt. delivered says the seat's program took the line; it is not a promise the seat acted on the message.
  • A hold on the receiver's side is not delivery. A message that reached a session but sits in a dialog the sender cannot see is held, with the side named, never delivered.
  • The sender id is as strong as the machine's honesty. Every seat runs as the owner-user, so the id on a record stops a mistake, not a process that writes another's id.
  • The mailbox is not a wall between seats. Any seat in the checkout can read every seat's folder.
  • An outside agent is not the owner. Nothing it relays counts as the owner's act.

5. What would change our mind

If the labs agree on a standard way for agents to message each other. Should the labs whose agents we run settle on a common way for an agent to send to another, across companies, with delivery that can be confirmed, TeamCLI should carry messages that way and stop carrying its own. It would keep only what it adds: who may talk to whom, and the record of what was said. The same holds for a protocol that is not the labs' own but proves reliable across them: one we did not write, used by agents we did not start, is better than one only we speak.

If how seats are started or contained makes the record unnecessary. The record exists because typed text has no edges and every seat runs as the owner-user. If the agents' own programs come to offer a delivery that confirms itself, for every lab we run and not only one, the daemon and the dropped file should give way to it. If seats come to be contained, so that each holds a key of its own and cannot read another's folder, the signature should come to say who wrote a message, not only that it was not changed (commitment 4). Pending Charter round 2, where it touches who may instruct whom.

If direct questions between operators cost more than they save. The hybrid model rests on the bet that letting operators ask each other keeps a team moving when its lead is busy. Today the default is already the line alone, so the bet is untested; if opening it shows those exchanges changing tasks without the lead, or a lead unable to keep the picture from the log, the default should close again. Pending Charter round 2 (agent roles).

6. What is true today

"Today" reads at two refs: commitments 1–16 at the tag v0.5.0 of floor/teamcli (52db9b75), which builds none of 17–34; commitments 17–34 at int/0.5.1 (8acb470b, unreleased).

# Commitment Today
1 Everything goes through TeamCLI Yes, with one thing still typed. team send writes the message whole and delivers it; the fixed wake line is the only text typed into a pane, and a composer already holding unsent text is never typed into. One exception at the tag: a compose seat (Jules) takes the body through its module and is confirmed by a created-session proof line (deliverComposeSeat, src/commands/send.ts:1090–1157; src/profiles/jules.yaml:47; test/jules-compose.test.ts), as messaging.md's "What delivered means" describes; trigger-only delivery stays the target, with this leg the disclosed gap. Nothing writes a mailbox record.
2 A message is a record In part, two stores. Receive side: signed records at .agents/messages/<seat>/<id>.json, bound to the checkout and the session, kept out of git; nothing writes one. Send side: a drop file and a history line (from, to, body, outcome). Kind, what-it-answers and work item: not yet.
3 It arrives whole, or you know Yes for sends. Seven outcomes, each with its exit code; delivered says what was seen (for a compose seat, a created-session proof line); held and not-submitted say what happened instead. --wait is refused for every seat in this build. Read and answered are not receipts yet.
4 TeamCLI knows who is speaking In part. Agent records with immutable ids (agt_ + 12 hex); the sender on a record is that id, with the admitting rule; an unregistered sender is judged from the file's declaration and the header says so. The signature is still integrity, not authenticity: every seat runs as the owner-user. Pending Charter round 2 (Kernel §11, §15).
5 Who may talk to whom is yours; talking is not instructing The rules, as a fixed list. The admitted pairs are built into the code, default deny, with the project level's three rules beside them and an owner-admitted pair (a setting, not a capability) to add one; a refusal exits 1 and names its reason. Not a per-team setting yet; nothing marks material vs instruction on delivery. The mailbox is not a wall between seats. Pending Charter round 2 (the instruction boundary).
6 What waits on someone is visible In part. Every settled send appends a line to the project's history and team feed shows it. No who-waits-on-whom view yet (team waits is not built); the watch's in-team nudge stands.
7 You are told where you are In part. On the machine only, by a desktop notification for what a team's watch reports. Owner channels are not built.
8 You can answer from there No. A notification cannot be answered. Pending Charter round 2 for what an answer from a device may agree to.
9 All of it is yours to set In part. The delivery order is a policy with a fallback and a drop-or-reorder rule; seats crossing teams is a declaration, default off; some watch reports and notifications can be switched off, and its timing set. Who-may-talk-to-whom, quiet hours, summaries, routing and retention cannot.
10 One module, every channel Not built at v0.5.0. No envelope, no inbox and no router; one front serves a whole team, and each way in is a path of its own.
11 Module, seat and nickname are three things Not built at v0.5.0. No communication role, no adapter, and no router; a team's channels are worked by hand.
12 The address is the seat; a name travels as name@team In part. The forms resolve at the tag; the address is resolved when it is sent, not when it is delivered, and nothing delivers to the agent on watch.
13 TeamCLI runs each channel's whole lifecycle Not built at v0.5.0. The front's settings are edited by hand; no per-client declaration, check matrix, supervision, deploy record or rollback.
14 A message is kept; one that never arrived is not forgotten In part, by size only. The history rotates at 1 MB keeping three files; a read receipt is not aged; nothing keeps 90 days, no retention setting exists, and no daily report names an undelivered message.
15 An outside agent never instructs a leading seat by being named In part, by default only. The rule that reaches across teams is a lead pair within one entity, subject to the chain's setting; project-level pairs and owner-signed pairs have their own rules; different entities are refused on the lead-pair rule. No rule treats a leading seat specially, and the case cannot arise before such a pair is admitted.
16 An undelivered push is not silent Not built at v0.5.0. No push to an outside agent is made (mcp-inbox is not in this build), and no clock names one.
17 One signed record per message Built at int/0.5.1, unreleased. team send writes the signed record with both agent ids, the rule and the thread fields.
18 The id is the record's name Built at int/0.5.1, unreleased. The filename is the id.
19 Status is derived, never edited Built at int/0.5.1, unreleased. The receipt moves the status forward only; the attempt outcomes sit on the records, not on the status.
20 A thread is the first message's id Built at int/0.5.1, unreleased. team reply <id> [<text>] [--close] writes on the thread, admitted while it is open.
21 A question is a kind, an answer another Built at int/0.5.1, unreleased for the close; the question and answer kinds and thread-ttl are as the build notes say.
22 The owner's messages are records too Built at int/0.5.1, unreleased in the bus's owner delivery.
23 An event is a signed record in the event log Built at int/0.5.1, unreleased. The bus appends signed event records with sequence numbers.
24 The bus is a service of its own Built at int/0.5.1, unreleased. team bus over .agents/bus.sock, with gap, resync and the reconcile pass; only the owner runs it, from outside the pane manager.
25 The list, with default recipients Built at int/0.5.1, unreleased as a closed table, with five kinds beyond 002 amendment 01's and the prompt kinds.
26 Routing is a policy Built at int/0.5.1, unreleased. event-routing, the privileged kinds, and the named privileged-self-subscription refusal.
27 The owner is reached by the Runtime, never through a seat Built at int/0.5.1, unreleased in the bus's owner delivery.
28 The MCP inbox Built at int/0.5.1, unreleased; the leg takes a send only while the seat's inbox server listens, and is skipped as unavailable otherwise.
29 The drop leg Built at int/0.5.1, unreleased as a fourth leg; the record written whole, nothing typed.
30 No typing into a human's pane Built at int/0.5.1, unreleased in the order's own note; the order runs mcp-inbox, drop, then typed.
31 Typed is the fallback; native stays the sender's own As stated at int/0.5.1, unreleased.
32 The one-step send Built at int/0.5.1, unreleased. The record is written whole, and the reconcile pass raises events for records that have none.
33 Failure and retry Built at int/0.5.1, unreleased in the bus's seats and reconcile passes; the retry pacing is the build's tuning.
34 Authority, admission and what an event is not In part at int/0.5.1, unreleased. The reply is admitted on the open thread and the inbox verifies and shows a record; the material-versus-instruction mark is not built (commitment 5's Today).

7. The security boundary

The signature is integrity, not authenticity. Every seat runs as the owner-user, so a signature proves the record was not rewritten after it was signed; it does not say which seat sent it. from is a name the writer put on the record. A receipt says a process was shown the body, not that the recipient acted on it. And the mailbox is not a wall between seats: any seat in the checkout can read every seat's folder. Knowing who is speaking (commitment 4) needs what the Kernel builds: keys held apart and agents contained. What team send writes is not signed at all (at v0.5.0; signed under the checkout key at int/0.5.1, commitment 17): a drop file is protected by its mode and a per-seat lock, and the id on its first line is as strong as the sender's honesty until seats are contained. At int/0.5.1 the signature gives the record integrity — it was not rewritten after it was signed — not sender authenticity, because every seat holds the checkout key. The same-user forgery limit stands; a signature does not establish which seat authored a record.

The same-user limit is the Kernel's (§14), stated once there: every gate is an integrity control until the verifier's files are contained.

A receipt is read into the seat state the Runtime keeps (Runtime commitment 8): a seat whose state is unknown can confirm nothing, and a dispatch counts as taken only on the seat's receipt, never on a delivery outcome (010 Workflow, commitment 13).

Typing text into another agent's pane is a last resort only, beyond the two fixed lines that are allowed to be typed: the watch's ring that a message waits, and team send's wake line, measured; a trigger longer than the measured line is refused, never cut. team send is the only path by which one agent types into another seat's pane; a pane whose composer already holds unsent text is never typed into at all (the send is held, said, and the message stays on disk until a person clears the box). Two policies of the Kernel's Policies section govern any key sent into another agent's pane: pane-authority, enforced through team answer — a key or option is sent only by the pane's lead, a seat above it in the chain of command, or on the owner's word for that case; trust, permission, login and security prompts are answered by the owner only, or by a lead on the owner's word for that specific case; never a human's pane unless that human asked — and pane-conduct, advised: reading another agent's screen is allowed within the entity; before acting, read the screen; act only on the exact prompt expected; one key or one option, never free text; log every action with the pane, the prompt seen and the key sent. Raw keys are a logged fallback at the end of the delivery order, never a path of their own.

8. Where it sits, and what it leaves to others

  • The Kernel: who a sender is, and that it cannot present itself as another (§11, §15); who may instruct a seat, and the refusal of every other channel as a source of instructions (§11); what makes an answer from a device as strong as one at the machine, and whether a yes was witnessed or reported (§10, §11); what each principal may do, and that a device or a message gives nobody a right they do not hold (§9); that agree, grant, create and leads are never copied (§3, §10); the obligation that the owner is told when what a team may do changes (§11); the record ledger (§15); and, through the Kernel's Policies section, the policies this module's settings become where they gate.
  • The Runtime: the seat registry, which says who an agent is; the seat state (working, idle, done, blocked, held, unreachable, unknown); the shared record store this module writes into; the wake line, its delivery primitive; the adapters of each leg; the timers that say something has waited too long; delivering the owner's notices.
  • 010 Workflow: when a task travels and to whom, the round, and that a task is taken only on the seat's receipt.
  • The Memory module: what a team keeps from one day to the next. A message is a record of what was said; what is remembered from it is not this module's.
  • Internal design records, written before anything is offered: how an agent is given its way to reach TeamCLI, what stands between the machine and a phone and who runs it, and the send daemon.
  • The order of this work (an owner decision): first, restore the Grok Bot line and route it through registry ids; then the envelope and the router, with the MCP read-only tools; then one channel adapter at a time, in the owner's order: Grok Bot, the Claude app, ChatGPT, then the phone number.

9. Prior art and alternatives

The relay this module replaced: typing text into a pane, pressing Enter, and reading the pane back — which left tasks sitting unsent, and was refused as the design (§3). The socket leg of the interim build was measured and reversed: it reached Claude Code seats in about 250 ms but every message sat in the receiver's held-message dialog, so delivery went back to typed and the delivery-order policy was decided (2026-10-17); an unattested socket is not among the legs. TeamCLI never attests a permission mode on a seat's behalf: a CLI process cannot truthfully say which mode it runs in, and attesting anyway would be a false statement to another tool's gate, so it is not an option, configurable or otherwise (the owner, 2026-10-17).

10. Open questions

From the accepted text:

  1. Kinds of message. Is the list in commitment 2 right? What kind is missing; which two are the same?
  2. Read is not understood. Should an agent that receives a task say back what it understood before it acts (as the Runtime asks at a handover), or is that ceremony for ordinary work?
  3. Who may talk to whom, by default. What should a new team start with? And for a team with nobody leading it? (Partly settled: the project level's pairs, built at v0.5.0; the rest is the per-team setting.)
  4. Agents of a team among themselves. Useful, or the start of agents steering each other away from what you asked? (Section 2 proposes an answer; the question stays open until reviewers agree.)
  5. Settled by an owner decision: the two stand — a change to what a team may do, and a thing waiting on the owner — and a third is added: a budget floor, or a security refusal.
  6. Settled by an owner decision: safe from a watch are yes, no, pause, stop and later; a grant, a revoke, a publish, a payment and a removal must be read in full and read back first.
  7. Settled by an owner decision: naming is enough for material status only; instructing authority needs a grant (the Kernel's Grants section), never a name alone.
  8. Two agents answering each other without end. What should stop it: a count, a cost, whoever is above?
  9. Settled by an owner decision: messages and the read trace are kept 90 days by default, a setting per entity; an undelivered message is kept until it is delivered or the retention ends, and is named in the daily report after 24 hours.

From External agents:

  1. Settled by an owner decision: no; an outside agent reaches a leading seat through its own team's lead, or through an owner's grant, never by being named.
  2. Settled by an owner decision: the owner is told after one hour by day and at the next morning report in quiet hours; a setting.

Raised by the re-lay:

  1. Who may talk to whom: setting or refusal? If TeamCLI declines to deliver a message the setting does not allow, that is a refusal, and a module may not add one (Kernel §13). Decided for this release line (an owner decision): admission is a setting; the expected answer for the amendment is a policy of the enforced kind whose gate is the Kernel's; the round confirms.
  2. team send's authority. The admit rules are a fixed list in the code. If they are enforced, which Kernel capability backs them, given that the vocabulary is closed and has none for sending? For this release line, none: admission is a setting (an owner decision); the Kernel question stays for the round.
  3. team send and peer channels. If a worker cannot send to another seat, how does a peer question of the hybrid model travel? A separate kind of message that can never be an instruction, a looser rule for questions, or no peer channels until the Kernel says?
  4. Settled. A team with no lead: Kernel §9 allows zero leads, and then the owner is the team-level address; identity.leads resolves to nobody (009, question 9). Peer records and safeguard 3's "back up to the lead" go to the owner.
  5. Settled by 010 Workflow. A task travels by pull from the backlog (team next, commitment 15); team send carries questions, answers and wake-ups.
  6. Settled by 010 Workflow, commitment 13. "Received" is the receipt: a task is taken only on the seat's receipt, and a delivery outcome is never one.
  7. For the build: the mailbox is not a wall — team messages shows every waiting message in the checkout to whoever runs it. Once the registry can say who is asking, should it show only the caller's?
  8. For the build: signing before containment — until the Kernel's identity and containment exist, a message signature says nothing about who wrote it. Is it worth showing a signed from at all, or should the record show it marked as the writer's claim until then?
  9. The receiver-side hold. Is a hold on the receiver's side its own outcome (held, side named, as this text says) or a distinct one, and after how long is the owner told of a message held there?

To carry at the round: the channel lifecycle's formats — one client's declaration (its authentication and its Origin), the per-client check matrix, and the deploy-and-rollback record — wait for a channel-lifecycle spec in a later revision; until then an owner decision governs them, and no spec holds them yet.

12. Links to the spec

  • Specs-messaging §3: the message record, the receipt, the drop file, the history line, the route record, and the envelope (planned).
  • Specs-messaging §4: team send, team messages, team feed, team route — synopses, options, outcomes and exit codes.
  • Specs-messaging §5: the address grammar, the addressing rows (planned), the admission rules, the delivery-order rules, the typing rules.
  • Specs-events §3: the event record, the status words, the kinds table (commitments 17–26).
  • Specs-events §4: team bus, team reply, the inbox and the delivery legs (commitments 24–32).
  • Specs-events §5: the routing policy's form, gap and resync, the reconcile pass, the retry and admission rules (commitments 26–34).
  • Registries this text contributes to: delivery-legs (native, mcp-inbox, drop, typed), exit-codes (the send.*, messages.*, feed.*, route.*, bus.*, reply.* rows), policy-names (delivery-order, pane-authority, pane-conduct, event-routing, privileged-kinds), event-kinds (the table of events.md §3).

Clone this wiki locally