Record the setups model, and put the transport above the drivers

Two decisions from Bryan today, written down before they are built, since
this file is where the reasoning is supposed to live rather than in a
conversation.

A setup is a machine carrying the providers that machine has, and spawning
picks a setup then one of its providers. The independent providers × hosts
model it replaces is left in place below it, because its reasoning is worth
keeping and the code still implements it. What that model got wrong is that
the axes are not independent: it offers combinations that cannot work, and
"Run on" is already a control that does nothing for the echo driver, which
takes no host at all.

The transport wraps the driver rather than the driver reaching for the
transport. ClaudeDriver::spawn calls ssh::command itself today, which puts
transport knowledge inside a translator whose job is a wire format, and
obliges every future driver to remember the same. Inverted, a driver that
emits no command has nothing to wrap, which is the same "Run on" problem
solved structurally instead of by a special case.

Also noted: llama.cpp is the case where "wrap a command" is not enough on
its own, since a managed llama-server is spawned but then spoken to over
HTTP, so a transport is "run this" plus "reach this port". And this
section's claim that remote attachments need scp was never true of the
code -- images are base64 inside the stream-json message in both
directions, so nothing has to exist on the remote filesystem.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_017xn8nHw1tw1R6PtiY1eEtw
This commit is contained in:
irisandClaude Opus 5 committed 2026-08-28 04:30:50 -04:00
1 parent 8bf99f4b76
commit ba6c770be3
1 file changed
+49 -3
+49 -3
View File
@@ -44,7 +44,35 @@ Decisions already made (2026-08-24):
## Architecture ## Architecture
### Providers and hosts (decided 2026-08-25) ### Setups and providers (decided 2026-08-28, superseding the below)
**A setup is a machine, and it carries the providers that machine has.**
Optional ssh details, plus the list of what can be run there. Spawning is
then two choices in order: pick a setup, then pick one of its providers.
This replaces the independent providers × hosts model recorded below,
which is what the code does today. What went wrong with it: the two axes
are not actually independent. A provider is only real on a machine where
that CLI is installed, so a free cross-product offers combinations that
cannot work — `claude-cli` on a machine with no `claude`, and every
provider paired with a host the driver ignores entirely (`EchoDriver`
takes no host, so "Run on" is a control that silently does nothing for
it). Grouping providers under the machine they exist on makes the picker
show only what is true.
Open, and worth settling before this is built:
- **Where the built-in echo provider lives.** It needs no configuration
and is the connectivity check that costs no tokens, so probably a
provider of an implicit local setup rather than something configured.
- **Migration.** Sessions store the provider and host names they were
spawned with; they would store a setup and a provider instead.
- **Setups are edited from the phone**, not by hand in `config.ron` — the
standing preference for this app. Key material is the exception that
cannot travel, so a setup names an identity file that must already exist
on the backend machine.
The superseded model, for the reasoning it recorded:
Two independent axes, configured separately and chosen per session: Two independent axes, configured separately and chosen per session:
@@ -225,8 +253,26 @@ host) and **hosts**. The manager runs at most one llama-server per
`ssh -T host …`. Process death ≙ connection death; the session shows as `ssh -T host …`. Process death ≙ connection death; the session shows as
`exited` and both dialects resume (`--resume` / pi session file) on respawn, `exited` and both dialects resume (`--resume` / pi session file) on respawn,
so a dropped SSH connection is an annoyance, not data loss. so a dropped SSH connection is an annoyance, not data loss.
- Images and attachments for remote sessions are written to the remote - **The transport wraps the driver, not the other way round** (decided
session dir via `scp`/stdin before the message referencing them is sent. 2026-08-28). A driver says what to run — program, arguments, working
directory — and something above it turns that into a process, locally or
through ssh. Today `ClaudeDriver::spawn` calls `ssh::command` itself,
which puts transport knowledge inside a translator whose job is a wire
format, and means every future driver has to remember to do the same.
Inverting it also removes the "Run on" lie for free: a driver that emits
no command, like the echo one, has nothing for a transport to wrap, and
the picker can say so.
- The interface that inversion needs is **not just "run a command"**, and
llama.cpp is the case that shows it: a managed `llama-server` is started
as a process but then spoken to over HTTP, so a remote one needs a
forwarded port (`ssh -L`) as well as a spawned process. A transport is
therefore "run this" plus "reach this port", and the second operation is
a no-op locally.
- Attachments need no file transfer, contrary to what this section said
before: `attachment_block` base64s an uploaded image into the
stream-json message itself, and produced images come back the same way
for the translator to write out locally. Nothing has to exist on the
remote filesystem, so there is no `scp` step to get wrong.
### Usage limits (Claude) ### Usage limits (Claude)