Spell the driver kind the way Rust and RON do

`kind: r#claude-cli` was the config file paying for a serde default. RON is
modelled on Rust, a hyphen is not an identifier in either, and this file is
edited by hand -- so the escape existed only to write a name nobody would
have typed that way. Snake case, and it reads `kind: claude_cli`.

The same string is the one the phone compares against to decide whether to
offer models, a working directory and permission modes, so SpawnScreen.kt
moves with it. That is a wire-format change: Dev Updater delivers the
server before the APK, so between the two an installed build sees a kind it
does not recognise and drops the Claude-specific fields from the spawn
form until the APK lands. It recovers on its own, and nothing else reads
the value.

The provider *name* is left as "claude-cli". It is a label a person picks
and may edit, and sessions reference providers by it -- renaming the
default would orphan them.

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 03:41:13 -04:00
1 parent 7a02e79613
commit 585e4a0369
2 files changed
+7 -8

No files matched your search

@@ -86,7 +86,7 @@ fun SpawnScreen(
// Only the Claude CLI has models, a working directory, and permission // Only the Claude CLI has models, a working directory, and permission
// modes; keying the extra fields on the kind rather than the provider // modes; keying the extra fields on the kind rather than the provider
// name keeps a second Claude provider from needing anything here. // name keeps a second Claude provider from needing anything here.
val isClaude = current?.kind == "claude-cli" val isClaude = current?.kind == "claude_cli"
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp)) { Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp)) {
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) { Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
+6 -7
View File
@@ -143,14 +143,13 @@ pub struct HostConfig {
/// Which translator runs a session. A new one is a new driver behind the /// Which translator runs a session. A new one is a new driver behind the
/// same trait -- never a branch in shared code. /// same trait -- never a branch in shared code.
/// ///
/// The kebab-case spelling is the one the phone compares against /// Snake case, which is both Rust's and RON's: this is written into a
/// (`SpawnScreen.kt`), so it is the HTTP surface's, not a formatting /// config a person edits by hand, and a hyphen is not a RON identifier, so
/// choice. The cost lands on the config file, where a hyphen is not an /// kebab case cost the file a `kind: r#claude-cli` escape to say a name
/// identifier: RON writes and reads it as `kind: r#claude-cli`. Left that /// nobody would type that way. The same string is what the phone compares
/// way rather than renaming the variant, because the string is a contract /// against (`SpawnScreen.kt`), so the two move together.
/// with whatever build is installed on the phone and the file is not.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)] #[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "kebab-case")] #[serde(rename_all = "snake_case")]
pub enum DriverKind { pub enum DriverKind {
/// The phase-1 fake: echoes messages back as streamed events. Proves /// The phase-1 fake: echoes messages back as streamed events. Proves
/// the pipe (spawn, SSE, transcript cursors, questions) with no AI /// the pipe (spawn, SSE, transcript cursors, questions) with no AI