Discover this machine's providers instead of asserting them

A fresh install wrote a `claude-cli` provider into the local setup
unconditionally. Nothing looked for `claude`; the list was hardcoded in
`Config::seed`, so on any machine without it -- which is every machine but
the dev VM -- the phone was offered a provider that cannot spawn, stated
with exactly the confidence of one that had been checked.

Discovery already existed and was already right: `setups::discover` probes
with `command -v` over the transport, includes echo for the local one
because it runs in-process, and records the resolved path rather than the
bare name. Only the local setup skipped it, which is the one place the
answer felt obvious enough not to ask.

So `seed` now takes the providers it is given, and seeding asks this
machine the same question it asks any other. It moved out of
`SessionManager::new` into an awaited step in main, because asking is I/O
and a constructor that quietly spawns a subprocess surprises every caller.
A discovery that fails seeds `echo` alone and says so, since echo is true
wherever this server runs -- falling back to the hardcoded list would be
the same bug with an extra step.

The test that covered this agreed with the bug, because both were written
from the same assumption: it asserted the seed contains `claude-cli`. It
now asserts the opposite -- that the seed invents nothing -- and the
session tests seed echo explicitly rather than relying on a constructor
that would make them pass or fail on whether `claude` happens to be
installed on whoever runs them.

Verified by running a server on a PATH holding only `sh`: it seeds `echo`
alone. With claude and llama-server present it finds both. 34 tests.
This commit is contained in:
iris committed 2026-08-28 20:15:44 -04:00
1 parent 3cf6925d90
commit 4370c467ca
3 files changed
+126 -44

No files matched your search

+65 -32
View File
@@ -212,34 +212,36 @@ impl Config {
self.setups.iter().find(|setup| setup.name == name)
}
/// What a fresh install starts with: this machine, offering the echo
/// driver to prove the pipe and the Claude CLI to be useful.
/// This machine, offering whatever was found on it.
///
/// Echo runs in-process, so it belongs to the setup with no ssh --
/// there is nothing for a transport to wrap, and offering it on a
/// remote machine would be a choice that changes nothing.
pub fn seed() -> SetupConfig {
/// The providers are passed in rather than written here because they
/// have to be *discovered*: a hardcoded list is a claim about what is
/// installed, and this one was wrong -- every fresh install asserted a
/// `claude-cli` provider whether or not `claude` existed, which on a
/// machine without it is a spawn option that cannot work and a
/// statement the server never checked. Providers are discovered by
/// asking the machine, here exactly as for any other setup.
pub fn seed(providers: Vec<ProviderConfig>) -> SetupConfig {
SetupConfig {
id: LOCAL_SETUP_ID.to_string(),
name: LOCAL_SETUP.to_string(),
ssh: None,
providers: vec![
ProviderConfig {
name: ECHO_PROVIDER.to_string(),
kind: DriverKind::Echo,
command: None,
models: Vec::new(),
},
ProviderConfig {
name: "claude-cli".to_string(),
kind: DriverKind::ClaudeCli,
command: None,
models: ["fable", "opus", "sonnet", "haiku"]
.iter()
.map(|m| (*m).to_string())
.collect(),
},
],
providers,
}
}
/// The one provider that needs no discovery, and the floor to fall
/// back to when discovery itself fails.
///
/// Echo runs in-process, so it exists exactly where this server does
/// and nowhere else -- there is nothing to probe for, and offering it
/// on a remote machine would be a choice that changes nothing.
pub fn echo_provider() -> ProviderConfig {
ProviderConfig {
name: ECHO_PROVIDER.to_string(),
kind: DriverKind::Echo,
command: None,
models: Vec::new(),
}
}
@@ -315,7 +317,15 @@ mod tests {
sha256: "ab".repeat(32),
}],
setups: vec![
Config::seed(),
Config::seed(vec![
Config::echo_provider(),
ProviderConfig {
name: "claude-cli".to_string(),
kind: DriverKind::ClaudeCli,
command: Some("/usr/bin/claude".to_string()),
models: Vec::new(),
},
]),
SetupConfig {
id: "vm".to_string(),
name: "the vm".to_string(),
@@ -395,21 +405,44 @@ mod tests {
}
#[test]
/// What a fresh install can do before anybody configures anything:
/// echo to prove the pipe, and the Claude CLI to be useful. Both are
/// on the machine the server runs on, and echo belongs there because
/// it runs in-process -- there is no transport for it to cross.
fn the_seed_setup_is_this_machine_and_can_spawn_something() {
let seed = Config::seed();
/// The seed is this machine and nothing more: a name, no ssh, and
/// exactly the providers it was handed.
///
/// It used to assert a `claude-cli` provider here, which is what made
/// the bug look correct -- the test agreed with the code that every
/// machine has `claude`, because both were written from the same
/// assumption. What a machine has is discovered, so the only thing
/// this can check is that the seed does not invent anything.
fn the_seed_is_this_machine_and_claims_only_what_it_was_given() {
let seed = Config::seed(vec![Config::echo_provider()]);
assert_eq!(seed.name, LOCAL_SETUP);
assert!(seed.ssh.is_none());
assert_eq!(
seed.provider(ECHO_PROVIDER).expect("echo").kind,
DriverKind::Echo
);
assert!(
seed.provider("claude-cli").is_none(),
"the seed must not assert a provider nobody looked for",
);
// And it carries through whatever discovery did find.
let discovered = Config::seed(vec![
Config::echo_provider(),
ProviderConfig {
name: "claude-cli".to_string(),
kind: DriverKind::ClaudeCli,
command: Some("/usr/bin/claude".to_string()),
models: Vec::new(),
},
]);
assert_eq!(
seed.provider("claude-cli").expect("claude").kind,
DriverKind::ClaudeCli,
discovered
.provider("claude-cli")
.expect("found")
.command
.as_deref(),
Some("/usr/bin/claude"),
);
}
}