diff --git a/desktop/src-tauri/src/managed_agents/discovery.rs b/desktop/src-tauri/src/managed_agents/discovery.rs
index 50206567ad..cb409786a4 100644
--- a/desktop/src-tauri/src/managed_agents/discovery.rs
+++ b/desktop/src-tauri/src/managed_agents/discovery.rs
@@ -111,7 +111,11 @@ const KNOWN_ACP_RUNTIMES: &[KnownAcpRuntime] = &[
         adapter_install_hint: "Install the Claude Code ACP adapter via npm.",
         skill_dir: Some(".claude/skills"),
         supports_acp_model_switching: false,
-        model_env_var: None,
+        // The Claude CLI honors this env var as a session model override,
+        // and the adapter's child `claude` process inherits the spawn env —
+        // without this, the model picked in the UI is persisted but never
+        // applied (#2692).
+        model_env_var: Some(crate::managed_agents::ANTHROPIC_MODEL_ENV_KEY),
         provider_env_var: None,
         provider_locked: true,
         default_env: &[],
diff --git a/desktop/src-tauri/src/managed_agents/discovery/tests.rs b/desktop/src-tauri/src/managed_agents/discovery/tests.rs
index 0ed4fe0f6a..e8a5fa732b 100644
--- a/desktop/src-tauri/src/managed_agents/discovery/tests.rs
+++ b/desktop/src-tauri/src/managed_agents/discovery/tests.rs
@@ -1270,3 +1270,25 @@ fn test_install_shell_from_some_returns_path() {
         "install_shell_from(Some) must return the path as Ok"
     );
 }
+
+/// The claude runtime must declare a model env channel — without one, the
+/// model picked in the UI is persisted but never applied at spawn (#2692).
+/// ANTHROPIC_MODEL is the override the Claude CLI honors, and the same key
+/// readiness uses for the anthropic provider.
+#[test]
+fn claude_runtime_declares_anthropic_model_env_var() {
+    let claude = super::known_acp_runtime_exact("claude").expect("claude runtime registered");
+    assert_eq!(claude.model_env_var, Some("ANTHROPIC_MODEL"));
+    // Provider stays locked: only the model is injectable for this harness.
+    assert!(claude.provider_locked);
+    assert_eq!(claude.provider_env_var, None);
+}
+
+/// A stale persisted ANTHROPIC_MODEL must not shadow the structured model
+/// field after a UI edit — same treatment as GOOSE_MODEL / BUZZ_AGENT_MODEL.
+#[test]
+fn anthropic_model_is_a_derived_env_key() {
+    use crate::managed_agents::is_derived_provider_model_key;
+    assert!(is_derived_provider_model_key("ANTHROPIC_MODEL"));
+    assert!(is_derived_provider_model_key("anthropic_model"));
+}
diff --git a/desktop/src-tauri/src/managed_agents/env_vars.rs b/desktop/src-tauri/src/managed_agents/env_vars.rs
index 6318a5c838..3e41eddc6a 100644
--- a/desktop/src-tauri/src/managed_agents/env_vars.rs
+++ b/desktop/src-tauri/src/managed_agents/env_vars.rs
@@ -24,11 +24,18 @@ use std::collections::BTreeMap;
 ///
 /// Non-structured knobs (`GOOSE_TEMPERATURE`, `GOOSE_CONTEXT_LIMIT`) are NOT
 /// in this list — they have no structured counterpart and must be preserved.
+/// Env var the Claude CLI honors as a session model override. Single source
+/// of truth for the key — referenced by the claude runtime entry
+/// (`discovery.rs`), the derived-key list below, and the readiness
+/// provider-model mapping, so the three cannot drift apart.
+pub(crate) const ANTHROPIC_MODEL_ENV_KEY: &str = "ANTHROPIC_MODEL";
+
 pub(crate) const DERIVED_PROVIDER_MODEL_ENV_KEYS: &[&str] = &[
     "GOOSE_MODEL",
     "GOOSE_PROVIDER",
     "BUZZ_AGENT_MODEL",
     "BUZZ_AGENT_PROVIDER",
+    ANTHROPIC_MODEL_ENV_KEY,
 ];
 
 /// Returns `true` if `key` is a derived provider/model env key that should be
diff --git a/desktop/src-tauri/src/managed_agents/readiness.rs b/desktop/src-tauri/src/managed_agents/readiness.rs
index 87ee6241ee..3fe689e422 100644
--- a/desktop/src-tauri/src/managed_agents/readiness.rs
+++ b/desktop/src-tauri/src/managed_agents/readiness.rs
@@ -327,7 +327,7 @@ fn buzz_agent_requirements(effective: &EffectiveAgentEnv) -> Vec<Requirement> {
         Some("databricks") | Some("databricks_v2") | Some("databricks-v2") => {
             Some("DATABRICKS_MODEL")
         }
-        Some("anthropic") => Some("ANTHROPIC_MODEL"),
+        Some("anthropic") => Some(super::ANTHROPIC_MODEL_ENV_KEY),
         Some("openai") | Some("openai-compat") => Some("OPENAI_COMPAT_MODEL"),
         _ => None,
     };
diff --git a/desktop/src-tauri/src/managed_agents/runtime/tests.rs b/desktop/src-tauri/src/managed_agents/runtime/tests.rs
index d86b69938f..979ef9f345 100644
--- a/desktop/src-tauri/src/managed_agents/runtime/tests.rs
+++ b/desktop/src-tauri/src/managed_agents/runtime/tests.rs
@@ -517,13 +517,21 @@ fn runtime_metadata_env_vars_injects_model_and_provider() {
 
 #[test]
 fn runtime_metadata_env_vars_skips_provider_when_locked() {
+    // The claude runtime shape: model injectable via ANTHROPIC_MODEL (#2692),
+    // provider locked to Anthropic so no provider env is written.
     let vars = runtime_metadata_env_vars(
-        None, // claude has no model_env_var
+        Some("ANTHROPIC_MODEL"),
         None, // claude has no provider_env_var
         true, // provider_locked = true
-        Some("claude-opus-4-7"),
+        Some("claude-sonnet-5"),
         Some("anthropic"),
     );
+    assert_eq!(vars, vec![("ANTHROPIC_MODEL", "claude-sonnet-5")]);
+}
+
+#[test]
+fn runtime_metadata_env_vars_empty_without_env_channels() {
+    let vars = runtime_metadata_env_vars(None, None, true, Some("some-model"), Some("anthropic"));
     assert!(vars.is_empty());
 }
 
