The companion seat as a reusable capability#
2026-10-06 ยท Opus 5.5 coordinator, two Opus 5.5 lanes (code reality, academic grounding) ยท jgwill/Miadi#714 (feature) under jgwill/Miadi#591 (relational software factory inquiry)
Question: how does "open a companion seat for an episode" become something other pages embed, other components call, and people outside install and run in their own factory, with publish/subscribe thinking underneath?
Current reality#
Verified by the coordinator in the same turn unless marked.
- F1. The opener is page code.
components/tide/companion-opener.tsxtakes no props, hard-codes/api/tide/companion, says "Mia" in its labels, and links to/tide?mux=tmux. The plan type exists three times: the opener,packages/chronicle-tui/src/tide.ts:21-51, and tide'scompanion.py:319-328. - F2. Two callers already exist. The
/tideopener and@miadi/chronicle-tui0.8.0 (packages/chronicle-tui/src/tide.ts:63posts to the same route). - F3. The repo already has a reuse shape, and the companion does not follow it. annotate, episode, attention, capture and voice each split into a core with no network, a client or contract with the base URL and credential passed in, a React UI fed by an injected transport or callbacks, and sometimes a service bin. gmtermux consumes only the cores and browser builds.
- F4. Only this checkout can run the route.
@miadi/tide0.6.1 on npm has noopenCompanion(lane:npm pack+ grep; coordinator:npm view= 0.6.1). - F5. An outside adopter hits private or unversioned links. The launcher chain
miacompanionyoloโclaudeyololives in private jgwill/binscripts (/opt/binscripts/etc/bash_aliases_common:7274). The presence file<folder>.listening.json, written by the kit'smia-listen.mjsand read by tide'scompanion.py, is a contract between two repos with no schema. One Unix user must own the web server, the tmux server and~/.claude/sessions. - F6. Nothing publishes seat transitions. The opener polls every 5 s because of it.
The one live event producer is
@miadi/voice(Redis streamassembly:voice:eventsand channelassembly-voice-events).@miadi/hooks-gatewayhasPOST /eventsspecified, not running. ep548 holds NATS and Kafka reviews and records no broker choice. - F7. Grounding is split across places.
foundations/bounded-contexts-and-integration-contractscovers contracts and outside adopters (Parnas, Meyer, open host service, boundary resources). The ep339 vessel holdsfoundations/companion-seat-opening, which grounds the seat's behaviour (reconciler, mixed initiative, relational continuity), not its reuse. Missing: product lines, component-based software engineering, service orientation, ports and adapters, software ecosystems, and primary sources for publish/subscribe. - F8. The TTY gate is off for every tide call from the app. The
miadi-serveruser unit setsTIDE_ALLOW_NON_INTERACTIVE_RUN=1at line 16, while@miadi/tidealready sets it per call for companion runs (client.ts:488).
What the fields say about this case (grounding lane, sources in GROUNDING.md)#
- Product lines (Parnas 1976, Pohl et al. 2005): name what is common (episode, plan, seat) and what varies (seat and its plugin, agent provider, host, archive policy, the page that embeds it), and give each variation point a binding time: build, config, call.
- Components (Szyperski 2002): "contractually specified interfaces and explicit context dependencies only". F1 and F5 are the implicit dependencies that keep it page code.
- Contract levels (Beugnard et al. 1999): plan-then-open is behavioural, one listener per episode is synchronisation, a minutes-long open behind a proxy is quality of service. Today all three live in code comments.
- Ports and adapters (Cockburn 2005): the core is "plan a seat, open a seat". React, the HTTP route, the CLI and chronicle-tui drive it. tmux and the agent provider are driven by it. The seat's plugin is an extension point (Birsan 2005).
- Publish/subscribe (Eugster et al. 2003; Fowler 2017; CloudEvents): define the event types and the envelope first and append them to a log. A broker chosen later is one more driven adapter.
- Ecosystems (Bosch 2010; Ghazawneh and Henfridsson 2013): outsiders need a package, a contract, documentation and a way to run without gaia's aliases.
Desired outcome#
The companion seat exists as one capability offered at three grains, with events underneath:
- Embedded control.
<CompanionOpener episode seat transport onOpened />mounts in any page that knows an episode./chronicle/[episode]is the first page after/tide, behind thecurrentMayWrite()gate that page already uses. - Callable service. One versioned
companioncontract (plan, open, the nine states, refusal codes, exit-to-status map) in@miadi/tide-contract. The Next route, chronicle-tui and the opener all use types generated from it. - Installable capability. Published packages plus a seat declaration (launcher command, plugin dirs, listener, session pattern) that an adopter writes as config, and a schema for the presence file. No step needs a private repo.
- Events.
seat.planned,seat.opened,seat.rearmed,seat.archived,seat.refusedin a CloudEvents envelope, each naming the person who asked, appended to a log by tide. The opener subscribes when a transport exists and polls until then.
Costs the lanes named#
- R1. Generality costs about nine times as much (Brooks 1995): making a program a product costs about three times as much, and making it a system component costs about three times as much again. Extract in order of callers: the contract now (two callers exist), the UI package when the third caller lands, the outsider install last.
- R2. An event that opens a seat is a confused deputy (Hardy 1988; Garlan and Shaw 1993). A seat starts an agent with permissions bypassed. Events report transitions. A command event, if one ever exists, carries the person and their consent.
- R3. F8 widens the blast radius of every tide call from :3335. Whether any other tide call relies on the unit-wide flag is unverified.
Prediction against what the lanes found#
Committed before dispatch (1f0103b1, PREDICTION.md).
| predicted | found | |
|---|---|---|
| P1 core/ui split precedent | yes | yes, plus a client and service grain (capture) |
| P2 /chronicle/[episode] next, route hard-coded | yes | yes, plus ceremony, circles, voice card, pde pages |
| P3 no event bus for seats | yes | yes, plus voice is the one live producer |
| P4 fields named | yes | yes, plus ecosystems |
| P5 extend bounded-contexts packet | wrong | lane proposes a new packet. Bounded-contexts is scoped to one domain projected into another product |
| P6 runtime chain is the adopter's hard part | yes | yes, plus the presence file as an unversioned contract |
Not predicted: chronicle-tui is already a second caller (F2), the published package lacks
openCompanion (F4), the TTY gate is off service-wide (F8), the confused-deputy risk (R2),
the ep339 companion-seat-opening packet (lane 1 found it, lane 2 did not look in vessels).