Design-shelf project — topic-subscription bus for CLI agents in tmux; sibling to tmux-tell (identity-addressed directed messaging).
Find a file
2026-07-12 19:32:03 +02:00
.gitignore Initial commit 2026-07-11 12:31:15 +02:00
README.md docs(readme): synthesize four cold-reads into design-shelf substrate (#2) 2026-07-12 19:32:03 +02:00

tmux-bus

Design-shelf project — no code yet. Captures the shape a topic-subscription bus for CLI agents in tmux would take, if we ever build it. Sibling to tmux-tell, which is stable and stays what it is.

This is not a proposal to replace tmux-tell. It becomes build-worthy only if genuine topic fan-out pressure appears repeatedly. Until then, the design shape is captured here; the substrate is not built.

Why this repo exists (and not code)

An operator's original naming proposal for what became tmux-tell was "tmux-bus." During the naming discussion, the crew objected on technical grounds: what was being built is not a bus. Point-to-point directed messaging — sender addresses a specific recipient via mailbox — was the accurate name for the mechanism. The project shipped as tmux-tell (see ADR-0017 for the canonical decision).

The insight that revived the bus framing (2026-07-11): the current tmux-tell architecture has a hidden isomorphism at the receive-worker shape. Each mailman polls its own mailbox (SELECT ... WHERE recipient=<me>). Structurally, mailmen are subscribers, each subscribed to exactly one topic (its own name). Registration and subscription look like the same primitive with the topic implicit and fixed.

But that isomorphism holds only at the worker shape. The bus-defining property — late-bound topic-match routing, where one publish fans out to N subscribers by subscription-match and publish-cardinality is decoupled from subscriber-count — is exactly what tmux-tell does not have. The sender writes the exact recipient (early-bind); there is no routing layer.

The honest reframe: tmux-tell today is the degenerate case of what a bus would be — topic ≡ identity, subscription-cardinality fixed at 1, no routing layer. The routing/matching layer is the actual new thing a bus adds, not a reframing of what already exists. This strengthens the tmux-tell not-a-bus decision rather than denting it.

If we ever want a real bus, this repo captures the shape it would take.

The design shape (if we ever build it)

Core primitive: topic subscription

  • An agent subscribes to one or more topics
  • A sender publishes to a topic (not to a recipient)
  • Mailmen deliver messages from the topics their agent subscribes to
  • No naming discipline forces the topic to match an agent name; identity topics are one convention among possible ones

The user-facing surface is a small verb set: publish --topic <t>, subscribe --name <me> --topic <t>, unsubscribe. Bare --name retains tmux-tell's addressed-identity affordance as a default identity-topic subscription — from the user's view there is no topic to choose in the identity case, which is why tmux-tell's own README correctly says "no topics."

What this cleans up vs tmux-tell

  • Registration and subscription unify into one primitive. Where tmux-tell today registers an agent and pairs that with an implicit identity topic, tmux-bus would express both through subscribe. The launch-time obscurity in the chamber-* wrappers gets a legible name.
  • Semantic topics compose naturally. An agent can subscribe to alice (its identity) plus pr-published or test-failed (event topics). It does not need to be N identities to receive messages on N subjects.
  • Explicitly authorized observer agents become tractable. A monitoring or curation agent can subscribe to a set of event topics without needing a privileged snoop path built alongside. See caveats in Where the load hides — an observer subscribing to a firehose is the single most expensive shape and would need a different delivery mode entirely.
  • Anonymous late-binding is available but not required. Senders can still name the topic explicitly (they usually will — you are routing to alice on purpose). Publish-and-forget for event-broadcast use cases is available without being the default mode.

What emphatically does NOT stay like tmux-tell

This is the section the original design-shelf under-weighted; folding Engineer's architect-lens cold-read.

The receive-worker shape looks similar, but the load-bearing substrate below it does not carry through:

  • Persistence is not the same schema. One publish must land as N deliveries — cardinality shifts 1:1 → 1:N. Either a fan-out row-insert per subscriber at publish time (with the atomicity discipline tmux-tell#573 teaches) or per-subscriber delivery cursors (a new schema shape). This is the deepest change; the current messages/recipient table shape does not survive.
  • Workers are per-subscriber, not per-subscription. A single serial pane must serialize deliveries from all topics its agent subscribes to; spawning one mailman per (agent, topic) would produce interleave hazards and multiplied poll load into the same pane. The invariant is one mailman per pane, polling the union of that agent's topics. That collapses to tmux-tell's worker model only in the degenerate 1-topic case.
  • Fan-in ordering into one serial pane is a new problem. An agent subscribed to N topics needs a starvation and ordering policy the identity-mailbox case never needed. A high-volume event topic can starve the directed-identity topic; the paste queue serializes late-arriving urgent messages behind bulk noise. Design pass required.
  • Subscription-set becomes live mutable state. subscribe and unsubscribe create a cache-invalidation surface on every mailman's poll query that the constant WHERE recipient=me does not have. Re-subscribe mid-flight must re-scope the poll.
  • Topic granularity is stated-but-unenforced. The design principle says "topics must be fine-grained enough that subscription is a real choice." No mechanism in this design stops an all-events catch-all from being created and everyone subscribing "just in case." Existence-vs-enforcement gap; under real use, topic taxonomies drift coarse and reintroduce the fan-in amplifier this design claims to avoid.

Topics decide who should receive a message; the mailman still decides when and whether it is safe to deliver to that pane. Delivery safety (paste readiness, classifier false-idles, usage/rate limits, collapsed paste submission) is adapter and pane-state logic. Topics route; delivery scheduling is separate. This boundary is important enough to state as a load-bearing invariant: topic taxonomy does not become safety policy.

Delivery layer: unchanged from tmux-tell

  • SQLite-backed persistence for the mailbox tables
  • Paste-and-enter tmux delivery as the default mode
  • Mailbox-only mode for agents without a tmux pane
  • Rate-limit handling — provider-specific throttle/avoidance logic, delivery-layer concern. See tmux-tell#580.

What is explicitly NOT the answer

  • Fine-grained topics as a rate-limit solution. Rate limits are provider-specific and belong in the delivery layer, not the topic taxonomy.
  • Global broadcast topics without opt-in. No all-events default subscription; topics must be fine-grained enough that subscription is a real choice, not a fan-out amplifier. See the enforcement gap above.
  • A distributed / networked bus. Same locality envelope as tmux-tell — one tmux server, one SQLite file. Cross-host coordination is out of scope.
  • A retrofit of tmux-tell. tmux-tell is stable and stays as directed messaging. If tmux-bus ever ships, it is a sibling project with a different primitive.

Use cases

Ranked honestly by whether they justify building.

Load-bearing (would justify building, if repeated pressure appears)

  • Semantic event feeds. Agents publishing pr-merged, release-cut, test-failed; other agents subscribing to what is relevant to them. This is the most genuinely bus-shaped case: late-bound, cardinality-decoupled from the sender's knowledge of subscribers. If we build tmux-bus for a real reason, this is the first worked instance.

Real value but partially served today

  • Broadcast maintenance directives (operator-authored topics). An operator publishing to a maintenance topic that some subset of warm agents has subscribed to. Note: tmux-tell#158 already lets an operator send to an enumerated recipient list; the honest delta this design would add is self-selection (subscribers opt in; the operator does not have to enumerate the warm set). Not "broadcast" per se — and delivery is fan-out-then-throttle-serialized when subscribers share a provider pool, not atomic.

Ergonomics, not capability

  • Cleaner agent onboarding. New agent joins → subscribes to <its-name> and a small default set. The launch-time obscurity in chamber-* wrappers gets a legible name. This is a legibility improvement, not a capability gap; tmux-tell handles agent-joining today.

Overclaimed by first draft, honest read below

  • Observer agents (subscribing to *). tmux-tell today already provides raw observation: internal/cli/tail.go renders every from→to from the shared messages.db, and any user with filesystem access can sqlite3 the store. The bus value here is not observation-at-all — it is semantic filtering (subscribe to topic-subsets). And an observer subscribed to a firehose fans every system message into one serial pane's paste queue while degenerating the poll to a full scan; that shape needs a different delivery mode entirely (tail into a SQLite view, or a log sink), not paste-into-pane. If we add observers, they need their own delivery-mode primitive; do not layer them onto subscribe naively.

Relation to tmux-tell

tmux-tell is stable and stays as directed messaging — not a bus. ADR-0017 anchors that decision. If tmux-bus ever ships, it is a sibling project with a different primitive and its own substrate design pass. It is not a retrofit of tmux-tell; the receive-worker shape carries over, but persistence, worker cardinality, fan-in ordering, and subscription-set state all change.

Status

2026-07-11 — repo created as design-shelf; no code. Idea captured during an operator conversation reviewing tmux-tell's doc drift where the repo description had re-adopted "bus" framing. The doc drift is fixed on tmux-tell side (ADR-0017 + README rewrite + docs sweep via tmux-tell#744); this repo captures the reframe honestly. Four independent cold-reads from Lookout, Herald, Surveyor, and Engineer subsequently refined the design shape captured above — see the amendments log for what they caught.

Build gate: build-worthy only when a real use case surfaces that tmux-tell would strain to serve — most likely repeated semantic event fan-out where senders currently need to enumerate recipients, or an authorized observer primitive that current substrate does not cleanly express. Until then, the shape is captured; the code is not written.

Contributing

Design-shelf. No code contributions until the design pass opens. Issues capturing use-case pressure, shape-refinement ideas, or explicit "we should build this now" arguments are welcome.


Amendments log

2026-07-11 — Four independent cold-reads (Lookout, Herald, Surveyor, Engineer)

The initial README (2026-07-11 morning) framed the reframe insight as "identity IS a topic in the current design" and treated the receive-worker shape as evidence for that claim. Four independent cold-reads on separate substrate axes surfaced load-bearing corrections. All four compose without contradiction.

Surveyor (substrate-truth) — caught the core overclaim: "identity IS a topic" hides the missing routing layer. The isomorphism holds only at the receive-worker shape; the bus-defining property (late-bound topic-match routing, cardinality-decoupled fan-out) is exactly absent because the sender writes the exact recipient (early-bind, verified at store_test.go:330). Reframe applied: tmux-tell is the degenerate case (topic ≡ identity, subscription-cardinality 1, no routing layer); the routing/matching layer is the actual new thing a bus adds — not a reframing of what already exists. Also caught the observer overclaim (tail already exists; bus value is semantic filtering, not observation-at-all) and the broadcast overclaim (#158 already serves the enumerated case; honest delta is self-selection). "What is explicitly NOT the answer" section retained as substrate-honest.

Lookout (build-pressure lens, codex-side substrate) — surfaced the insight-vs-build-pressure separator: the shape captured here is real; the build case is not, yet. Explicit statement added near the top: "This is not a proposal to replace tmux-tell. It becomes build-worthy only if genuine topic fan-out pressure appears repeatedly." Also proposed the codex-boundary invariant that separates topic routing from delivery scheduling: "Topics decide who should receive a message; the mailman still decides when and whether it is safe to deliver to that pane." That line is folded verbatim; it preempts topic-taxonomy-as-safety-policy drift. Observer rephrased as an authorized primitive with named privacy/noise risk. Broadcast example (compact-all) removed as casual — replaced with "operator-authored maintenance directive topics" because compaction is safety-scoped and self-affecting. Semantic event feeds promoted to first-worked-instance for build justification. Onboarding demoted to legibility improvement.

Herald (register + composition + sibling-fit) — caught the vocabulary seam: tmux-tell uses "agent" throughout per its ADR-0005 substrate-neutral term; the original README defaulted to "chamber" (crew register). Substrate-sibling should share the substrate register. All uses of "chamber" replaced with "agent" and <chamber> placeholders replaced with <name> matching tmux-tell's own register --name shape. Insider residue removed: the Weird Al Yankovic aside — a pure in-joke a cold reader cannot resolve — is cut; "operator/crew" naming neutralized. Rate-limit passage trimmed to a one-line reference plus the tmux-tell#580 link so the design-shape section stays focused on the topic-subscription primitive. Half-sentence reconciliation added on the "no topics" seam: from the user's view there is no topic to choose in the identity case, which is why tmux-tell's own README correctly says "no topics." "See its ADR" cross-references swapped to explicit ADR-0017 name now that it has landed.

Engineer (architect lens) — the deepest catch, in the section titled What emphatically does NOT stay like tmux-tell. The original README filed persistence, worker cardinality, fan-in ordering, and subscription-set state under "stays like tmux-tell." That was wrong. 100% of the real substrate cost lives in that layer, and the primitive-shift (1:1 → 1:N cardinality, per-subscriber-not-per-subscription worker, fan-in ordering into one serial pane, live mutable subscription-set) has no tmux-tell precedent. Section rewritten with the four architectural gotchas named explicitly. Observer * subscription reframed as needing a different delivery mode entirely (tail SQLite view or log sink, not paste-into-pane). Broadcast semantics named honestly: fan-out-then-throttle-serialized when subscribers share a provider pool, not atomic. The "topics must be fine-grained" claim called out as stated-but-unenforced (existence-vs-enforcement gap) so a future reader is not misled about the design's protection against topic-taxonomy drift.

The four reads composed with zero contradictions across substrate-truth, build-pressure, register, and architecture. That composition — plus the codex-boundary line Lookout donated and the workers-per-pane invariant Engineer named — is the substrate this synthesis rewrites the shelf around.

Original design-shelf issue: tmux-bus#1 (Surveyor's verbatim write-up with at-source citations).