# Future: WATCH streams and agent-to-agent communication A future plan, not active work: a WATCH verb that holds a QUIC stream open and pushes change events, so agents coordinate through durable, versioned documents instead of an ephemeral message bus. Audience: demarkus maintainers deciding whether and when to reopen the verb set. Drafted 2026-09-15 from a session weighing QUIC's value against an HTTP binding. ## Why this is the QUIC-native feature Transport stays QUIC by design ([protocol](/architecture/protocol.md)). WATCH is where that choice pays: one stream per subscription, many subscriptions on one connection with no head-of-line blocking, cancel by stream reset, and connection migration keeps a laptop's subscriptions alive across network changes. The client opens the stream, so subscribers need no public address. That is the flaw in the WebSub-style `SUBSCRIBE` with a callback URL sketched in `docs/DESIGN.md`: agents are not servers, and [the backlog](/roadmap/backlog.md) records that design as removed. SPEC ยง13 still lists Subscriptions as a planned extension. ## The agent-to-agent idea Agents talk by writing documents and watching prefixes; demarkus is the blackboard. Every message is versioned, hash-chained, attributed, and readable later, so the conversation is the record. Everything except WATCH already exists: | Need | Existing mechanism | |---|---| | Send | PUBLISH `/agents//inbox/.md` | | Reply in a thread | APPEND to the message | | Claim a task exactly once | PUBLISH with expected version; the loser gets a version conflict | | Acknowledge or close | ARCHIVE | | Discover work | LOOKUP by tag | | Wake on arrival | **WATCH** (new) | LLM agents are turn-based and do not sit on a socket. Push helps only when a harness wakes the agent, so the first consumer is `demarkus watch` printing one line per event, run as a long-lived background command an agent harness monitors. ## Gates before starting 1. An ADR in `docs/adr/` (next number) supersedes "Verb Set: Complete", mirrored to `/adr/`. 2. Durable core fortification 1, the protocol version on the wire, has landed so WATCH ships as an additive extension after the 1.0 freeze ([fortifications](/plans/durable-core/fortifications.md)). 3. A concrete multi-agent workflow where polling latency measurably hurts. Otherwise long-poll on a change feed (option C) is enough and needs no new verb. 4. The client's `removeConn` evicts a shared connection without closing it; fix before any long-lived stream rides on it. 5. The feature freeze in [ecosystem optimization](/plans/ecosystem-optimization.md) is lifted, or a tool budget is carved out for any MCP surface. ## Wire shape Request: `WATCH /prefix/` with request metadata `since: ` (optional) and the usual token. An exact document path is allowed. Response: one frontmatter block with `status: ok` and the current `cursor`, then one frontmatter block per event, no body: `cursor`, `path`, `version`, `hash`, `op` (publish, append, archive). Events are hints; the client fetches by hash for content. Terminal blocks end the stream: `status: resync` (cursor unknown or too old), `status: unauthorized` (token revoked or expired), `status: closing` (server drain). Cursor is opaque to clients: `:`. Delivery is at least once and ordered within an epoch. On `resync` the client rebuilds with LIST and VERSIONS of the prefix and resumes from the returned cursor. ## Server design - **Change hub** (`server/internal/changefeed`, no store deps): publish, subscribe by prefix and cursor, an in-memory ring buffer for resume, a bounded queue per subscriber. A full queue ends that subscription with `resync`; the write path never blocks. - **File store:** emit after a committed WriteVersion, Append, or Archive. The epoch is random per process start, so a restart forces resync. - **Bucket store:** the head sequence is already durable and monotonic, so the epoch is the world ID and a cursor survives restarts. Replicas learn of other replicas' commits only when they read the head, so each world runs a head poller while it has subscribers, diffs the old and new snapshot per path, and emits in sequence order. Several commits between polls coalesce to the latest version per path. A replica emits its own commits immediately; the hub drops any sequence it has already emitted. - **Runtime** (`worldruntime.serveStream`): a WATCH currently holds a concurrency slot and a read deadline for its whole life. Release the slot once subscribed, exempt the stream from the request timeout, add per-world and per-connection watch limits separate from `maxConcurrentRequests`, and send `closing` on drain so the active wait group finishes. - **Auth:** subscribing requires `read` on the prefix. Each event is checked against the token's paths, since a prefix can be wider than a token's scope and existence must not leak. A token reload re-checks live subscriptions. ## Client and edge - `fetch.Client.Watch(ctx, host, prefix, token, since)` returns an event iterator, reconnects with the last cursor, and surfaces `resync` to the caller rather than hiding it. - `demarkus watch mark://host/prefix/` prints one tab-separated line per event. - Broker relay over SSE or MCP resource notifications only on demand. The broker would hold subscriptions per MCP session, a scaling cost the dumb-server principle pushes to the edge on purpose. - Agent mailbox conventions ship as a plugin skill, not in core. ## Phases 1. **Protocol.** ADR, SPEC section, verb constant, event codec, status values. Proof: codec round trip; a server without WATCH rejects it with an error status, never a hang. 2. **Single server.** Change hub, file store emit, runtime changes, client, CLI. Proof: two sessions on one local soul, one publishes to an inbox, the other's `demarkus watch` prints the event within 100 ms; kill and reconnect resumes without gaps. 3. **Knowledge server.** Head poller and snapshot diff. Proof: two replicas, a write through replica A reaches a watcher on replica B within one poll interval; a cursor older than the ring gets `resync`. 4. **Agent conventions.** Mailbox layout, claim by expected version, ack by archive, as a skill. Proof: two agents split a task list with no double claims. 5. **Broker relay.** Only after a named consumer asks for it. ## Risks - **Dumb-server drift.** The server now holds subscriptions. Kept small because events are hints, subscribers keep no durable server state, and overload degrades to resync. - **Store as a message bus.** Every message is a stored version. Chatty agents bloat history; guidance says documents are for durable coordination, and high-frequency chatter belongs elsewhere. - **Bucket poll cost.** Object HEAD requests per world per interval, paid only while subscribers exist. - **Silent misses.** A bug that drops an event looks like quiet. The cursor makes gaps detectable; tests assert contiguous sequences. ## Open questions - Should a subscription accept LOOKUP filter predicates (`tag=inbox`) so agents watch a subject rather than a path? - Should events carry changed section anchors, so a watcher fetches one section instead of the whole document? - Should the file store persist its sequence so cursors survive restarts, making the epoch stable? - Is `demarkus watch` plus a harness monitor enough to leave the broker relay out permanently?